Index: head/share/man/man4/netgraph.4 =================================================================== --- head/share/man/man4/netgraph.4 (revision 69921) +++ head/share/man/man4/netgraph.4 (revision 69922) @@ -1,1115 +1,1237 @@ .\" 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. .\" .\" Authors: Julian Elischer .\" Archie Cobbs .\" .\" $FreeBSD$ .\" $Whistle: netgraph.4,v 1.7 1999/01/28 23:54:52 julian Exp $ .\" .Dd January 19, 1999 .Dt NETGRAPH 4 .Os FreeBSD .Sh NAME .Nm netgraph .Nd graph based kernel networking subsystem .Sh DESCRIPTION The .Nm system provides a uniform and modular system for the implementation of kernel objects which perform various networking functions. The objects, known as .Em nodes , can be arranged into arbitrarily complicated graphs. Nodes have .Em hooks which are used to connect two nodes together, forming the edges in the graph. Nodes communicate along the edges to process data, implement protocols, etc. .Pp The aim of .Nm is to supplement rather than replace the existing kernel networking infrastructure. It provides: .Pp .Bl -bullet -compact -offset 2n .It A flexible way of combining protocol and link level drivers .It A modular way to implement new protocols .It A common framework for kernel entities to inter-communicate .It A reasonably fast, kernel-based implementation .El .Sh Nodes and Types The most fundamental concept in .Nm is that of a .Em node . All nodes implement a number of predefined methods which allow them to interact with other nodes in a well defined manner. .Pp Each node has a .Em type , which is a static property of the node determined at node creation time. A node's type is described by a unique .Tn ASCII type name. The type implies what the node does and how it may be connected to other nodes. .Pp In object-oriented language, types are classes and nodes are instances of their respective class. All node types are subclasses of the generic node type, and hence inherit certain common functionality and capabilities (e.g., the ability to have an .Tn ASCII name). .Pp Nodes may be assigned a globally unique .Tn ASCII name which can be used to refer to the node. The name must not contain the characters .Dq \&. or .Dq \&: and is limited to .Dv "NG_NODELEN + 1" characters (including NUL byte). .Pp Each node instance has a unique .Em ID number which is expressed as a 32-bit hex value. This value may be used to refer to a node when there is no .Tn ASCII name assigned to it. .Sh Hooks Nodes are connected to other nodes by connecting a pair of .Em hooks , one from each node. Data flows bidirectionally between nodes along connected pairs of hooks. A node may have as many hooks as it needs, and may assign whatever meaning it wants to a hook. .Pp Hooks have these properties: .Pp .Bl -bullet -compact -offset 2n .It A hook has an .Tn ASCII name which is unique among all hooks on that node (other hooks on other nodes may have the same name). The name must not contain a .Dq \&. or a .Dq \&: and is limited to .Dv "NG_HOOKLEN + 1" characters (including NUL byte). .It A hook is always connected to another hook. That is, hooks are created at the time they are connected, and breaking an edge by removing either hook destroys both hooks. +.It +A hook can be set into a state where incoming packets are always queued +by the input queuing system, rather than being delivered directly. This +is used when the two joined nodes need to be decoupled, e.g. if they are +running at different processor priority levels. (spl) .El .Pp A node may decide to assign special meaning to some hooks. For example, connecting to the hook named .Dq debug might trigger the node to start sending debugging information to that hook. .Sh Data Flow Two types of information flow between nodes: data messages and control messages. Data messages are passed in mbuf chains along the edges in the graph, one edge at a time. The first mbuf in a chain must have the .Dv M_PKTHDR flag set. Each node decides how to handle data coming in on its hooks. .Pp Control messages are type-specific C structures sent from one node directly to some arbitrary other node. Control messages have a common header format, followed by type-specific data, and are binary structures for efficiency. However, node types also may support conversion of the type specific data between binary and .Tn ASCII for debugging and human interface purposes (see the .Dv NGM_ASCII2BINARY and .Dv NGM_BINARY2ASCII generic control messages below). Nodes are not required to support these conversions. .Pp -There are two ways to address a control message. If +There are three ways to address a control message. If there is a sequence of edges connecting the two nodes, the message may be .Dq source routed by specifying the corresponding sequence -of hooks as the destination address for the message (relative -addressing). Otherwise, the recipient node global +of .Tn ASCII +hook names as the destination address for the message (relative +addressing). If the destination is adjacent to the source, then the source +node may simply specify (as a pointer in the code) the hook across which the +message should be sent. Otherwise, the recipient node global +.Tn ASCII name (or equivalent ID based name) is used as the destination address -for the message (absolute addressing). The two types of addressing +for the message (absolute addressing). The two types of +.Tn ASCII +addressing may be combined, by specifying an absolute start node and a sequence -of hooks. +of hooks. Only the +.Tn ASCII +addressing modes are available to control programs outside the kernel, +as use of direct pointers is limited of course to kernel modules. .Pp Messages often represent commands that are followed by a reply message in the reverse direction. To facilitate this, the recipient of a control message is supplied with a .Dq return address -that is suitable -for addressing a reply. +that is suitable for addressing a reply. +In addition, depending on the topology of +the graph and whether the source has requested it, a pointer to a +pointer that can be read by the source node may also be supplied. +This allows the destination node to directly respond in a +synchronous manner when control returns to the source node, by +simply pointing the supplied pointer to the response message. +Such synchronous message responses are more efficient but are not always possible. .Pp Each control message contains a 32 bit value called a .Em typecookie indicating the type of the message, i.e., how to interpret it. Typically each type defines a unique typecookie for the messages that it understands. However, a node may choose to recognize and implement more than one type of message. .Pp -If message is delivered to an address that implies that it arrived -at that node through a particular hook, that hook is identified to the +If a message is delivered to an address that implies that it arrived +at that node through a particular hook, (as opposed to having been directly +addressed using its ID or global name), then that hook is identified to the receiving node. This allows a message to be rerouted or passed on, should -a node decide that this is required. +a node decide that this is required, in much the same way that data packets +are passed around between nodes. A set of standard +messages for flow control and link management purposes are +defined by the base system that are usually +passed around in this manner. Flow control message would usually travel +in the opposite direction to the data to which they pertain. +.Pp +Since flow control packets can also result from data being sent, it is also +possible to return a synchronous message response to a data packet being +sent between nodes. (See later). .Sh Netgraph is Functional In order to minimize latency, most .Nm operations are functional. That is, data and control messages are delivered by making function calls rather than by using queues and mailboxes. For example, if node A wishes to send a data mbuf to neighboring node B, it calls the generic .Nm data delivery function. This function in turn locates node B and calls B's .Dq receive data -method. +method. There are exceptions to this. .Pp It is allowable for nodes to reject a data packet, or to pass it back to the caller in a modified or completely replaced form. The caller can notify the node being called that it does not wish to receive any such packets by using the .Fn NG_SEND_DATA -macro, in which case, the second node should just discard rejected packets. +and +.Fn NG_SEND_DATA_ONLY +macros, in which case, the second node should just discard rejected packets. If the sender knows how to handle returned packets, it must use the .Fn NG_SEND_DATA_RET macro, which will adjust the parameters to point to the returned data or NULL if no data was returned to the caller. No packet return is possible across a queuing link (though an explicitly sent return is of course possible, it doesn't mean quite the same thing). .Pp While this mode of operation results in good performance, it has a few implications for node developers: .Pp .Bl -bullet -compact -offset 2n .It Whenever a node delivers a data or control message, the node may need to allow for the possibility of receiving a returning message before the original delivery function call returns. .It Netgraph nodes and support routines generally run at .Fn splnet . However, some nodes may want to send data and control messages -from a different priority level. Netgraph supplies queueing routines which -utilize the NETISR system to move message delivery to +from a different priority level. Netgraph supplies a mechanism which +utilizes the NETISR system to move message and data delivery to .Fn splnet . Nodes that run at other priorities (e.g. interfaces) can be directly linked to other nodes so that the combination runs at the other priority, -however any interaction with nodes running at splnet MUST be achievd via the +however any interaction with nodes running at splnet MUST be achieved via the queueing functions, (which use the .Fn netisr feature of the kernel). Note that messages are always received at .Fn splnet . .It It's possible for an infinite loop to occur if the graph contains cycles. .El .Pp So far, these issues have not proven problematical in practice. .Sh Interaction With Other Parts of the Kernel A node may have a hidden interaction with other components of the kernel outside of the .Nm subsystem, such as device hardware, kernel protocol stacks, etc. In fact, one of the benefits of .Nm is the ability to join disparate kernel networking entities together in a consistent communication framework. .Pp An example is the node type .Em socket which is both a netgraph node and a .Xr socket 2 BSD socket in the protocol family .Dv PF_NETGRAPH . Socket nodes allow user processes to participate in .Nm . Other nodes communicate with socket nodes using the usual methods, and the node hides the fact that it is also passing information to and from a cooperating user process. .Pp Another example is a device driver that presents a node interface to the hardware. .Sh Node Methods Nodes are notified of the following actions via function calls to the following node methods (all at .Fn splnet ) and may accept or reject that action (by returning the appropriate error code): .Bl -tag -width xxx .It Creation of a new node The constructor for the type is called. If creation of a new node is allowed, the constructor must call the generic node creation function (in object-oriented terms, the superclass constructor) and then allocate any special resources it needs. For nodes that correspond to hardware, this is typically done during the device attach routine. Often a global .Tn ASCII name corresponding to the device name is assigned here as well. .It Creation of a new hook The hook is created and tentatively linked to the node, and the node is told about the name that will be used to describe this hook. The node sets up any special data structures it needs, or may reject the connection, based on the name of the hook. .It Successful connection of two hooks After both ends have accepted their hooks, and the links have been made, the nodes get a chance to find out who their peer is across the link and can then decide to reject -the connection. Tear-down is automatic. +the connection. Tear-down is automatic. This is also the time at which +a node may decide whether to set a particular hook (or its peer) into +.Em queuing +mode. .It Destruction of a hook The node is notified of a broken connection. The node may consider some hooks to be critical to operation and others to be expendable: the disconnection of one hook may be an acceptable event while for another it may effect a total shutdown for the node. .It Shutdown of a node This method allows a node to clean up and to ensure that any actions that need to be performed at this time are taken. The method must call the generic (i.e., superclass) node destructor to get rid of the generic components of the node. Some nodes (usually associated with a piece of hardware) may be .Em persistent in that a shutdown breaks all edges and resets the node, but doesn't remove it, in which case the generic destructor is not called. .El .Sh Sending and Receiving Data -Three other methods are also supported by all nodes: +Two other methods are also supported by all nodes: .Bl -tag -width xxx .It Receive data message An mbuf chain is passed to the node. The node is notified on which hook the data arrived, and can use this information in its processing decision. The receiving node must always .Fn m_freem the mbuf chain on completion or error, pass it back (reject it), or pass it on to another node (or kernel module) which will then be responsible for freeing it. If a node passes a packet back to the caller, it does not have to be the same mbuf, in which case the original must be freed. Passing a packet back allows a module to modify the original data (e.g. encrypt it), or in some other way filter it (e.g. packet filtering). .Pp In addition to the mbuf chain itself there is also a pointer to a structure describing meta-data about the message (e.g. priority information). This pointer may be .Dv NULL if there is no additional information. The format for this information is described in -.Pa netgraph.h . +.Pa sys/netgraph/netgraph.h . The memory for meta-data must allocated via .Fn malloc with type .Dv M_NETGRAPH . As with the data itself, it is the receiver's responsibility to .Fn free the meta-data. If the mbuf chain is freed the meta-data must be freed at the same time. If the meta-data is freed but the real data on is passed on, then a .Dv NULL pointer must be substituted. Meta-data may be passed back in the same way that mbuf data may be passed back. As with mbuf data, the rejected or returned meta-data pointer may point to the same or different meta-data as that passed in, and if it is different, the original must be freed. .Pp The receiving node may decide to defer the data by queueing it in the .Nm -NETISR system (see below). +NETISR system (see below). It achieves this by setting the +.Dv HK_QUEUE +flag in the flags word of the hook on which that data will arrive. +The infrastructure will respect that bit and queue the data for delivery at +a later time, rather than deliver it directly. A node may decide to set +the bit on the +.Em peer +node, so that it's own output packets are queued. This is used +by device drivers running at different processor priorities to transfer +packet delivery to the splnet() level at which the bulk of +.Nm +runs. .Pp -The structure and use of meta-data is still experimental, but is presently used in -frame-relay to indicate that management packets should be queued for transmission +The structure and use of meta-data is still experimental, but is +presently used in frame-relay to indicate that management packets +should be queued for transmission at a higher priority than data packets. This is required for conformance with Frame Relay standards. .Pp -.It Receive queued data message -Usually this will be the same function as -.Em Receive data message. -This is the entry point called when a data message is being handed to -the node after having been queued in the NETISR system. -This allows a node to decide in the -.Em Receive data message -method that a message should be deferred and queued, -and be sure that when it is processed from the queue, -it will not be queued again. +The node may also receive information allowing it to send a synchronous +message response to one of the originators of the data. it is envisionned +that such a message would contain error or flow-control information. +Standard messages for these purposes have been defined in +.Pa sys/netgraph/netgraph.h . .It Receive control message This method is called when a control message is addressed to the node. A return address is always supplied, giving the address of the node that originated the message so a reply message can be sent anytime later. .Pp It is possible for a synchronous reply to be made, and in fact this is more common in practice. This is done by setting a pointer (supplied as an extra function parameter) to point to the reply. Then when the control message delivery function returns, the caller can check if this pointer has been made non-NULL, and if so then it points to the reply message allocated via .Fn malloc and containing the synchronous response. In both directions, (request and response) it is up to the receiver of that message to .Fn free the control message buffer. All control messages and replies are allocated with .Fn malloc type .Dv M_NETGRAPH . .Pp If the message was delivered via a specific hook, that hook will also be made known, which allows the use of such things as flow-control messages, and status change messages, where the node may want to forward the message out another hook to that on which it arrived. .El .Pp Much use has been made of reference counts, so that nodes being free'd of all references are automatically freed, and this behaviour has been tested and debugged to present a consistent and trustworthy framework for the .Dq type module writer to use. .Sh Addressing The .Nm framework provides an unambiguous and simple to use method of specifically addressing any single node in the graph. The naming of a node is independent of its type, in that another node, or external component need not know anything about the node's type in order to address it so as to send it a generic message type. Node and hook names should be chosen so as to make addresses meaningful. .Pp Addresses are either absolute or relative. An absolute address begins with a node name, (or ID), followed by a colon, followed by a sequence of hook names separated by periods. This addresses the node reached by starting at the named node and following the specified sequence of hooks. A relative address includes only the sequence of hook names, implicitly starting hook traversal at the local node. .Pp There are a couple of special possibilities for the node name. The name .Dq \&. (referred to as .Dq \&.: ) always refers to the local node. Also, nodes that have no global name may be addressed by their ID numbers, by enclosing the hex representation of the ID number within square brackets. Here are some examples of valid netgraph addresses: .Bd -literal -offset 4n -compact .: foo: .:hook1 foo:hook1.hook2 - [f057cd80]:hook1 + [d80]:hook1 .Ed .Pp Consider the following set of nodes might be created for a site with a single physical frame relay line having two active logical DLCI channels, with RFC-1490 frames on DLCI 16 and PPP frames over DLCI 20: .Pp .Bd -literal [type SYNC ] [type FRAME] [type RFC1490] [ "Frame1" ](uplink)<-->(data)[](dlci16)<-->(mux)[ ] [ A ] [ B ](dlci20)<---+ [ C ] | | [ type PPP ] +>(mux)[] [ D ] .Ed .Pp One could always send a control message to node C from anywhere by using the name .Em "Frame1:uplink.dlci16" . In this case, node C would also be notified that the message reached it via its hook .Dq mux . Similarly, .Em "Frame1:uplink.dlci20" could reliably be used to reach node D, and node A could refer to node B as .Em ".:uplink" , or simply .Em "uplink" . Conversely, B can refer to A as .Em "data" . The address .Em "mux.data" could be used by both nodes C and D to address a message to node A. .Pp Note that this is only for .Em control messages . In each of these cases, where a relative addressing mode is used, the recipient is notified of the hook on which the message arrived, as well as the originating node. This allows the option of hop-by-hop distibution of messages and state information. Data messages are .Em only routed one hop at a time, by specifying the departing hook, with each node making the next routing decision. So when B receives a frame on hook .Dq data it decodes the frame relay header to determine the DLCI, and then forwards the unwrapped frame to either C or D. .Pp +In a similar way, flow control messages may be routed in the reverse +direction to outgoing data. For example a "buffer nearly full" message from +.Em "Frame1: +would be passed to node +.Em B +which might decide to send similar messages to both nodes +.Em C +and +.Em D . +The nodes would use +.Em "Direct hook pointer" +addressing to route the messages. The message may have travelled from +.Em "Frame1: +to +.Em B +as a synchronous reply, saving time and cycles. + + +.Pp A similar graph might be used to represent multi-link PPP running over an ISDN line: .Pp .Bd -literal [ type BRI ](B1)<--->(link1)[ type MPP ] [ "ISDN1" ](B2)<--->(link2)[ (no name) ] [ ](D) <-+ | +----------------+ | +->(switch)[ type Q.921 ](term1)<---->(datalink)[ type Q.931 ] [ (no name) ] [ (no name) ] .Ed .Sh Netgraph Structures -Interesting members of the node and hook structures are shown below: +Interesting members of the node and hook structures are shown below +however you should +check +.Pa sys/netgraph/netgraph.h +on your system for more up-to-date versions. + .Bd -literal struct ng_node { char *name; /* Optional globally unique name */ void *private; /* Node implementation private info */ struct ng_type *type; /* The type of this node */ int refs; /* Number of references to this struct */ int numhooks; /* Number of connected hooks */ hook_p hooks; /* Linked list of (connected) hooks */ }; typedef struct ng_node *node_p; struct ng_hook { char *name; /* This node's name for this hook */ void *private; /* Node implementation private info */ int refs; /* Number of references to this struct */ struct ng_node *node; /* The node this hook is attached to */ struct ng_hook *peer; /* The other hook in this connected pair */ struct ng_hook *next; /* Next in list of hooks for this node */ }; typedef struct ng_hook *hook_p; .Ed .Pp The maintenance of the name pointers, reference counts, and linked list of hooks for each node is handled automatically by the .Nm subsystem. Typically a node's private info contains a back-pointer to the node or hook structure, which counts as a new reference that must be registered by incrementing .Dv "node->refs" . .Pp From a hook you can obtain the corresponding node, and from a node the list of all active hooks. .Pp -Node types are described by these structures: +Node types are described by the structures below: .Bd -literal /** How to convert a control message from binary <-> ASCII */ struct ng_cmdlist { u_int32_t cookie; /* 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 */ }; struct ng_type { u_int32_t version; /* Must equal NG_VERSION */ const char *name; /* Unique type name */ /* Module event handler */ modeventhand_t mod_event; /* Handle load/unload (optional) */ /* Constructor */ int (*constructor)(node_p *node); /* Create a new node */ /** Methods using the node **/ int (*rcvmsg)(node_p node, /* Receive control message */ - struct ng_mesg *msg, /* The message */ - const char *retaddr, /* Return address */ - struct ng_mesg **resp /* Synchronous response */ - hook_p lasthook); /* last hook traversed */ + struct ng_mesg *msg, /* The message */ + const char *retaddr, /* Return address */ + struct ng_mesg **resp /* Synchronous response */ + hook_p lasthook); /* last hook traversed */ int (*shutdown)(node_p node); /* Shutdown this node */ int (*newhook)(node_p node, /* create a new hook */ - hook_p hook, /* Pre-allocated struct */ - const char *name); /* Name for new hook */ + hook_p hook, /* Pre-allocated struct */ + const char *name); /* Name for new hook */ /** Methods using the hook **/ int (*connect)(hook_p hook); /* Confirm new hook attachment */ int (*rcvdata)(hook_p hook, /* Receive data on a hook */ - struct mbuf *m, /* The data in an mbuf */ - meta_p meta, /* Meta-data, if any */ - struct mbuf **ret_m, /* return data here */ - meta_p *ret_meta); /* return Meta-data here */ + struct mbuf *m, /* The data in an mbuf */ + meta_p meta, /* Meta-data, if any */ + struct mbuf **ret_m, /* return data here */ + meta_p *ret_meta, /* return Meta-data here */ + struct ng_message **resp); /* Synchronous reply info */ int (*disconnect)(hook_p hook); /* Notify disconnection of hook */ /** How to convert control messages binary <-> ASCII */ const struct ng_cmdlist *cmdlist; /* Optional; may be NULL */ }; .Ed .Pp Control messages have the following structure: .Bd -literal #define NG_CMDSTRLEN 15 /* Max command string (16 with null) */ struct ng_mesg { struct ng_msghdr { u_char version; /* Must equal NG_VERSION */ u_char spare; /* Pad to 2 bytes */ u_short arglen; /* Length of cmd/resp data */ u_long flags; /* Message status flags */ u_long token; /* Reply should have the same token */ u_long typecookie; /* Node type understanding this message */ u_long cmd; /* Command identifier */ u_char cmdstr[NG_CMDSTRLEN+1]; /* Cmd string (for debug) */ } header; char data[0]; /* Start of cmd/resp data */ }; -#define NG_VERSION 1 /* Netgraph version */ +#define NG_VERSION 3 /* Netgraph version */ #define NGF_ORIG 0x0000 /* Command */ #define NGF_RESP 0x0001 /* Response */ .Ed .Pp Control messages have the fixed header shown above, followed by a variable length data section which depends on the type cookie and the command. Each field is explained below: .Bl -tag -width xxx .It Dv version Indicates the version of netgraph itself. The current version is .Dv NG_VERSION . .It Dv arglen This is the length of any extra arguments, which begin at .Dv data . .It Dv flags Indicates whether this is a command or a response control message. .It Dv token The .Dv token is a means by which a sender can match a reply message to the corresponding command message; the reply always has the same token. .Pp .It Dv typecookie The corresponding node type's unique 32-bit value. If a node doesn't recognize the type cookie it must reject the message by returning .Er EINVAL . .Pp Each type should have an include file that defines the commands, argument format, and cookie for its own messages. The typecookie insures that the same header file was included by both sender and receiver; when an incompatible change in the header file is made, the typecookie .Em must be changed. The de facto method for generating unique type cookies is to take the seconds from the epoch at the time the header file is written (i.e., the output of .Dv "date -u +'%s'" ) . .Pp There is a predefined typecookie .Dv NGM_GENERIC_COOKIE for the .Dq generic node type, and a corresponding set of generic messages which all nodes understand. The handling of these messages is automatic. .It Dv command The identifier for the message command. This is type specific, and is defined in the same header file as the typecookie. .It Dv cmdstr Room for a short human readable version of .Dq command (for debugging purposes only). .El .Pp Some modules may choose to implement messages from more than one of the header files and thus recognize more than one type cookie. .Sh Control Message ASCII Form Control messages are in binary format for efficiency. However, for debugging and human interface purposes, and if the node type supports it, control messages may be converted to and from an equivalent .Tn ASCII form. The .Tn ASCII form is similar to the binary form, with two exceptions: .Pp .Bl -tag -compact -width xxx .It o The .Dv cmdstr header field must contain the .Tn ASCII name of the command, corresponding to the .Dv cmd header field. .It o The .Dv args field contains a NUL-terminated .Tn ASCII string version of the message arguments. .El .Pp In general, the arguments field of a control messgage can be any arbitrary C data type. Netgraph includes parsing routines to support some pre-defined datatypes in .Tn ASCII with this simple syntax: .Pp .Bl -tag -compact -width xxx .It o Integer types are represented by base 8, 10, or 16 numbers. .It o Strings are enclosed in double quotes and respect the normal C language backslash escapes. .It o IP addresses have the obvious form. .It o Arrays are enclosed in square brackets, with the elements listed consecutively starting at index zero. An element may have an optional index and equals sign preceeding it. Whenever an element does not have an explicit index, the index is implicitly the previous element's index plus one. .It o Structures are enclosed in curly braces, and each field is specified in the form .Dq fieldname=value . .It o Any array element or structure field whose value is equal to its .Dq default value may be omitted. For integer types, the default value is usually zero; for string types, the empty string. .It o Array elements and structure fields may be specified in any order. .El .Pp Each node type may define its own arbitrary types by providing the necessary routines to parse and unparse. .Tn ASCII forms defined for a specific node type are documented in the documentation for that node type. .Sh Generic Control Messages There are a number of standard predefined messages that will work for any node, as they are supported directly by the framework itself. These are defined in .Pa ng_message.h along with the basic layout of messages and other similar information. .Bl -tag -width xxx .It Dv NGM_CONNECT Connect to another node, using the supplied hook names on either end. .It Dv NGM_MKPEER Construct a node of the given type and then connect to it using the supplied hook names. .It Dv NGM_SHUTDOWN The target node should disconnect from all its neighbours and shut down. Persistent nodes such as those representing physical hardware might not disappear from the node namespace, but only reset themselves. The node must disconnect all of its hooks. This may result in neighbors shutting themselves down, and possibly a cascading shutdown of the entire connected graph. .It Dv NGM_NAME Assign a name to a node. Nodes can exist without having a name, and this is the default for nodes created using the .Dv NGM_MKPEER method. Such nodes can only be addressed relatively or by their ID number. .It Dv NGM_RMHOOK Ask the node to break a hook connection to one of its neighbours. Both nodes will have their .Dq disconnect method invoked. Either node may elect to totally shut down as a result. .It Dv NGM_NODEINFO Asks the target node to describe itself. The four returned fields are the node name (if named), the node type, the node ID and the number of hooks attached. The ID is an internal number unique to that node. .It Dv NGM_LISTHOOKS This returns the information given by .Dv NGM_NODEINFO , but in addition includes an array of fields describing each link, and the description for the node at the far end of that link. .It Dv NGM_LISTNAMES This returns an array of node descriptions (as for .Dv NGM_NODEINFO ")" where each entry of the array describes a named node. All named nodes will be described. .It Dv NGM_LISTNODES This is the same as .Dv NGM_LISTNAMES except that all nodes are listed regardless of whether they have a name or not. .It Dv NGM_LISTTYPES This returns a list of all currently installed netgraph types. .It Dv NGM_TEXT_STATUS The node may return a text formatted status message. The status information is determined entirely by the node type. It is the only "generic" message that requires any support within the node itself and as such the node may elect to not support this message. The text response must be less than .Dv NG_TEXTRESPONSE bytes in length (presently 1024). This can be used to return general status information in human readable form. .It Dv NGM_BINARY2ASCII This message converts a binary control message to its .Tn ASCII form. The entire control message to be converted is contained within the arguments field of the .Dv Dv NGM_BINARY2ASCII message itself. If successful, the reply will contain the same control message in .Tn ASCII form. A node will typically only know how to translate messages that it itself understands, so the target node of the .Dv NGM_BINARY2ASCII is often the same node that would actually receive that message. .It Dv NGM_ASCII2BINARY The opposite of .Dv NGM_BINARY2ASCII . The entire control message to be converted, in .Tn ASCII form, is contained in the arguments section of the .Dv NGM_ASCII2BINARY and need only have the .Dv flags , .Dv cmdstr , and .Dv arglen header fields filled in, plus the NUL-terminated string version of the arguments in the arguments field. If successful, the reply contains the binary version of the control message. .El + +.Sh Flow Control Messages +In addition to the control messages that affect nodes with respect to the +graph, there are also a number of +.Em Flow-control +messages defined. At present these are +.Em NOT +handled automatically by the system, so +nodes need to handle them if they are going to be used in a graph utilising +flow control, and will be in the likely path of these messages. The +default action of a node that doesn't understand these messages should +be to pass them onto the next node. Hopefully some helper functions +will assist in this eventually. These messages are also defined in +.Pa sys/netgraph/ng_message.h +and have a separate cookie +.Em NG_FLOW_COOKIE +to help identify them. They will not be covered in depth here. .Sh Metadata Data moving through the .Nm system can be accompanied by meta-data that describes some aspect of that data. The form of the meta-data is a fixed header, which contains enough information for most uses, and can optionally be supplemented by trailing .Em option structures, which contain a .Em cookie (see the section on control messages), an identifier, a length and optional data. If a node does not recognize the cookie associated with an option, it should ignore that option. .Pp Meta data might include such things as priority, discard eligibility, or special processing requirements. It might also mark a packet for debug status, etc. The use of meta-data is still experimental. .Sh INITIALIZATION The base .Nm code may either be statically compiled into the kernel or else loaded dynamically as a KLD via .Xr kldload 8 . In the former case, include .Bd -literal -offset 4n -compact options NETGRAPH .Ed in your kernel configuration file. You may also include selected node types in the kernel compilation, for example: .Bd -literal -offset 4n -compact options NETGRAPH options NETGRAPH_SOCKET options NETGRAPH_ECHO .Ed .Pp Once the .Nm subsystem is loaded, individual node types may be loaded at any time as KLD modules via .Xr kldload 8 . Moreover, .Nm knows how to automatically do this; when a request to create a new node of unknown type .Em type is made, .Nm will attempt to load the KLD module .Pa ng_type.ko . .Pp Types can also be installed at boot time, as certain device drivers may want to export each instance of the device as a netgraph node. .Pp In general, new types can be installed at any time from within the kernel by calling .Fn ng_newtype , supplying a pointer to the type's .Dv struct ng_type structure. .Pp The .Fn NETGRAPH_INIT macro automates this process by using a linker set. .Sh EXISTING NODE TYPES Several node types currently exist. Each is fully documented in its own man page: .Bl -tag -width xxx .It SOCKET The socket type implements two new sockets in the new protocol domain .Dv PF_NETGRAPH . The new sockets protocols are .Dv NG_DATA and .Dv NG_CONTROL , both of type .Dv SOCK_DGRAM . Typically one of each is associated with a socket node. When both sockets have closed, the node will shut down. The .Dv NG_DATA socket is used for sending and receiving data, while the .Dv NG_CONTROL socket is used for sending and receiving control messages. Data and control messages are passed using the .Xr sendto 2 and .Xr recvfrom 2 calls, using a .Dv struct sockaddr_ng socket address. .Pp .It HOLE Responds only to generic messages and is a .Dq black hole for data, Useful for testing. Always accepts new hooks. .Pp .It ECHO Responds only to generic messages and always echoes data back through the hook from which it arrived. Returns any non generic messages as their own response. Useful for testing. Always accepts new hooks. .Pp .It TEE This node is useful for .Dq snooping . It has 4 hooks: .Dv left , .Dv right , .Dv left2right , and .Dv right2left . Data entering from the right is passed to the left and duplicated on .Dv right2left, and data entering from the left is passed to the right and duplicated on .Dv left2right . Data entering from .Dv left2right is sent to the right and data from .Dv right2left to left. .Pp .It RFC1490 MUX Encapsulates/de-encapsulates frames encoded according to RFC 1490. Has a hook for the encapsulated packets .Pq Dq downstream and one hook for each protocol (i.e., IP, PPP, etc.). .Pp .It FRAME RELAY MUX Encapsulates/de-encapsulates Frame Relay frames. Has a hook for the encapsulated packets .Pq Dq downstream and one hook for each DLCI. .Pp .It FRAME RELAY LMI Automatically handles frame relay .Dq LMI (link management interface) operations and packets. Automatically probes and detects which of several LMI standards is in use at the exchange. .Pp .It TTY This node is also a line discipline. It simply converts between mbuf frames and sequential serial data, allowing a tty to appear as a netgraph node. It has a programmable .Dq hotkey character. .Pp .It ASYNC This node encapsulates and de-encapsulates asynchronous frames according to RFC 1662. This is used in conjunction with the TTY node type for supporting PPP links over asynchronous serial lines. .Pp .It INTERFACE This node is also a system networking interface. It has hooks representing each protocol family (IP, AppleTalk, IPX, etc.) and appears in the output of .Xr ifconfig 8 . The interfaces are named .Em ng0 , .Em ng1 , etc. +.It ONE2MANY +This node implements a simple round-robin multiplexer. It can be used +for example to make several LAN ports act together to get a higher speed +link between two machines. +.It Various PPP related nodes. +There is a full multilink PPP implementation that runs in Netgraph. +The +.Em Mpd +port can use these modules to make a very low latency high +capacity ppp system. It also supports +.Em PPTP +vpns using the +.Em PPTP +node. +.It PPPOE +A server and client side implememtation of PPPoE. Used in conjunction with +either +.Xr ppp 8 +or the +.Em mpd port. +.It BRIDGE +This node, togther with the ethernet nodes allows a very flexible +bridging system to be implemented. +.It KSOCKET +This intriguing node looks like a socket to the system but diverts +all data to and from the netgraph system for further processing. This allows +such things as UDP tunnels to be almost trivially implemented from the +command line. + .El +.Pp +Refer to the section at the end of this man page for more nodes types. .Sh NOTES Whether a named node exists can be checked by trying to send a control message to it (e.g., .Dv NGM_NODEINFO ). If it does not exist, .Er ENOENT will be returned. .Pp All data messages are mbuf chains with the M_PKTHDR flag set. .Pp Nodes are responsible for freeing what they allocate. There are three exceptions: .Bl -tag -width xxxx .It 1 Mbufs sent across a data link are never to be freed by the sender, unless it is returned from the recipient. .It 2 Any meta-data information traveling with the data has the same restriction. It might be freed by any node the data passes through, and a .Dv NULL passed onwards, but the caller will never free it. Two macros .Fn NG_FREE_META "meta" and .Fn NG_FREE_DATA "m" "meta" should be used if possible to free data and meta data (see .Pa netgraph.h ) . .It 3 Messages sent using .Fn ng_send_message are freed by the recipient. As in the case above, the addresses associated with the message are freed by whatever allocated them so the recipient should copy them if it wants to keep that information. .El .Sh FILES .Bl -tag -width xxxxx -compact .It Pa /sys/netgraph/netgraph.h Definitions for use solely within the kernel by .Nm nodes. .It Pa /sys/netgraph/ng_message.h Definitions needed by any file that needs to deal with .Nm messages. .It Pa /sys/netgraph/ng_socket.h Definitions needed to use .Nm socket type nodes. .It Pa /sys/netgraph/ng_{type}.h Definitions needed to use .Nm {type} nodes, including the type cookie definition. .It Pa /modules/netgraph.ko Netgraph subsystem loadable KLD module. .It Pa /modules/ng_{type}.ko Loadable KLD module for node type {type}. .El .Sh USER MODE SUPPORT There is a library for supporting user-mode programs that wish to interact with the netgraph system. See .Xr netgraph 3 for details. .Pp Two user-mode support programs, .Xr ngctl 8 and .Xr nghook 8 , are available to assist manual configuration and debugging. .Pp There are a few useful techniques for debugging new node types. First, implementing new node types in user-mode first makes debugging easier. The .Em tee node type is also useful for debugging, especially in conjunction with .Xr ngctl 8 and .Xr nghook 8 . +.Pp +Also look in /usr/share/examples/netgraph for solutions to several +common networking problems, solved using +.Nm . .Sh SEE ALSO .Xr socket 2 , .Xr netgraph 3 , .Xr ng_async 4 , +.Xr ng_bridge 4 , .Xr ng_bpf 4 , .Xr ng_cisco 4 , .Xr ng_ether 4 , .Xr ng_echo 4 , +.Xr ng_ether 4 , .Xr ng_frame_relay 4 , .Xr ng_hole 4 , .Xr ng_iface 4 , .Xr ng_ksocket 4 , .Xr ng_lmi 4 , .Xr ng_mppc 4 , .Xr ng_ppp 4 , .Xr ng_pppoe 4 , +.Xr ng_pptpgre 4 , .Xr ng_rfc1490 4 , .Xr ng_socket 4 , .Xr ng_tee 4 , .Xr ng_tty 4 , .Xr ng_UI 4 , .Xr ng_vjc 4 , .Xr ng_{type} 4 , .Xr ngctl 8 , .Xr nghook 8 .Sh HISTORY The .Nm system was designed and first implemented at Whistle Communications, Inc. in a version of .Fx 2.2 customized for the Whistle InterJet. It first made its debut in the main tree in .Fx 3.4 . .Sh AUTHORS .An -nosplit .An Julian Elischer Aq julian@FreeBSD.org , with contributions by .An Archie Cobbs Aq archie@FreeBSD.org . Index: head/sys/dev/ar/if_ar.c =================================================================== --- head/sys/dev/ar/if_ar.c (revision 69921) +++ head/sys/dev/ar/if_ar.c (revision 69922) @@ -1,2403 +1,2407 @@ /* * Copyright (c) 1995, 1999 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. * * $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 "ar.h" #include #include #include #include #include #include #include #include #include #ifdef NETGRAPH #include #include #include #include #else /* NETGRAPH */ #include #include #endif /* NETGRAPH */ #include #include #include #include #ifndef COMPAT_OLDISA #error "The ar device requires the old isa compatibility shims" #endif #ifndef NETGRAPH #include "sppp.h" #if NSPPP <= 0 #error device 'ar' require sppp. #endif /* NSPPP <= 0 */ #endif /* NETGRAPH */ #ifdef TRACE #define TRC(x) x #else #define TRC(x) #endif #define TRCL(x) x #define PPP_HEADER_LEN 4 #define ARC_GET_WIN(addr) ((addr >> ARC_WIN_SHFT) & AR_WIN_MSK) #define ARC_SET_MEM(iobase,win) outb(iobase+AR_MSCA_EN, AR_ENA_MEM | \ ARC_GET_WIN(win)) #define ARC_SET_SCA(iobase,ch) outb(iobase+AR_MSCA_EN, AR_ENA_MEM | \ AR_ENA_SCA | (ch ? AR_SEL_SCA_1:AR_SEL_SCA_0)) #define ARC_SET_OFF(iobase) outb(iobase+AR_MSCA_EN, 0) struct ar_hardc { int cunit; struct ar_softc *sc; u_short iobase; int isa_irq; int numports; caddr_t mem_start; caddr_t mem_end; u_char *orbase; u_int memsize; /* in bytes */ u_int winsize; /* in bytes */ u_int winmsk; u_char bustype; /* ISA, MCA, PCI.... */ u_char interface[NPORT];/* X21, V.35, EIA-530.... */ u_char revision; u_char handshake; /* handshake lines supported by card. */ u_char txc_dtr[NPORT/NCHAN]; /* the register is write only */ u_int txc_dtr_off[NPORT/NCHAN]; sca_regs *sca[NPORT/NCHAN]; }; static int next_ar_unit = 0; static struct ar_hardc ar_hardc[NAR]; 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_NODELEN + 1]; /* 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 */ static int arprobe(struct isa_device *id); static int arattach_isa(struct isa_device *id); /* * 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 */ }; struct isa_driver ardriver = { INTR_TYPE_NET, arprobe, arattach_isa, "ar" }; COMPAT_ISA_DRIVER(ar, ardriver); struct ar_hardc *arattach_pci(int unit, vm_offset_t mem_addr); void arintr_hc(struct ar_hardc *hc); static ointhand2_t arintr; static int arattach(struct ar_hardc *hc); 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_rmnode; 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 = { NG_VERSION, NG_AR_NODE_TYPE, NULL, ngar_constructor, ngar_rcvmsg, ngar_rmnode, ngar_newhook, NULL, ngar_connect, ngar_rcvdata, - ngar_rcvdata, ngar_disconnect, NULL }; static int ngar_done_init = 0; #endif /* NETGRAPH */ /* * Register the Adapter. * Probe to see if it is there. * Get its information and fill it in. */ static int arprobe(struct isa_device *id) { struct ar_hardc *hc = &ar_hardc[id->id_unit]; u_int tmp; u_short port; /* * Register the card. */ /* * Now see if the card is realy there. * * XXX For now I just check the undocumented ports * for "570". We will probably have to do more checking. */ port = id->id_iobase; if((inb(port+AR_ID_5) != '5') || (inb(port+AR_ID_7) != '7') || (inb(port+AR_ID_0) != '0')) return 0; /* * We have a card here, fill in what we can. */ tmp = inb(port + AR_BMI); hc->bustype = tmp & AR_BUS_MSK; hc->memsize = (tmp & AR_MEM_MSK) >> AR_MEM_SHFT; hc->memsize = 1 << hc->memsize; hc->memsize <<= 16; hc->interface[0] = (tmp & AR_IFACE_MSK); hc->interface[1] = hc->interface[0]; hc->interface[2] = hc->interface[0]; hc->interface[3] = hc->interface[0]; tmp = inb(port + AR_REV); hc->revision = tmp & AR_REV_MSK; hc->winsize = 1 << ((tmp & AR_WSIZ_MSK) >> AR_WSIZ_SHFT); hc->winsize *= ARC_WIN_SIZ; hc->winmsk = hc->winsize - 1; hc->numports = inb(port + AR_PNUM); hc->handshake = inb(port + AR_HNDSH); id->id_msize = hc->winsize; hc->iobase = id->id_iobase; hc->mem_start = id->id_maddr; hc->mem_end = id->id_maddr + id->id_msize; hc->cunit = id->id_unit; hc->isa_irq = id->id_irq; switch(hc->interface[0]) { case AR_IFACE_EIA_232: printf("ar%d: The EIA 232 interface is not supported.\n", id->id_unit); return 0; case AR_IFACE_V_35: break; case AR_IFACE_EIA_530: printf("ar%d: WARNING: The EIA 530 interface is untested.\n", id->id_unit); break; case AR_IFACE_X_21: break; case AR_IFACE_COMBO: printf("ar%d: WARNING: The COMBO interface is untested.\n", id->id_unit); break; } /* * Do a little sanity check. */ if((hc->numports > NPORT) || (hc->memsize > (512*1024))) return 0; return ARC_IO_SIZ; /* return the amount of IO addresses used. */ } /* * Malloc memory for the softc structures. * Reset the card to put it in a known state. * Register the ports on the adapter. * Fill in the info for each port. * Attach each port to sppp and bpf. */ static int arattach_isa(struct isa_device *id) { struct ar_hardc *hc = &ar_hardc[id->id_unit]; id->id_ointr = arintr; return arattach(hc); } struct ar_hardc * arattach_pci(int unit, vm_offset_t mem_addr) { struct ar_hardc *hc; u_int i, tmp; hc = malloc(sizeof(struct ar_hardc), M_DEVBUF, M_WAITOK | M_ZERO); hc->cunit = unit; hc->mem_start = (caddr_t)mem_addr; hc->sca[0] = (sca_regs *)(mem_addr + AR_PCI_SCA_1_OFFSET); hc->sca[1] = (sca_regs *)(mem_addr + AR_PCI_SCA_2_OFFSET); hc->iobase = 0; hc->orbase = (u_char *)(mem_addr + AR_PCI_ORBASE_OFFSET); tmp = hc->orbase[AR_BMI * 4]; hc->bustype = tmp & AR_BUS_MSK; hc->memsize = (tmp & AR_MEM_MSK) >> AR_MEM_SHFT; hc->memsize = 1 << hc->memsize; hc->memsize <<= 16; hc->interface[0] = (tmp & AR_IFACE_MSK); tmp = hc->orbase[AR_REV * 4]; hc->revision = tmp & AR_REV_MSK; hc->winsize = (1 << ((tmp & AR_WSIZ_MSK) >> AR_WSIZ_SHFT)) * 16 * 1024; hc->mem_end = (caddr_t)(mem_addr + hc->winsize); hc->winmsk = hc->winsize - 1; hc->numports = hc->orbase[AR_PNUM * 4]; hc->handshake = hc->orbase[AR_HNDSH * 4]; for(i = 1; i < hc->numports; i++) hc->interface[i] = hc->interface[0]; TRC(printf("arp%d: bus %x, rev %d, memstart %p, winsize %d, " "winmsk %x, interface %x\n", unit, hc->bustype, hc->revision, hc->mem_start, hc->winsize, hc->winmsk, hc->interface[0])); arattach(hc); return hc; } static int arattach(struct ar_hardc *hc) { struct ar_softc *sc; #ifndef NETGRAPH struct ifnet *ifp; char *iface; #endif /* NETGRAPH */ int unit; printf("arc%d: %uK RAM, %u ports, rev %u.\n", hc->cunit, hc->memsize/1024, hc->numports, hc->revision); arc_init(hc); 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; ifp->if_unit = sc->unit; ifp->if_name = "ar"; 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 (0); sc->node->private = 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", MTX_DEF); mtx_init(&sc->xmitq_hipri.ifq_mtx, "ar_xmitq_hipri", MTX_DEF); sprintf(sc->nodename, "%s%d", NG_AR_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { ng_rmnode(sc->node); ng_unref(sc->node); return (0); } sc->running = 0; #endif /* NETGRAPH */ } if(hc->bustype == AR_BUS_ISA) ARC_SET_OFF(hc->iobase); return 1; } /* * 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(int unit) { struct ar_hardc *hc; hc = &ar_hardc[unit]; arintr_hc(hc); return; } void arintr_hc(struct ar_hardc *hc) { 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 = inb(hc->iobase + 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->iobase, 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 = inb(hc->iobase + AR_ISTAT); } } if(hc->bustype == AR_BUS_ISA) ARC_SET_OFF(hc->iobase); } /* * 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->iobase, 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->iobase); } /* * 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->iobase, 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 if(ifp->if_bpf) 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->iobase); 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(printf("ar%d: arioctl.\n", ifp->if_unit);) was_up = ifp->if_flags & IFF_RUNNING; error = sppp_ioctl(ifp, cmd, data); TRC(printf("ar%d: ioctl: ifsppp.pp_flags = %x, if_flags %x.\n", ifp->if_unit, ((struct sppp *)ifp)->pp_flags, ifp->if_flags);) if(error) return error; if((cmd != SIOCSIFFLAGS) && cmd != (SIOCSIFADDR)) return 0; TRC(printf("ar%d: arioctl %s.\n", ifp->if_unit, (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->iobase, 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->iobase, 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 outb(sc->hc->iobase + 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) inb(sc->hc->iobase + 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->iobase); #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->iobase, sc->scano); msci->cmd = SCA_CMD_RXDISABLE; if(sc->hc->bustype == AR_BUS_ISA) inb(sc->hc->iobase + 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 outb(sc->hc->iobase + 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->iobase); } 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; 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 outb(hc->iobase + 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 outb(hc->iobase + AR_TXC_DTR0, hc->txc_dtr[0]); if(hc->bustype == AR_BUS_ISA) { /* * Configure the card. * Mem address, irq, */ mar = kvtop(hc->mem_start) >> 16; isr = irqtable[ffs(hc->isa_irq) - 1] << 1; if(isr == 0) printf("ar%d: Warning illegal interrupt %d\n", hc->cunit, ffs(hc->isa_irq) - 1); isr = isr | ((kvtop(hc->mem_start) & 0xc000) >> 10); hc->sca[0] = (sca_regs *)hc->mem_start; hc->sca[1] = (sca_regs *)hc->mem_start; outb(hc->iobase + AR_MEM_SEL, mar); outb(hc->iobase + 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 outb(hc->iobase + 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 outb(hc->iobase + 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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 if(sc->ifsppp.pp_if.if_bpf) bpf_mtap(&sc->ifsppp.pp_if, m); sppp_input(&sc->ifsppp.pp_if, m); sc->ifsppp.pp_if.if_ipackets++; #else /* NETGRAPH */ - ng_queue_data(sc->hook, m, NULL); + 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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); TRC( #ifndef NETGRAPH if(tt == sc->ifsppp.pp_if.if_ipackets) { #else /* NETGRAPH */ if(tt == sc->ipackets) { #endif /* NETGRAPH */ sca_descriptor *rxdesc; int i; if(hc->bustype == AR_BUS_ISA) ARC_SET_SCA(hc->iobase, 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->iobase, 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->iobase, 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->iobase, 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 *nodep) { 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 = node->private; /* * check if it's our friend the debug hook */ if (strcmp(name, NG_AR_HOOK_DEBUG) == 0) { hook->private = NULL; /* paranoid */ sc->debug_hook = hook; return (0); } /* * Check for raw mode hook. */ if (strcmp(name, NG_AR_HOOK_RAW) != 0) { return (EINVAL); } hook->private = 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, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook) { struct ar_softc * sc; int error = 0; sc = node->private; 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; MALLOC(*resp, struct ng_mesg *, resplen, M_NETGRAPH, M_NOWAIT | M_ZERO); if (*resp == NULL) { error = ENOMEM; break; } arg = (*resp)->data; /* * Put in the throughput information. */ 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.version = NG_VERSION; (*resp)->header.arglen = strlen(arg) + 1; (*resp)->header.token = msg->header.token; (*resp)->header.typecookie = NG_AR_COOKIE; (*resp)->header.cmd = msg->header.cmd; strncpy((*resp)->header.cmdstr, "status", NG_CMDSTRLEN); } break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } free(msg, M_NETGRAPH); return (error); } /* * get data from another node and transmit it to the correct channel */ static int ngar_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { int s; int error = 0; struct ar_softc * sc = hook->node->private; struct ifqueue *xmitq_p; /* * data doesn't come in from just anywhere (e.g control hook) */ if ( hook->private == NULL) { error = ENETDOWN; goto bad; } /* * Now queue the data for when it can be sent */ if (meta && meta->priority > 0) { xmitq_p = (&sc->xmitq_hipri); } 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_DATA(m, 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_rmnode(node_p node) { struct ar_softc * sc = node->private; ar_down(sc); ng_cutlinks(node); node->flags &= ~NG_INVALID; /* bounce back to life */ return (0); } /* already linked */ static int ngar_connect(hook_p hook) { + /* probably not at splnet, force outward queueing */ + hook->peer->flags |= HK_QUEUE; /* 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 = hook->node->private; int s; /* * If it's the data hook, then free resources etc. */ if (hook->private) { 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/ar/if_ar_isa.c =================================================================== --- head/sys/dev/ar/if_ar_isa.c (revision 69921) +++ head/sys/dev/ar/if_ar_isa.c (revision 69922) @@ -1,2403 +1,2407 @@ /* * Copyright (c) 1995, 1999 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. * * $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 "ar.h" #include #include #include #include #include #include #include #include #include #ifdef NETGRAPH #include #include #include #include #else /* NETGRAPH */ #include #include #endif /* NETGRAPH */ #include #include #include #include #ifndef COMPAT_OLDISA #error "The ar device requires the old isa compatibility shims" #endif #ifndef NETGRAPH #include "sppp.h" #if NSPPP <= 0 #error device 'ar' require sppp. #endif /* NSPPP <= 0 */ #endif /* NETGRAPH */ #ifdef TRACE #define TRC(x) x #else #define TRC(x) #endif #define TRCL(x) x #define PPP_HEADER_LEN 4 #define ARC_GET_WIN(addr) ((addr >> ARC_WIN_SHFT) & AR_WIN_MSK) #define ARC_SET_MEM(iobase,win) outb(iobase+AR_MSCA_EN, AR_ENA_MEM | \ ARC_GET_WIN(win)) #define ARC_SET_SCA(iobase,ch) outb(iobase+AR_MSCA_EN, AR_ENA_MEM | \ AR_ENA_SCA | (ch ? AR_SEL_SCA_1:AR_SEL_SCA_0)) #define ARC_SET_OFF(iobase) outb(iobase+AR_MSCA_EN, 0) struct ar_hardc { int cunit; struct ar_softc *sc; u_short iobase; int isa_irq; int numports; caddr_t mem_start; caddr_t mem_end; u_char *orbase; u_int memsize; /* in bytes */ u_int winsize; /* in bytes */ u_int winmsk; u_char bustype; /* ISA, MCA, PCI.... */ u_char interface[NPORT];/* X21, V.35, EIA-530.... */ u_char revision; u_char handshake; /* handshake lines supported by card. */ u_char txc_dtr[NPORT/NCHAN]; /* the register is write only */ u_int txc_dtr_off[NPORT/NCHAN]; sca_regs *sca[NPORT/NCHAN]; }; static int next_ar_unit = 0; static struct ar_hardc ar_hardc[NAR]; 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_NODELEN + 1]; /* 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 */ static int arprobe(struct isa_device *id); static int arattach_isa(struct isa_device *id); /* * 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 */ }; struct isa_driver ardriver = { INTR_TYPE_NET, arprobe, arattach_isa, "ar" }; COMPAT_ISA_DRIVER(ar, ardriver); struct ar_hardc *arattach_pci(int unit, vm_offset_t mem_addr); void arintr_hc(struct ar_hardc *hc); static ointhand2_t arintr; static int arattach(struct ar_hardc *hc); 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_rmnode; 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 = { NG_VERSION, NG_AR_NODE_TYPE, NULL, ngar_constructor, ngar_rcvmsg, ngar_rmnode, ngar_newhook, NULL, ngar_connect, ngar_rcvdata, - ngar_rcvdata, ngar_disconnect, NULL }; static int ngar_done_init = 0; #endif /* NETGRAPH */ /* * Register the Adapter. * Probe to see if it is there. * Get its information and fill it in. */ static int arprobe(struct isa_device *id) { struct ar_hardc *hc = &ar_hardc[id->id_unit]; u_int tmp; u_short port; /* * Register the card. */ /* * Now see if the card is realy there. * * XXX For now I just check the undocumented ports * for "570". We will probably have to do more checking. */ port = id->id_iobase; if((inb(port+AR_ID_5) != '5') || (inb(port+AR_ID_7) != '7') || (inb(port+AR_ID_0) != '0')) return 0; /* * We have a card here, fill in what we can. */ tmp = inb(port + AR_BMI); hc->bustype = tmp & AR_BUS_MSK; hc->memsize = (tmp & AR_MEM_MSK) >> AR_MEM_SHFT; hc->memsize = 1 << hc->memsize; hc->memsize <<= 16; hc->interface[0] = (tmp & AR_IFACE_MSK); hc->interface[1] = hc->interface[0]; hc->interface[2] = hc->interface[0]; hc->interface[3] = hc->interface[0]; tmp = inb(port + AR_REV); hc->revision = tmp & AR_REV_MSK; hc->winsize = 1 << ((tmp & AR_WSIZ_MSK) >> AR_WSIZ_SHFT); hc->winsize *= ARC_WIN_SIZ; hc->winmsk = hc->winsize - 1; hc->numports = inb(port + AR_PNUM); hc->handshake = inb(port + AR_HNDSH); id->id_msize = hc->winsize; hc->iobase = id->id_iobase; hc->mem_start = id->id_maddr; hc->mem_end = id->id_maddr + id->id_msize; hc->cunit = id->id_unit; hc->isa_irq = id->id_irq; switch(hc->interface[0]) { case AR_IFACE_EIA_232: printf("ar%d: The EIA 232 interface is not supported.\n", id->id_unit); return 0; case AR_IFACE_V_35: break; case AR_IFACE_EIA_530: printf("ar%d: WARNING: The EIA 530 interface is untested.\n", id->id_unit); break; case AR_IFACE_X_21: break; case AR_IFACE_COMBO: printf("ar%d: WARNING: The COMBO interface is untested.\n", id->id_unit); break; } /* * Do a little sanity check. */ if((hc->numports > NPORT) || (hc->memsize > (512*1024))) return 0; return ARC_IO_SIZ; /* return the amount of IO addresses used. */ } /* * Malloc memory for the softc structures. * Reset the card to put it in a known state. * Register the ports on the adapter. * Fill in the info for each port. * Attach each port to sppp and bpf. */ static int arattach_isa(struct isa_device *id) { struct ar_hardc *hc = &ar_hardc[id->id_unit]; id->id_ointr = arintr; return arattach(hc); } struct ar_hardc * arattach_pci(int unit, vm_offset_t mem_addr) { struct ar_hardc *hc; u_int i, tmp; hc = malloc(sizeof(struct ar_hardc), M_DEVBUF, M_WAITOK | M_ZERO); hc->cunit = unit; hc->mem_start = (caddr_t)mem_addr; hc->sca[0] = (sca_regs *)(mem_addr + AR_PCI_SCA_1_OFFSET); hc->sca[1] = (sca_regs *)(mem_addr + AR_PCI_SCA_2_OFFSET); hc->iobase = 0; hc->orbase = (u_char *)(mem_addr + AR_PCI_ORBASE_OFFSET); tmp = hc->orbase[AR_BMI * 4]; hc->bustype = tmp & AR_BUS_MSK; hc->memsize = (tmp & AR_MEM_MSK) >> AR_MEM_SHFT; hc->memsize = 1 << hc->memsize; hc->memsize <<= 16; hc->interface[0] = (tmp & AR_IFACE_MSK); tmp = hc->orbase[AR_REV * 4]; hc->revision = tmp & AR_REV_MSK; hc->winsize = (1 << ((tmp & AR_WSIZ_MSK) >> AR_WSIZ_SHFT)) * 16 * 1024; hc->mem_end = (caddr_t)(mem_addr + hc->winsize); hc->winmsk = hc->winsize - 1; hc->numports = hc->orbase[AR_PNUM * 4]; hc->handshake = hc->orbase[AR_HNDSH * 4]; for(i = 1; i < hc->numports; i++) hc->interface[i] = hc->interface[0]; TRC(printf("arp%d: bus %x, rev %d, memstart %p, winsize %d, " "winmsk %x, interface %x\n", unit, hc->bustype, hc->revision, hc->mem_start, hc->winsize, hc->winmsk, hc->interface[0])); arattach(hc); return hc; } static int arattach(struct ar_hardc *hc) { struct ar_softc *sc; #ifndef NETGRAPH struct ifnet *ifp; char *iface; #endif /* NETGRAPH */ int unit; printf("arc%d: %uK RAM, %u ports, rev %u.\n", hc->cunit, hc->memsize/1024, hc->numports, hc->revision); arc_init(hc); 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; ifp->if_unit = sc->unit; ifp->if_name = "ar"; 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 (0); sc->node->private = 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", MTX_DEF); mtx_init(&sc->xmitq_hipri.ifq_mtx, "ar_xmitq_hipri", MTX_DEF); sprintf(sc->nodename, "%s%d", NG_AR_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { ng_rmnode(sc->node); ng_unref(sc->node); return (0); } sc->running = 0; #endif /* NETGRAPH */ } if(hc->bustype == AR_BUS_ISA) ARC_SET_OFF(hc->iobase); return 1; } /* * 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(int unit) { struct ar_hardc *hc; hc = &ar_hardc[unit]; arintr_hc(hc); return; } void arintr_hc(struct ar_hardc *hc) { 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 = inb(hc->iobase + 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->iobase, 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 = inb(hc->iobase + AR_ISTAT); } } if(hc->bustype == AR_BUS_ISA) ARC_SET_OFF(hc->iobase); } /* * 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->iobase, 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->iobase); } /* * 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->iobase, 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 if(ifp->if_bpf) 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->iobase); 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(printf("ar%d: arioctl.\n", ifp->if_unit);) was_up = ifp->if_flags & IFF_RUNNING; error = sppp_ioctl(ifp, cmd, data); TRC(printf("ar%d: ioctl: ifsppp.pp_flags = %x, if_flags %x.\n", ifp->if_unit, ((struct sppp *)ifp)->pp_flags, ifp->if_flags);) if(error) return error; if((cmd != SIOCSIFFLAGS) && cmd != (SIOCSIFADDR)) return 0; TRC(printf("ar%d: arioctl %s.\n", ifp->if_unit, (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->iobase, 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->iobase, 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 outb(sc->hc->iobase + 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) inb(sc->hc->iobase + 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->iobase); #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->iobase, sc->scano); msci->cmd = SCA_CMD_RXDISABLE; if(sc->hc->bustype == AR_BUS_ISA) inb(sc->hc->iobase + 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 outb(sc->hc->iobase + 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->iobase); } 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; 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 outb(hc->iobase + 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 outb(hc->iobase + AR_TXC_DTR0, hc->txc_dtr[0]); if(hc->bustype == AR_BUS_ISA) { /* * Configure the card. * Mem address, irq, */ mar = kvtop(hc->mem_start) >> 16; isr = irqtable[ffs(hc->isa_irq) - 1] << 1; if(isr == 0) printf("ar%d: Warning illegal interrupt %d\n", hc->cunit, ffs(hc->isa_irq) - 1); isr = isr | ((kvtop(hc->mem_start) & 0xc000) >> 10); hc->sca[0] = (sca_regs *)hc->mem_start; hc->sca[1] = (sca_regs *)hc->mem_start; outb(hc->iobase + AR_MEM_SEL, mar); outb(hc->iobase + 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 outb(hc->iobase + 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 outb(hc->iobase + 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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 if(sc->ifsppp.pp_if.if_bpf) bpf_mtap(&sc->ifsppp.pp_if, m); sppp_input(&sc->ifsppp.pp_if, m); sc->ifsppp.pp_if.if_ipackets++; #else /* NETGRAPH */ - ng_queue_data(sc->hook, m, NULL); + 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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); TRC( #ifndef NETGRAPH if(tt == sc->ifsppp.pp_if.if_ipackets) { #else /* NETGRAPH */ if(tt == sc->ipackets) { #endif /* NETGRAPH */ sca_descriptor *rxdesc; int i; if(hc->bustype == AR_BUS_ISA) ARC_SET_SCA(hc->iobase, 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->iobase, 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->iobase, 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->iobase, 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 *nodep) { 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 = node->private; /* * check if it's our friend the debug hook */ if (strcmp(name, NG_AR_HOOK_DEBUG) == 0) { hook->private = NULL; /* paranoid */ sc->debug_hook = hook; return (0); } /* * Check for raw mode hook. */ if (strcmp(name, NG_AR_HOOK_RAW) != 0) { return (EINVAL); } hook->private = 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, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook) { struct ar_softc * sc; int error = 0; sc = node->private; 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; MALLOC(*resp, struct ng_mesg *, resplen, M_NETGRAPH, M_NOWAIT | M_ZERO); if (*resp == NULL) { error = ENOMEM; break; } arg = (*resp)->data; /* * Put in the throughput information. */ 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.version = NG_VERSION; (*resp)->header.arglen = strlen(arg) + 1; (*resp)->header.token = msg->header.token; (*resp)->header.typecookie = NG_AR_COOKIE; (*resp)->header.cmd = msg->header.cmd; strncpy((*resp)->header.cmdstr, "status", NG_CMDSTRLEN); } break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } free(msg, M_NETGRAPH); return (error); } /* * get data from another node and transmit it to the correct channel */ static int ngar_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { int s; int error = 0; struct ar_softc * sc = hook->node->private; struct ifqueue *xmitq_p; /* * data doesn't come in from just anywhere (e.g control hook) */ if ( hook->private == NULL) { error = ENETDOWN; goto bad; } /* * Now queue the data for when it can be sent */ if (meta && meta->priority > 0) { xmitq_p = (&sc->xmitq_hipri); } 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_DATA(m, 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_rmnode(node_p node) { struct ar_softc * sc = node->private; ar_down(sc); ng_cutlinks(node); node->flags &= ~NG_INVALID; /* bounce back to life */ return (0); } /* already linked */ static int ngar_connect(hook_p hook) { + /* probably not at splnet, force outward queueing */ + hook->peer->flags |= HK_QUEUE; /* 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 = hook->node->private; int s; /* * If it's the data hook, then free resources etc. */ if (hook->private) { 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/lmc/if_lmc.c =================================================================== --- head/sys/dev/lmc/if_lmc.c (revision 69921) +++ head/sys/dev/lmc/if_lmc.c (revision 69922) @@ -1,1526 +1,1529 @@ /*- * Copyright (c) 1994-1997 Matt Thomas (matt@3am-software.com) * Copyright (c) LAN Media Corporation 1998, 1999. * Copyright (c) 2000 Stephen Kiernan (sk-ports@vegamuse.org) * 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. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * From NetBSD: if_de.c,v 1.56.2.1 1997/10/27 02:13:25 thorpej Exp * $Id: if_lmc.c,v 1.9 1999/02/19 15:08:42 explorer Exp $ */ char lmc_version[] = "BSD 1.1"; #include "opt_netgraph.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INCLUDE_PATH_PREFIX "dev/lmc/" /* Intel CPUs should use I/O mapped access. */ #if defined(__i386__) #define LMC_IOMAPPED #endif /* * This turns on all sort of debugging stuff and make the * driver much larger. */ #ifdef LMC_DEBUG #define DP(x) printf x #else #define DP(x) #endif #define LMC_HZ 10 #ifndef TULIP_GP_PINSET #define TULIP_GP_PINSET 0x00000100L #endif #ifndef TULIP_BUSMODE_READMULTIPLE #define TULIP_BUSMODE_READMULTIPLE 0x00200000L #endif /* * C sucks */ typedef struct lmc___softc lmc_softc_t; typedef struct lmc___media lmc_media_t; typedef struct lmc___ctl lmc_ctl_t; #include "dev/lmc/if_lmcioctl.h" #include "dev/lmc/if_lmcvar.h" #include "dev/lmc/if_lmc_common.c" #include "dev/lmc/if_lmc_media.c" /* * This module supports * the DEC 21140A pass 2.2 PCI Fast Ethernet Controller. */ static lmc_intrfunc_t lmc_intr_normal(void *); static ifnet_ret_t lmc_ifstart(lmc_softc_t * const sc ); static ifnet_ret_t lmc_ifstart_one(lmc_softc_t * const sc); static struct mbuf *lmc_txput(lmc_softc_t * const sc, struct mbuf *m); static void lmc_rx_intr(lmc_softc_t * const sc); static void lmc_watchdog(lmc_softc_t * const sc); static void lmc_ifup(lmc_softc_t * const sc); static void lmc_ifdown(lmc_softc_t * const sc); #ifdef LMC_DEBUG static void ng_lmc_dump_packet(struct mbuf *m); #endif /* LMC_DEBUG */ static void ng_lmc_watchdog_frame(void *arg); static void ng_lmc_init(void *ignored); static ng_constructor_t ng_lmc_constructor; static ng_rcvmsg_t ng_lmc_rcvmsg; static ng_shutdown_t ng_lmc_rmnode; static ng_newhook_t ng_lmc_newhook; /*static ng_findhook_t ng_lmc_findhook; */ static ng_connect_t ng_lmc_connect; static ng_rcvdata_t ng_lmc_rcvdata; static ng_disconnect_t ng_lmc_disconnect; /* Parse type for struct lmc_ctl */ static const struct ng_parse_fixedarray_info ng_lmc_ctl_cardspec_info = { &ng_parse_int32_type, 7, NULL }; static const struct ng_parse_type ng_lmc_ctl_cardspec_type = { &ng_parse_fixedarray_type, &ng_lmc_ctl_cardspec_info }; static const struct ng_parse_struct_info ng_lmc_ctl_type_info = { { { "cardtype", &ng_parse_int32_type }, { "clock_source", &ng_parse_int32_type }, { "clock_rate", &ng_parse_int32_type }, { "crc_length", &ng_parse_int32_type }, { "cable_length", &ng_parse_int32_type }, { "scrambler_onoff", &ng_parse_int32_type }, { "cable_type", &ng_parse_int32_type }, { "keepalive_onoff", &ng_parse_int32_type }, { "ticks", &ng_parse_int32_type }, { "cardspec", &ng_lmc_ctl_cardspec_type }, { "circuit_type", &ng_parse_int32_type }, { NULL }, } }; static const struct ng_parse_type ng_lmc_ctl_type = { &ng_parse_struct_type, &ng_lmc_ctl_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_lmc_cmdlist[] = { { NG_LMC_COOKIE, NGM_LMC_GET_CTL, "getctl", NULL, &ng_lmc_ctl_type, }, { NG_LMC_COOKIE, NGM_LMC_SET_CTL, "setctl", &ng_lmc_ctl_type, NULL }, { 0 } }; static struct ng_type typestruct = { NG_VERSION, NG_LMC_NODE_TYPE, NULL, ng_lmc_constructor, ng_lmc_rcvmsg, ng_lmc_rmnode, ng_lmc_newhook, NULL, ng_lmc_connect, ng_lmc_rcvdata, - ng_lmc_rcvdata, ng_lmc_disconnect, ng_lmc_cmdlist }; static int ng_lmc_done_init = 0; /* * Code the read the SROM and MII bit streams (I2C) */ static void lmc_delay_300ns(lmc_softc_t * const sc) { int idx; for (idx = (300 / 33) + 1; idx > 0; idx--) (void)LMC_CSR_READ(sc, csr_busmode); } #define EMIT \ do { \ LMC_CSR_WRITE(sc, csr_srom_mii, csr); \ lmc_delay_300ns(sc); \ } while (0) static void lmc_srom_idle(lmc_softc_t * const sc) { unsigned bit, csr; csr = SROMSEL ; EMIT; csr = SROMSEL | SROMRD; EMIT; csr ^= SROMCS; EMIT; csr ^= SROMCLKON; EMIT; /* * Write 25 cycles of 0 which will force the SROM to be idle. */ for (bit = 3 + SROM_BITWIDTH + 16; bit > 0; bit--) { csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */ csr ^= SROMCLKON; EMIT; /* clock high; data valid */ } csr ^= SROMCLKOFF; EMIT; csr ^= SROMCS; EMIT; csr = 0; EMIT; } static void lmc_srom_read(lmc_softc_t * const sc) { unsigned idx; const unsigned bitwidth = SROM_BITWIDTH; const unsigned cmdmask = (SROMCMD_RD << bitwidth); const unsigned msb = 1 << (bitwidth + 3 - 1); unsigned lastidx = (1 << bitwidth) - 1; lmc_srom_idle(sc); for (idx = 0; idx <= lastidx; idx++) { unsigned lastbit, data, bits, bit, csr; csr = SROMSEL ; EMIT; csr = SROMSEL | SROMRD; EMIT; csr ^= SROMCSON; EMIT; csr ^= SROMCLKON; EMIT; lastbit = 0; for (bits = idx|cmdmask, bit = bitwidth + 3 ; bit > 0 ; bit--, bits <<= 1) { const unsigned thisbit = bits & msb; csr ^= SROMCLKOFF; EMIT; /* clock L data invalid */ if (thisbit != lastbit) { csr ^= SROMDOUT; EMIT;/* clock L invert data */ } else { EMIT; } csr ^= SROMCLKON; EMIT; /* clock H data valid */ lastbit = thisbit; } csr ^= SROMCLKOFF; EMIT; for (data = 0, bits = 0; bits < 16; bits++) { data <<= 1; csr ^= SROMCLKON; EMIT; /* clock H data valid */ data |= LMC_CSR_READ(sc, csr_srom_mii) & SROMDIN ? 1 : 0; csr ^= SROMCLKOFF; EMIT; /* clock L data invalid */ } sc->lmc_rombuf[idx*2] = data & 0xFF; sc->lmc_rombuf[idx*2+1] = data >> 8; csr = SROMSEL | SROMRD; EMIT; csr = 0; EMIT; } lmc_srom_idle(sc); } #define MII_EMIT do { LMC_CSR_WRITE(sc, csr_srom_mii, csr); lmc_delay_300ns(sc); } while (0) static void lmc_mii_writebits(lmc_softc_t * const sc, unsigned data, unsigned bits) { unsigned msb = 1 << (bits - 1); unsigned csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); unsigned lastbit = (csr & MII_DOUT) ? msb : 0; csr |= MII_WR; MII_EMIT; /* clock low; assert write */ for (; bits > 0; bits--, data <<= 1) { const unsigned thisbit = data & msb; if (thisbit != lastbit) { csr ^= MII_DOUT; MII_EMIT; /* clock low; invert data */ } csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ lastbit = thisbit; csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ } } static void lmc_mii_turnaround(lmc_softc_t * const sc, unsigned cmd) { unsigned csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); if (cmd == MII_WRCMD) { csr |= MII_DOUT; MII_EMIT; /* clock low; change data */ csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ csr ^= MII_DOUT; MII_EMIT; /* clock low; change data */ } else { csr |= MII_RD; MII_EMIT; /* clock low; switch to read */ } csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ } static unsigned lmc_mii_readbits(lmc_softc_t * const sc) { unsigned data; unsigned csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); int idx; for (idx = 0, data = 0; idx < 16; idx++) { data <<= 1; /* this is NOOP on the first pass through */ csr ^= MII_CLKON; MII_EMIT; /* clock high; data valid */ if (LMC_CSR_READ(sc, csr_srom_mii) & MII_DIN) data |= 1; csr ^= MII_CLKOFF; MII_EMIT; /* clock low; data not valid */ } csr ^= MII_RD; MII_EMIT; /* clock low; turn off read */ return data; } static unsigned lmc_mii_readreg(lmc_softc_t * const sc, unsigned devaddr, unsigned regno) { unsigned csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); unsigned data; csr &= ~(MII_RD|MII_CLK); MII_EMIT; lmc_mii_writebits(sc, MII_PREAMBLE, 32); lmc_mii_writebits(sc, MII_RDCMD, 8); lmc_mii_writebits(sc, devaddr, 5); lmc_mii_writebits(sc, regno, 5); lmc_mii_turnaround(sc, MII_RDCMD); data = lmc_mii_readbits(sc); return data; } static void lmc_mii_writereg(lmc_softc_t * const sc, unsigned devaddr, unsigned regno, unsigned data) { unsigned csr = LMC_CSR_READ(sc, csr_srom_mii) & (MII_RD|MII_DOUT|MII_CLK); csr &= ~(MII_RD|MII_CLK); MII_EMIT; lmc_mii_writebits(sc, MII_PREAMBLE, 32); lmc_mii_writebits(sc, MII_WRCMD, 8); lmc_mii_writebits(sc, devaddr, 5); lmc_mii_writebits(sc, regno, 5); lmc_mii_turnaround(sc, MII_WRCMD); lmc_mii_writebits(sc, data, 16); } static int lmc_read_macaddr(lmc_softc_t * const sc) { lmc_srom_read(sc); bcopy(sc->lmc_rombuf + 20, sc->lmc_enaddr, 6); return 0; } /* * Check to make certain there is a signal from the modem, and flicker * lights as needed. */ static void lmc_watchdog(lmc_softc_t * const sc) { int state; u_int32_t ostatus; u_int32_t link_status; u_int32_t ticks; state = 0; link_status = sc->lmc_media->get_link_status(sc); ostatus = ((sc->lmc_flags & LMC_MODEMOK) == LMC_MODEMOK); /* * hardware level link lost, but the interface is marked as up. * Mark it as down. */ if (link_status == 0 && ostatus) { printf(LMC_PRINTF_FMT ": physical link down\n", LMC_PRINTF_ARGS); sc->lmc_flags &= ~LMC_MODEMOK; lmc_led_off(sc, LMC_MII16_LED1); } /* * hardware link is up, but the interface is marked as down. * Bring it back up again. */ if (link_status != 0 && !ostatus) { printf(LMC_PRINTF_FMT ": physical link up\n", LMC_PRINTF_ARGS); if (sc->lmc_flags & LMC_IFUP) lmc_ifup(sc); sc->lmc_flags |= LMC_MODEMOK; lmc_led_on(sc, LMC_MII16_LED1); return; } /* * remember the timer value */ ticks = LMC_CSR_READ(sc, csr_gp_timer); LMC_CSR_WRITE(sc, csr_gp_timer, 0xffffffffUL); sc->ictl.ticks = 0x0000ffff - (ticks & 0x0000ffff); sc->lmc_out_dog = LMC_DOG_HOLDOFF; } /* * Mark the interface as "up" and enable TX/RX and TX/RX interrupts. * This also does a full software reset. */ static void lmc_ifup(lmc_softc_t * const sc) { untimeout(ng_lmc_watchdog_frame, sc, sc->lmc_handle); sc->lmc_running = 0; lmc_dec_reset(sc); lmc_reset(sc); sc->lmc_media->set_link_status(sc, 1); sc->lmc_media->set_status(sc, NULL); sc->lmc_flags |= LMC_IFUP; lmc_led_on(sc, LMC_MII16_LED1); /* * select what interrupts we want to get */ sc->lmc_intrmask |= (TULIP_STS_NORMALINTR | TULIP_STS_RXINTR | TULIP_STS_TXINTR | TULIP_STS_ABNRMLINTR | TULIP_STS_SYSERROR | TULIP_STS_TXSTOPPED | TULIP_STS_TXUNDERFLOW | TULIP_STS_RXSTOPPED ); LMC_CSR_WRITE(sc, csr_intr, sc->lmc_intrmask); sc->lmc_cmdmode |= TULIP_CMD_TXRUN; sc->lmc_cmdmode |= TULIP_CMD_RXRUN; LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode); untimeout(ng_lmc_watchdog_frame, sc, sc->lmc_handle); sc->lmc_handle = timeout(ng_lmc_watchdog_frame, sc, hz); sc->lmc_running = 1; } /* * Mark the interface as "down" and disable TX/RX and TX/RX interrupts. * This is done by performing a full reset on the interface. */ static void lmc_ifdown(lmc_softc_t * const sc) { untimeout(ng_lmc_watchdog_frame, sc, sc->lmc_handle); sc->lmc_running = 0; sc->lmc_flags &= ~LMC_IFUP; sc->lmc_media->set_link_status(sc, 0); lmc_led_off(sc, LMC_MII16_LED1); lmc_dec_reset(sc); lmc_reset(sc); sc->lmc_media->set_status(sc, NULL); } static void lmc_rx_intr(lmc_softc_t * const sc) { lmc_ringinfo_t * const ri = &sc->lmc_rxinfo; int fillok = 1; sc->lmc_rxtick++; for (;;) { tulip_desc_t *eop = ri->ri_nextin; int total_len = 0, last_offset = 0; struct mbuf *ms = NULL, *me = NULL; int accept = 0; if (fillok && sc->lmc_rxq.ifq_len < LMC_RXQ_TARGET) goto queue_mbuf; /* * If the TULIP has no descriptors, there can't be any receive * descriptors to process. */ if (eop == ri->ri_nextout) break; /* * 90% of the packets will fit in one descriptor. So we * optimize for that case. */ if ((((volatile tulip_desc_t *) eop)->d_status & (TULIP_DSTS_OWNER|TULIP_DSTS_RxFIRSTDESC|TULIP_DSTS_RxLASTDESC)) == (TULIP_DSTS_RxFIRSTDESC|TULIP_DSTS_RxLASTDESC)) { _IF_DEQUEUE(&sc->lmc_rxq, ms); me = ms; } else { /* * If still owned by the TULIP, don't touch it. */ if (((volatile tulip_desc_t *)eop)->d_status & TULIP_DSTS_OWNER) break; /* * It is possible (though improbable unless the * BIG_PACKET support is enabled or MCLBYTES < 1518) * for a received packet to cross more than one * receive descriptor. */ while ((((volatile tulip_desc_t *) eop)->d_status & TULIP_DSTS_RxLASTDESC) == 0) { if (++eop == ri->ri_last) eop = ri->ri_first; if (eop == ri->ri_nextout || ((((volatile tulip_desc_t *) eop)->d_status & TULIP_DSTS_OWNER))) { return; } total_len++; } /* * Dequeue the first buffer for the start of the * packet. Hopefully this will be the only one we * need to dequeue. However, if the packet consumed * multiple descriptors, then we need to dequeue * those buffers and chain to the starting mbuf. * All buffers but the last buffer have the same * length so we can set that now. (we add to * last_offset instead of multiplying since we * normally won't go into the loop and thereby * saving a ourselves from doing a multiplication * by 0 in the normal case). */ _IF_DEQUEUE(&sc->lmc_rxq, ms); for (me = ms; total_len > 0; total_len--) { me->m_len = LMC_RX_BUFLEN; last_offset += LMC_RX_BUFLEN; _IF_DEQUEUE(&sc->lmc_rxq, me->m_next); me = me->m_next; } } /* * Now get the size of received packet (minus the CRC). */ total_len = ((eop->d_status >> 16) & 0x7FFF); if (sc->ictl.crc_length == 16) total_len -= 2; else total_len -= 4; sc->lmc_inbytes += total_len; sc->lmc_inlast = 0; if ((sc->lmc_flags & LMC_RXIGNORE) == 0 && ((eop->d_status & LMC_DSTS_ERRSUM) == 0 )) { me->m_len = total_len - last_offset; sc->lmc_flags |= LMC_RXACT; accept = 1; } else { sc->lmc_ierrors++; if (eop->d_status & TULIP_DSTS_RxOVERFLOW) { sc->lmc_dot3stats.dot3StatsInternalMacReceiveErrors++; } } sc->lmc_ipackets++; if (++eop == ri->ri_last) eop = ri->ri_first; ri->ri_nextin = eop; queue_mbuf: /* * Either we are priming the TULIP with mbufs (m == NULL) * or we are about to accept an mbuf for the upper layers * so we need to allocate an mbuf to replace it. If we * can't replace it, send up it anyways. This may cause * us to drop packets in the future but that's better than * being caught in livelock. * * Note that if this packet crossed multiple descriptors * we don't even try to reallocate all the mbufs here. * Instead we rely on the test of the beginning of * the loop to refill for the extra consumed mbufs. */ if (accept || ms == NULL) { struct mbuf *m0; MGETHDR(m0, M_DONTWAIT, MT_DATA); if (m0 != NULL) { MCLGET(m0, M_DONTWAIT); if ((m0->m_flags & M_EXT) == 0) { m_freem(m0); m0 = NULL; } } if (accept) { + int error; + ms->m_pkthdr.len = total_len; ms->m_pkthdr.rcvif = NULL; - ng_queue_data(sc->lmc_hook, ms, NULL); + NG_SEND_DATA_ONLY(error, sc->lmc_hook, ms); } ms = m0; } if (ms == NULL) { /* * Couldn't allocate a new buffer. Don't bother * trying to replenish the receive queue. */ fillok = 0; sc->lmc_flags |= LMC_RXBUFSLOW; continue; } /* * Now give the buffer(s) to the TULIP and save in our * receive queue. */ do { ri->ri_nextout->d_length1 = LMC_RX_BUFLEN; ri->ri_nextout->d_addr1 = LMC_KVATOPHYS(sc, mtod(ms, caddr_t)); ri->ri_nextout->d_status = TULIP_DSTS_OWNER; if (++ri->ri_nextout == ri->ri_last) ri->ri_nextout = ri->ri_first; me = ms->m_next; ms->m_next = NULL; _IF_ENQUEUE(&sc->lmc_rxq, ms); } while ((ms = me) != NULL); if (sc->lmc_rxq.ifq_len >= LMC_RXQ_TARGET) sc->lmc_flags &= ~LMC_RXBUFSLOW; } } static int lmc_tx_intr(lmc_softc_t * const sc) { lmc_ringinfo_t * const ri = &sc->lmc_txinfo; struct mbuf *m; int xmits = 0; int descs = 0; sc->lmc_txtick++; while (ri->ri_free < ri->ri_max) { u_int32_t d_flag; if (((volatile tulip_desc_t *) ri->ri_nextin)->d_status & TULIP_DSTS_OWNER) break; d_flag = ri->ri_nextin->d_flag; if (d_flag & TULIP_DFLAG_TxLASTSEG) { const u_int32_t d_status = ri->ri_nextin->d_status; _IF_DEQUEUE(&sc->lmc_txq, m); if (m != NULL) { #if NBPFILTER > 0 if (sc->lmc_bpf != NULL) LMC_BPF_MTAP(sc, m); #endif m_freem(m); #if defined(LMC_DEBUG) } else { printf(LMC_PRINTF_FMT ": tx_intr: failed to dequeue mbuf?!?\n", LMC_PRINTF_ARGS); #endif } xmits++; if (d_status & LMC_DSTS_ERRSUM) { sc->lmc_oerrors++; if (d_status & TULIP_DSTS_TxUNDERFLOW) sc->lmc_dot3stats.dot3StatsInternalTransmitUnderflows++; } else { if (d_status & TULIP_DSTS_TxDEFERRED) sc->lmc_dot3stats.dot3StatsDeferredTransmissions++; } } if (++ri->ri_nextin == ri->ri_last) ri->ri_nextin = ri->ri_first; ri->ri_free++; descs++; /*sc->lmc_if.if_flags &= ~IFF_OACTIVE;*/ sc->lmc_out_deficit++; } /* * If nothing left to transmit, disable the timer. * Else if progress, reset the timer back to 2 ticks. */ sc->lmc_opackets += xmits; return descs; } static void lmc_print_abnormal_interrupt (lmc_softc_t * const sc, u_int32_t csr) { printf(LMC_PRINTF_FMT ": Abnormal interrupt\n", LMC_PRINTF_ARGS); } static void lmc_intr_handler(lmc_softc_t * const sc, int *progress_p) { u_int32_t csr; while ((csr = LMC_CSR_READ(sc, csr_status)) & sc->lmc_intrmask) { *progress_p = 1; LMC_CSR_WRITE(sc, csr_status, csr); if (csr & TULIP_STS_SYSERROR) { sc->lmc_last_system_error = (csr & TULIP_STS_ERRORMASK) >> TULIP_STS_ERR_SHIFT; if (sc->lmc_flags & LMC_NOMESSAGES) { sc->lmc_flags |= LMC_SYSTEMERROR; } else { printf(LMC_PRINTF_FMT ": system error: %s\n", LMC_PRINTF_ARGS, lmc_system_errors[sc->lmc_last_system_error]); } sc->lmc_flags |= LMC_NEEDRESET; sc->lmc_system_errors++; break; } if (csr & (TULIP_STS_RXINTR | TULIP_STS_RXNOBUF)) { u_int32_t misses = LMC_CSR_READ(sc, csr_missed_frames); if (csr & TULIP_STS_RXNOBUF) sc->lmc_dot3stats.dot3StatsMissedFrames += misses & 0xFFFF; /* * Pass 2.[012] of the 21140A-A[CDE] may hang and/or corrupt data * on receive overflows. */ if ((misses & 0x0FFE0000) && (sc->lmc_features & LMC_HAVE_RXBADOVRFLW)) { sc->lmc_dot3stats.dot3StatsInternalMacReceiveErrors++; /* * Stop the receiver process and spin until it's stopped. * Tell rx_intr to drop the packets it dequeues. */ LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode & ~TULIP_CMD_RXRUN); while ((LMC_CSR_READ(sc, csr_status) & TULIP_STS_RXSTOPPED) == 0) ; LMC_CSR_WRITE(sc, csr_status, TULIP_STS_RXSTOPPED); sc->lmc_flags |= LMC_RXIGNORE; } lmc_rx_intr(sc); if (sc->lmc_flags & LMC_RXIGNORE) { /* * Restart the receiver. */ sc->lmc_flags &= ~LMC_RXIGNORE; LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode); } } if (csr & TULIP_STS_ABNRMLINTR) { u_int32_t tmp = csr & sc->lmc_intrmask & ~(TULIP_STS_NORMALINTR|TULIP_STS_ABNRMLINTR); if (csr & TULIP_STS_TXUNDERFLOW) { if ((sc->lmc_cmdmode & TULIP_CMD_THRESHOLDCTL) != TULIP_CMD_THRSHLD160) { sc->lmc_cmdmode += TULIP_CMD_THRSHLD96; sc->lmc_flags |= LMC_NEWTXTHRESH; } else if (sc->lmc_features & LMC_HAVE_STOREFWD) { sc->lmc_cmdmode |= TULIP_CMD_STOREFWD; sc->lmc_flags |= LMC_NEWTXTHRESH; } } if (sc->lmc_flags & LMC_NOMESSAGES) { sc->lmc_statusbits |= tmp; } else { lmc_print_abnormal_interrupt(sc, tmp); sc->lmc_flags |= LMC_NOMESSAGES; } LMC_CSR_WRITE(sc, csr_command, sc->lmc_cmdmode); } if (csr & TULIP_STS_TXINTR) lmc_tx_intr(sc); if (sc->lmc_flags & LMC_WANTTXSTART) lmc_ifstart(sc); } } static lmc_intrfunc_t lmc_intr_normal(void *arg) { lmc_softc_t * sc = (lmc_softc_t *) arg; int progress = 0; lmc_intr_handler(sc, &progress); #if !defined(LMC_VOID_INTRFUNC) return progress; #endif } static struct mbuf * lmc_mbuf_compress(struct mbuf *m) { struct mbuf *m0; #if MCLBYTES >= LMC_MTU + PPP_HEADER_LEN && !defined(BIG_PACKET) MGETHDR(m0, M_DONTWAIT, MT_DATA); if (m0 != NULL) { if (m->m_pkthdr.len > MHLEN) { MCLGET(m0, M_DONTWAIT); if ((m0->m_flags & M_EXT) == 0) { m_freem(m); m_freem(m0); return NULL; } } m_copydata(m, 0, m->m_pkthdr.len, mtod(m0, caddr_t)); m0->m_pkthdr.len = m0->m_len = m->m_pkthdr.len; } #else int mlen = MHLEN; int len = m->m_pkthdr.len; struct mbuf **mp = &m0; while (len > 0) { if (mlen == MHLEN) { MGETHDR(*mp, M_DONTWAIT, MT_DATA); } else { MGET(*mp, M_DONTWAIT, MT_DATA); } if (*mp == NULL) { m_freem(m0); m0 = NULL; break; } if (len > MLEN) { MCLGET(*mp, M_DONTWAIT); if (((*mp)->m_flags & M_EXT) == 0) { m_freem(m0); m0 = NULL; break; } (*mp)->m_len = (len <= MCLBYTES ? len : MCLBYTES); } else { (*mp)->m_len = (len <= mlen ? len : mlen); } m_copydata(m, m->m_pkthdr.len - len, (*mp)->m_len, mtod((*mp), caddr_t)); len -= (*mp)->m_len; mp = &(*mp)->m_next; mlen = MLEN; } #endif m_freem(m); return m0; } /* * queue the mbuf handed to us for the interface. If we cannot * queue it, return the mbuf. Return NULL if the mbuf was queued. */ static struct mbuf * lmc_txput(lmc_softc_t * const sc, struct mbuf *m) { lmc_ringinfo_t * const ri = &sc->lmc_txinfo; tulip_desc_t *eop, *nextout; int segcnt, free; u_int32_t d_status; struct mbuf *m0; #if defined(LMC_DEBUG) if ((sc->lmc_cmdmode & TULIP_CMD_TXRUN) == 0) { printf(LMC_PRINTF_FMT ": txput: tx not running\n", LMC_PRINTF_ARGS); sc->lmc_flags |= LMC_WANTTXSTART; goto finish; } #endif /* * Now we try to fill in our transmit descriptors. This is * a bit reminiscent of going on the Ark two by two * since each descriptor for the TULIP can describe * two buffers. So we advance through packet filling * each of the two entries at a time to fill each * descriptor. Clear the first and last segment bits * in each descriptor (actually just clear everything * but the end-of-ring or chain bits) to make sure * we don't get messed up by previously sent packets. * * We may fail to put the entire packet on the ring if * there is either not enough ring entries free or if the * packet has more than MAX_TXSEG segments. In the former * case we will just wait for the ring to empty. In the * latter case we have to recopy. */ again: d_status = 0; eop = nextout = ri->ri_nextout; m0 = m; segcnt = 0; free = ri->ri_free; do { int len = m0->m_len; caddr_t addr = mtod(m0, caddr_t); unsigned clsize = CLBYTES - (((u_long) addr) & (CLBYTES-1)); while (len > 0) { unsigned slen = min(len, clsize); #ifdef BIG_PACKET int partial = 0; if (slen >= 2048) slen = 2040, partial = 1; #endif segcnt++; if (segcnt > LMC_MAX_TXSEG) { /* * The packet exceeds the number of transmit * buffer entries that we can use for one * packet, so we have recopy it into one mbuf * and then try again. */ m = lmc_mbuf_compress(m); if (m == NULL) goto finish; goto again; } if (segcnt & 1) { if (--free == 0) { /* * See if there's any unclaimed space * in the transmit ring. */ if ((free += lmc_tx_intr(sc)) == 0) { /* * There's no more room but * since nothing has been * committed at this point, * just show output is active, * put back the mbuf and * return. */ sc->lmc_flags |= LMC_WANTTXSTART; goto finish; } } eop = nextout; if (++nextout == ri->ri_last) nextout = ri->ri_first; eop->d_flag &= TULIP_DFLAG_ENDRING; eop->d_flag |= TULIP_DFLAG_TxNOPADDING; if (sc->ictl.crc_length == 16) eop->d_flag |= TULIP_DFLAG_TxHASCRC; eop->d_status = d_status; eop->d_addr1 = LMC_KVATOPHYS(sc, addr); eop->d_length1 = slen; } else { /* * Fill in second half of descriptor */ eop->d_addr2 = LMC_KVATOPHYS(sc, addr); eop->d_length2 = slen; } d_status = TULIP_DSTS_OWNER; len -= slen; addr += slen; #ifdef BIG_PACKET if (partial) continue; #endif clsize = CLBYTES; } } while ((m0 = m0->m_next) != NULL); /* * The descriptors have been filled in. Now get ready * to transmit. */ _IF_ENQUEUE(&sc->lmc_txq, m); m = NULL; /* * Make sure the next descriptor after this packet is owned * by us since it may have been set up above if we ran out * of room in the ring. */ nextout->d_status = 0; /* * If we only used the first segment of the last descriptor, * make sure the second segment will not be used. */ if (segcnt & 1) { eop->d_addr2 = 0; eop->d_length2 = 0; } /* * Mark the last and first segments, indicate we want a transmit * complete interrupt, and tell it to transmit! */ eop->d_flag |= TULIP_DFLAG_TxLASTSEG | TULIP_DFLAG_TxWANTINTR; /* * Note that ri->ri_nextout is still the start of the packet * and until we set the OWNER bit, we can still back out of * everything we have done. */ ri->ri_nextout->d_flag |= TULIP_DFLAG_TxFIRSTSEG; ri->ri_nextout->d_status = TULIP_DSTS_OWNER; LMC_CSR_WRITE(sc, csr_txpoll, 1); /* * This advances the ring for us. */ ri->ri_nextout = nextout; ri->ri_free = free; /* * switch back to the single queueing ifstart. */ sc->lmc_flags &= ~LMC_WANTTXSTART; sc->lmc_xmit_busy = 0; sc->lmc_out_dog = 0; /* * If we want a txstart, there must be not enough space in the * transmit ring. So we want to enable transmit done interrupts * so we can immediately reclaim some space. When the transmit * interrupt is posted, the interrupt handler will call tx_intr * to reclaim space and then txstart (since WANTTXSTART is set). * txstart will move the packet into the transmit ring and clear * WANTTXSTART thereby causing TXINTR to be cleared. */ finish: return m; } /* * These routines gets called at device spl */ static ifnet_ret_t lmc_ifstart(lmc_softc_t * const sc) { struct mbuf *m; if (sc->lmc_flags & LMC_IFUP) { sc->lmc_xmit_busy = 1; for(;;) { struct ifqueue *q = &sc->lmc_xmitq_hipri; IF_DEQUEUE(q, m); if (m == NULL) { q = &sc->lmc_xmitq; IF_DEQUEUE(q, m); } if (m) { sc->lmc_outbytes = m->m_pkthdr.len; sc->lmc_opackets++; if ((m = lmc_txput(sc, m)) != NULL) { IF_PREPEND(q, m); printf(LMC_PRINTF_FMT ": lmc_txput failed\n", LMC_PRINTF_ARGS); break; } LMC_CSR_WRITE(sc, csr_txpoll, 1); } else break; } } } static ifnet_ret_t lmc_ifstart_one(lmc_softc_t * const sc) { struct mbuf *m; if ((sc->lmc_flags & LMC_IFUP)) { struct ifqueue *q = &sc->lmc_xmitq_hipri; IF_DEQUEUE(q, m); if (m == NULL) { q = &sc->lmc_xmitq; IF_DEQUEUE(q, m); } if (m) { sc->lmc_outbytes += m->m_pkthdr.len; sc->lmc_opackets++; if ((m = lmc_txput(sc, m)) != NULL) { IF_PREPEND(q, m); } LMC_CSR_WRITE(sc, csr_txpoll, 1); } } } /* * Set up the OS interface magic and attach to the operating system * network services. */ static int lmc_attach(lmc_softc_t * const sc) { /* * we have found a node, make sure our 'type' is availabe. */ if (ng_lmc_done_init == 0) ng_lmc_init(NULL); if (ng_make_node_common(&typestruct, &sc->lmc_node) != 0) return (0); sc->lmc_node->private = sc; callout_handle_init(&sc->lmc_handle); sc->lmc_xmitq.ifq_maxlen = IFQ_MAXLEN; sc->lmc_xmitq_hipri.ifq_maxlen = IFQ_MAXLEN; mtx_init(&sc->lmc_xmitq.ifq_mtx, "lmc_xmitq", MTX_DEF); mtx_init(&sc->lmc_xmitq_hipri.ifq_mtx, "lmc_xmitq_hipri", MTX_DEF); sprintf(sc->lmc_nodename, "%s%d", NG_LMC_NODE_TYPE, sc->lmc_unit); if (ng_name_node(sc->lmc_node, sc->lmc_nodename)) { ng_rmnode(sc->lmc_node); ng_unref(sc->lmc_node); return (0); } sc->lmc_running = 0; /* * turn off those LEDs... */ sc->lmc_miireg16 |= LMC_MII16_LED_ALL; lmc_led_on(sc, LMC_MII16_LED0); return 1; } static void lmc_initring(lmc_softc_t * const sc, lmc_ringinfo_t * const ri, tulip_desc_t *descs, int ndescs) { ri->ri_max = ndescs; ri->ri_first = descs; ri->ri_last = ri->ri_first + ri->ri_max; bzero((caddr_t) ri->ri_first, sizeof(ri->ri_first[0]) * ri->ri_max); ri->ri_last[-1].d_flag = TULIP_DFLAG_ENDRING; } #ifdef LMC_DEBUG static void ng_lmc_dump_packet(struct mbuf *m) { int i; printf("mbuf: %d bytes, %s packet\n", m->m_len, (m->m_type == MT_DATA)?"data":"other"); for (i=0; i < m->m_len; i++) { if( (i % 8) == 0 ) { if( i ) printf("\n"); printf("\t"); } else printf(" "); printf( "0x%02x", m->m_dat[i] ); } printf("\n"); } #endif /* LMC_DEBUG */ /* Device timeout/watchdog routine */ static void ng_lmc_watchdog_frame(void *arg) { lmc_softc_t * sc = (lmc_softc_t *) arg; int s; int speed; if(sc->lmc_running == 0) return; /* if we are not running let timeouts die */ /* * calculate the apparent throughputs * XXX a real hack */ s = splimp(); speed = sc->lmc_inbytes - sc->lmc_lastinbytes; sc->lmc_lastinbytes = sc->lmc_inbytes; if ( sc->lmc_inrate < speed ) sc->lmc_inrate = speed; speed = sc->lmc_outbytes - sc->lmc_lastoutbytes; sc->lmc_lastoutbytes = sc->lmc_outbytes; if ( sc->lmc_outrate < speed ) sc->lmc_outrate = speed; sc->lmc_inlast++; splx(s); if ((sc->lmc_inlast > LMC_QUITE_A_WHILE) && (sc->lmc_out_deficit > LMC_LOTS_OF_PACKETS)) { log(LOG_ERR, "%s%d: No response from remote end\n", sc->lmc_name, sc->lmc_unit); s = splimp(); lmc_ifdown(sc); lmc_ifup(sc); sc->lmc_inlast = sc->lmc_out_deficit = 0; splx(s); } else if (sc->lmc_xmit_busy) { if (sc->lmc_out_dog == 0) { log(LOG_ERR, "ar%d: Transmit failure.. no clock?\n", sc->lmc_unit); s = splimp(); lmc_watchdog(sc); #if 0 lmc_ifdown(sc); lmc_ifup(sc); #endif splx(s); sc->lmc_inlast = sc->lmc_out_deficit = 0; } else { sc->lmc_out_dog--; } } lmc_watchdog(sc); sc->lmc_handle = timeout(ng_lmc_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 ng_lmc_constructor(node_p *nodep) { return (EINVAL); } /* * give our ok for a hook to be added... * If we are not running this should kick the device into life. * We allow hooks called "control", "rawdata", and "debug". * The hook's private info points to our stash of info about that * device. */ static int ng_lmc_newhook(node_p node, hook_p hook, const char *name) { lmc_softc_t * sc = (lmc_softc_t *) node->private; /* * check if it's our friend the debug hook */ if (strcmp(name, NG_LMC_HOOK_DEBUG) == 0) { hook->private = NULL; /* paranoid */ sc->lmc_debug_hook = hook; return (0); } /* * Check for raw mode hook. */ if (strcmp(name, NG_LMC_HOOK_RAW) != 0) { return (EINVAL); } hook->private = sc; sc->lmc_hook = hook; sc->lmc_datahooks++; lmc_ifup(sc); return (0); } /* * incoming messages. * Just respond to the generic TEXT_STATUS message */ static int ng_lmc_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { lmc_softc_t *sc = (lmc_softc_t *) node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NG_LMC_COOKIE: switch (msg->header.cmd) { case NGM_LMC_GET_CTL: { lmc_ctl_t *ctl; NG_MKRESPONSE(resp, msg, sizeof(*ctl), M_NOWAIT); if (!resp) { error = ENOMEM; break; } ctl = (lmc_ctl_t *) resp->data; memcpy( ctl, &sc->ictl, sizeof(*ctl) ); break; } case NGM_LMC_SET_CTL: { lmc_ctl_t *ctl; if (msg->header.arglen != sizeof(*ctl)) { error = EINVAL; break; } ctl = (lmc_ctl_t *) msg->data; sc->lmc_media->set_status(sc, ctl); break; } default: error = EINVAL; /* unknown command */ break; } 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; MALLOC(resp, struct ng_mesg *, resplen, M_NETGRAPH, M_NOWAIT | M_ZERO); if (resp == NULL) { error = ENOMEM; break; } arg = (resp)->data; /* * Put in the throughput information. */ pos = sprintf(arg, "%ld bytes in, %ld bytes out\n" "highest rate seen: %ld B/S in, " "%ld B/S out\n", sc->lmc_inbytes, sc->lmc_outbytes, sc->lmc_inrate, sc->lmc_outrate); pos += sprintf(arg + pos, "%ld output errors\n", sc->lmc_oerrors); pos += sprintf(arg + pos, "%ld input errors\n", sc->lmc_ierrors); resp->header.version = NG_VERSION; resp->header.arglen = strlen(arg) + 1; resp->header.token = msg->header.token; resp->header.typecookie = NG_LMC_COOKIE; resp->header.cmd = msg->header.cmd; strncpy(resp->header.cmdstr, "status", NG_CMDSTRLEN); } break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } /* Take care of synchronous response, if any */ if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); free(msg, M_NETGRAPH); return (error); } /* * get data from another node and transmit it to the line */ static int ng_lmc_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { int s; int error = 0; lmc_softc_t * sc = (lmc_softc_t *) hook->node->private; struct ifqueue *xmitq_p; /* * data doesn't come in from just anywhere (e.g control hook) */ if ( hook->private == NULL) { error = ENETDOWN; goto bad; } /* * Now queue the data for when it can be sent */ if (meta && meta->priority > 0) { xmitq_p = (&sc->lmc_xmitq_hipri); } else { xmitq_p = (&sc->lmc_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); lmc_ifstart_one(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_DATA(m, 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 ng_lmc_rmnode(node_p node) { lmc_softc_t * sc = (lmc_softc_t *) node->private; lmc_ifdown(sc); ng_cutlinks(node); node->flags &= ~NG_INVALID; /* bounce back to life */ return (0); } /* already linked */ static int ng_lmc_connect(hook_p hook) { + /* We are probably not at splnet.. force outward queueing */ + hook->peer->flags |= HK_QUEUE; /* be really amiable and just say "YUP that's OK by me! " */ return (0); } /* * notify on hook disconnection (destruction) * * 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 ng_lmc_disconnect(hook_p hook) { lmc_softc_t * sc = (lmc_softc_t *) hook->node->private; int s; /* * If it's the data hook, then free resources etc. */ if (hook->private) { s = splimp(); sc->lmc_datahooks--; if (sc->lmc_datahooks == 0) lmc_ifdown(sc); splx(s); } else { sc->lmc_debug_hook = NULL; } return (0); } /* * called during bootup * or LKM loading to put this type into the list of known modules */ static void ng_lmc_init(void *ignored) { if (ng_newtype(&typestruct)) printf("ng_lmc install failed\n"); ng_lmc_done_init = 1; } /* * This is the PCI configuration support. */ #define PCI_CFID 0x00 /* Configuration ID */ #define PCI_CFCS 0x04 /* Configurtion Command/Status */ #define PCI_CFRV 0x08 /* Configuration Revision */ #define PCI_CFLT 0x0c /* Configuration Latency Timer */ #define PCI_CBIO 0x10 /* Configuration Base IO Address */ #define PCI_CBMA 0x14 /* Configuration Base Memory Address */ #define PCI_SSID 0x2c /* subsystem config register */ #define PCI_CFIT 0x3c /* Configuration Interrupt */ #define PCI_CFDA 0x40 /* Configuration Driver Area */ #include "dev/lmc/if_lmc_fbsd3.c" Index: head/sys/dev/sr/if_sr.c =================================================================== --- head/sys/dev/sr/if_sr.c (revision 69921) +++ head/sys/dev/sr/if_sr.c (revision 69922) @@ -1,3359 +1,3363 @@ /* * Copyright (c) 1996 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. * * $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 "sr.h" #include "opt_netgraph.h" #ifdef NETGRAPH #include #endif /* NETGRAPH */ #ifndef NETGRAPH #include "sppp.h" #if NSPPP <= 0 #error Device 'sr' requires sppp. #endif #endif /* NETGRAPH */ #include #include #include #include #include #include #include #include #include #ifdef NETGRAPH #include #else /* NETGRAPH */ #include #include #endif /* NETGRAPH */ #include #include #include #include #ifdef NETGRAPH #include #include #endif /* NETGRAPH */ /* #define USE_MODEMCK */ #ifndef COMPAT_OLDISA #error "The sr device requires the old isa compatibility shims" #endif #ifndef BUGGY #define BUGGY 0 #endif #ifndef NETGRAPH #define PPP_HEADER_LEN 4 #endif /* NETGRAPH */ /* * These macros are used to hide the difference between the way the * ISA N2 cards and the PCI N2 cards access the Hitachi 64570 SCA. */ #define SRC_GET8(base,off) (*hc->src_get8)(base,(u_int)&off) #define SRC_GET16(base,off) (*hc->src_get16)(base,(u_int)&off) #define SRC_PUT8(base,off,d) (*hc->src_put8)(base,(u_int)&off,d) #define SRC_PUT16(base,off,d) (*hc->src_put16)(base,(u_int)&off,d) /* * These macros enable/disable the DPRAM and select the correct * DPRAM page. */ #define SRC_GET_WIN(addr) ((addr >> SRC_WIN_SHFT) & SR_PG_MSK) #define SRC_SET_ON(iobase) outb(iobase+SR_PCR, \ SR_PCR_MEM_WIN | inb(iobase+SR_PCR)) #define SRC_SET_MEM(iobase,win) outb(iobase+SR_PSR, SRC_GET_WIN(win) | \ (inb(iobase+SR_PSR) & ~SR_PG_MSK)) #define SRC_SET_OFF(iobase) outb(iobase+SR_PCR, \ ~SR_PCR_MEM_WIN & inb(iobase+SR_PCR)) /* * Define the hardware (card information) structure needed to keep * track of the device itself... There is only one per card. */ struct sr_hardc { struct sr_hardc *next; /* PCI card linkage */ struct sr_softc *sc; /* software channels */ int cunit; /* card w/in system */ u_short iobase; /* I/O Base Address */ int cardtype; int numports; /* # of ports on cd */ int mempages; u_int memsize; /* DPRAM size: bytes */ u_int winmsk; vm_offset_t sca_base; vm_offset_t mem_pstart; /* start of buffer */ caddr_t mem_start; /* start of DP RAM */ caddr_t mem_end; /* end of DP RAM */ caddr_t plx_base; sca_regs *sca; /* register array */ /* * We vectorize the following functions to allow re-use between the * ISA card's needs and those of the PCI card. */ void (*src_put8)(u_int base, u_int off, u_int val); void (*src_put16)(u_int base, u_int off, u_int val); u_int (*src_get8)(u_int base, u_int off); u_int (*src_get16)(u_int base, u_int off); }; static int next_sc_unit = 0; #ifndef NETGRAPH static int sr_watcher = 0; #endif /* NETGRAPH */ static struct sr_hardc sr_hardc[NSR]; static struct sr_hardc *sr_hardc_pci; /* * 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_NODELEN + 1]; /* 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 */ #if ( __FreeBSD__ >= 3 ) struct callout_handle handle; /* timeout(9) handle */ #endif 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 */ /* * List of valid interrupt numbers for the N2 ISA card. */ static int sr_irqtable[16] = { 0, /* 0 */ 0, /* 1 */ 0, /* 2 */ 1, /* 3 */ 1, /* 4 */ 1, /* 5 */ 0, /* 6 */ 1, /* 7 */ 0, /* 8 */ 0, /* 9 */ 1, /* 10 */ 1, /* 11 */ 1, /* 12 */ 0, /* 13 */ 0, /* 14 */ 1 /* 15 */ }; static int srprobe(struct isa_device *id); static int srattach_isa(struct isa_device *id); struct isa_driver srdriver = { INTR_TYPE_NET, srprobe, srattach_isa, "sr" }; COMPAT_ISA_DRIVER(sr, srdriver); /* * 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 struct sr_hardc *srattach_pci(int unit, vm_offset_t plx_vaddr, vm_offset_t sca_vaddr); void srintr_hc(struct sr_hardc *hc); static ointhand2_t srintr; static int srattach(struct sr_hardc *hc); 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 static void sr_modemck(void *x); #else static void sr_modemck(struct sr_softc *x); #endif /* NETGRAPH */ static u_int src_get8_io(u_int base, u_int off); static u_int src_get16_io(u_int base, u_int off); static void src_put8_io(u_int base, u_int off, u_int val); static void src_put16_io(u_int base, u_int off, u_int val); static u_int src_get8_mem(u_int base, u_int off); static u_int src_get16_mem(u_int base, u_int off); static void src_put8_mem(u_int base, u_int off, u_int val); static void src_put16_mem(u_int base, u_int off, u_int val); #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_rmnode; 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 = { NG_VERSION, NG_SR_NODE_TYPE, NULL, ngsr_constructor, ngsr_rcvmsg, ngsr_rmnode, ngsr_newhook, NULL, ngsr_connect, ngsr_rcvdata, ngsr_rcvdata, ngsr_disconnect, NULL }; static int ngsr_done_init = 0; #endif /* NETGRAPH */ /* * I/O for ISA N2 card(s) */ #define SRC_REG(iobase,y) ((((y) & 0xf) + (((y) & 0xf0) << 6) + \ (iobase)) | 0x8000) static u_int src_get8_io(u_int base, u_int off) { return inb(SRC_REG(base, off)); } static u_int src_get16_io(u_int base, u_int off) { return inw(SRC_REG(base, off)); } static void src_put8_io(u_int base, u_int off, u_int val) { outb(SRC_REG(base, off), val); } static void src_put16_io(u_int base, u_int off, u_int val) { outw(SRC_REG(base, off), val); } /* * I/O for PCI N2 card(s) */ #define SRC_PCI_SCA_REG(y) ((y & 2) ? ((y & 0xfd) + 0x100) : y) static u_int src_get8_mem(u_int base, u_int off) { return *((u_char *)(base + SRC_PCI_SCA_REG(off))); } static u_int src_get16_mem(u_int base, u_int off) { return *((u_short *)(base + SRC_PCI_SCA_REG(off))); } static void src_put8_mem(u_int base, u_int off, u_int val) { *((u_char *)(base + SRC_PCI_SCA_REG(off))) = (u_char)val; } static void src_put16_mem(u_int base, u_int off, u_int val) { *((u_short *)(base + SRC_PCI_SCA_REG(off))) = (u_short)val; } /* * Probe for an ISA card. If it is there, size its memory. Then get the * rest of its information and fill it in. */ static int srprobe(struct isa_device *id) { struct sr_hardc *hc = &sr_hardc[id->id_unit]; u_int pgs, i, tmp; u_short port; u_short *smem; u_char mar; sca_regs *sca = 0; /* * Now see if the card is realy there. */ hc->cardtype = SR_CRD_N2; /* * We have to fill these in early because the SRC_PUT* and SRC_GET* * macros use them. */ hc->src_get8 = src_get8_io; hc->src_get16 = src_get16_io; hc->src_put8 = src_put8_io; hc->src_put16 = src_put16_io; hc->sca = 0; port = id->id_iobase; hc->numports = NCHAN; /* assumed # of channels on the card */ if (id->id_flags & SR_FLAGS_NCHAN_MSK) hc->numports = id->id_flags & SR_FLAGS_NCHAN_MSK; outb(port + SR_PCR, 0); /* turn off the card */ /* * Next, we'll test the Base Address Register to retension of * data... ... seeing if we're *really* talking to an N2. */ for (i = 0; i < 0x100; i++) { outb(port + SR_BAR, i); inb(port + SR_PCR); tmp = inb(port + SR_BAR); if (tmp != i) { printf("sr%d: probe failed BAR %x, %x.\n", id->id_unit, i, tmp); return 0; } } /* * Now see if we can see the SCA. */ outb(port + SR_PCR, SR_PCR_SCARUN | inb(port + SR_PCR)); SRC_PUT8(port, sca->wcrl, 0); SRC_PUT8(port, sca->wcrm, 0); SRC_PUT8(port, sca->wcrh, 0); SRC_PUT8(port, sca->pcr, 0); SRC_PUT8(port, sca->msci[0].tmc, 0); inb(port); tmp = SRC_GET8(port, sca->msci[0].tmc); if (tmp != 0) { printf("sr%d: Error reading SCA 0, %x\n", id->id_unit, tmp); return 0; } SRC_PUT8(port, sca->msci[0].tmc, 0x5A); inb(port); tmp = SRC_GET8(port, sca->msci[0].tmc); if (tmp != 0x5A) { printf("sr%d: Error reading SCA 0x5A, %x\n", id->id_unit, tmp); return 0; } SRC_PUT16(port, sca->dmac[0].cda, 0); inb(port); tmp = SRC_GET16(port, sca->dmac[0].cda); if (tmp != 0) { printf("sr%d: Error reading SCA 0, %x\n", id->id_unit, tmp); return 0; } SRC_PUT16(port, sca->dmac[0].cda, 0x55AA); inb(port); tmp = SRC_GET16(port, sca->dmac[0].cda); if (tmp != 0x55AA) { printf("sr%d: Error reading SCA 0x55AA, %x\n", id->id_unit, tmp); return 0; } /* * OK, the board's interface registers seem to work. Now we'll see * if the Dual-Ported RAM is fully accessible... */ outb(port + SR_PCR, SR_PCR_EN_VPM | SR_PCR_ISA16); outb(port + SR_PSR, SR_PSR_WIN_16K); /* * Take the kernel "virtual" address supplied to us and convert * it to a "real" address. Then program the card to use that. */ mar = (kvtop(id->id_maddr) >> 16) & SR_PCR_16M_SEL; outb(port + SR_PCR, mar | inb(port + SR_PCR)); mar = kvtop(id->id_maddr) >> 12; outb(port + SR_BAR, mar); outb(port + SR_PCR, inb(port + SR_PCR) | SR_PCR_MEM_WIN); smem = (u_short *)id->id_maddr; /* DP RAM Address */ /* * Here we will perform the memory scan to size the device. * * This is done by marking each potential page with a magic number. * We then loop through the pages looking for that magic number. As * soon as we no longer see that magic number, we'll quit the scan, * knowing that no more memory is present. This provides the number * of pages present on the card. * * Note: We're sizing 16K memory granules. */ for (i = 0; i <= SR_PSR_PG_SEL; i++) { outb(port + SR_PSR, (inb(port + SR_PSR) & ~SR_PSR_PG_SEL) | i); *smem = 0xAA55; } for (i = 0; i <= SR_PSR_PG_SEL; i++) { outb(port + SR_PSR, (inb(port + SR_PSR) & ~SR_PSR_PG_SEL) | i); if (*smem != 0xAA55) { /* * If we have less than 64k of memory, give up. That * is 4 x 16k pages. */ if (i < 4) { printf("sr%d: Bad mem page %d, mem %x, %x.\n", id->id_unit, i, 0xAA55, *smem); return 0; } break; } *smem = i; } hc->mempages = i; hc->memsize = i * SRC_WIN_SIZ; hc->winmsk = SRC_WIN_MSK; pgs = i; /* final count of 16K pages */ /* * This next loop erases the contents of that page in DPRAM */ for (i = 0; i <= pgs; i++) { outb(port + SR_PSR, (inb(port + SR_PSR) & ~SR_PSR_PG_SEL) | i); bzero(smem, SRC_WIN_SIZ); } SRC_SET_OFF(port); /* * We have a card here, fill in what we can. */ id->id_msize = SRC_WIN_SIZ; hc->iobase = id->id_iobase; hc->sca_base = id->id_iobase; hc->mem_start = id->id_maddr; hc->mem_end = (id->id_maddr + id->id_msize) - 1; hc->mem_pstart = 0; hc->cunit = id->id_unit; /* * Do a little sanity check. */ if (sr_irqtable[ffs(id->id_irq) - 1] == 0) printf("sr%d: Warning: illegal interrupt %d chosen.\n", id->id_unit, ffs(id->id_irq) - 1); /* * Bogus card configuration */ if ((hc->numports > NCHAN) /* only 2 ports/card */ ||(hc->memsize > (512 * 1024))) /* no more than 256K */ return 0; return SRC_IO_SIZ; /* return the amount of IO addresses used. */ } /* * srattach_isa and srattach_pci allocate memory for hardc, softc and * data buffers. It also does any initialization that is bus specific. * At the end they call the common srattach() function. */ static int srattach_isa(struct isa_device *id) { u_char mar; struct sr_hardc *hc = &sr_hardc[id->id_unit]; /* * Allocate the software interface table(s) */ MALLOC(hc->sc, struct sr_softc *, hc->numports * sizeof(struct sr_softc), M_DEVBUF, M_WAITOK | M_ZERO); if (hc->sc == NULL) return(0); id->id_ointr = srintr; outb(hc->iobase + SR_PCR, inb(hc->iobase + SR_PCR) | SR_PCR_SCARUN); outb(hc->iobase + SR_PSR, inb(hc->iobase + SR_PSR) | SR_PSR_EN_SCA_DMA); outb(hc->iobase + SR_MCR, SR_MCR_DTR0 | SR_MCR_DTR1 | SR_MCR_TE0 | SR_MCR_TE1); SRC_SET_ON(hc->iobase); /* * Configure the card. Mem address, irq, */ mar = (kvtop(id->id_maddr) >> 16) & SR_PCR_16M_SEL; outb(hc->iobase + SR_PCR, mar | (inb(hc->iobase + SR_PCR) & ~SR_PCR_16M_SEL)); mar = kvtop(id->id_maddr) >> 12; outb(hc->iobase + SR_BAR, mar); /* * Get the TX clock direction and configuration. The default is a * single external clock which is used by RX and TX. */ #ifdef N2_TEST_SPEED if (sr_test_speed[0] > 0) hc->sc[0].clk_cfg = SR_FLAGS_INT_CLK; else if (id->id_flags & SR_FLAGS_0_CLK_MSK) hc->sc[0].clk_cfg = (id->id_flags & SR_FLAGS_0_CLK_MSK) >> SR_FLAGS_CLK_SHFT; #else if (id->id_flags & SR_FLAGS_0_CLK_MSK) hc->sc[0].clk_cfg = (id->id_flags & SR_FLAGS_0_CLK_MSK) >> SR_FLAGS_CLK_SHFT; #endif 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 (id->id_flags & SR_FLAGS_1_CLK_MSK) hc->sc[1].clk_cfg = (id->id_flags & SR_FLAGS_1_CLK_MSK) >> (SR_FLAGS_CLK_SHFT + SR_FLAGS_CLK_CHAN_SHFT); return srattach(hc); } struct sr_hardc * srattach_pci(int unit, vm_offset_t plx_vaddr, vm_offset_t sca_vaddr) { int numports, pndx; u_int fecr, *fecrp = (u_int *)(sca_vaddr + SR_FECR); struct sr_hardc *hc, **hcp; /* * Configure the PLX. This is magic. I'm doing it just like I'm told * to. :-) * * offset * 0x00 - Map Range - Mem-mapped to locate anywhere * 0x04 - Re-Map - PCI address decode enable * 0x18 - Bus Region - 32-bit bus, ready enable * 0x1c - Master Range - include all 16 MB * 0x20 - Master RAM - Map SCA Base at 0 * 0x28 - Master Remap - direct master memory enable * 0x68 - Interrupt - Enable interrupt (0 to disable) * * Note: This is "cargo cult" stuff. - jrc */ *((u_int *)(plx_vaddr + 0x00)) = 0xfffff000; *((u_int *)(plx_vaddr + 0x04)) = 1; *((u_int *)(plx_vaddr + 0x18)) = 0x40030043; *((u_int *)(plx_vaddr + 0x1c)) = 0xff000000; *((u_int *)(plx_vaddr + 0x20)) = 0; *((u_int *)(plx_vaddr + 0x28)) = 0xe9; *((u_int *)(plx_vaddr + 0x68)) = 0x10900; /* * Get info from card. * * Only look for the second port if the first exists. Too many things * will break if we have only a second port. */ fecr = *fecrp; numports = 0; if (((fecr & SR_FECR_ID0) >> SR_FE_ID0_SHFT) != SR_FE_ID_NONE) { numports++; if (((fecr & SR_FECR_ID1) >> SR_FE_ID1_SHFT) != SR_FE_ID_NONE) numports++; } if (numports == 0) return NULL; hc = sr_hardc_pci; hcp = &sr_hardc_pci; while (hc) { hcp = &hc->next; hc = hc->next; } MALLOC(hc, struct sr_hardc *, sizeof(*hc), M_DEVBUF, M_WAITOK | M_ZERO); if (hc == NULL) return NULL; MALLOC(hc->sc, struct sr_softc *, numports * sizeof(struct sr_softc), M_DEVBUF, M_WAITOK | M_ZERO); if (hc->sc == NULL) { FREE(hc, M_DEVBUF); return NULL; } *hcp = hc; hc->numports = numports; hc->cunit = unit; hc->cardtype = SR_CRD_N2PCI; hc->plx_base = (caddr_t)plx_vaddr; hc->sca_base = sca_vaddr; hc->src_put8 = src_put8_mem; hc->src_put16 = src_put16_mem; hc->src_get8 = src_get8_mem; hc->src_get16 = src_get16_mem; /* * Malloc area for tx and rx buffers. For now allocate SRC_WIN_SIZ * (16k) for each buffer. * * Allocate the block below 16M because the N2pci card can only access * 16M memory at a time. * * (We could actually allocate a contiguous block above the 16MB limit, * but this would complicate card programming more than we want to * right now -jrc) */ hc->memsize = 2 * hc->numports * SRC_WIN_SIZ; hc->mem_start = contigmalloc(hc->memsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 0x10000, 0x1000000); if (hc->mem_start == NULL) { printf("src%d: pci: failed to allocate buffer space.\n", unit); return NULL; } hc->winmsk = 0xffffffff; hc->mem_end = (caddr_t)((u_int)hc->mem_start + hc->memsize); hc->mem_pstart = kvtop(hc->mem_start); bzero(hc->mem_start, hc->memsize); for (pndx = 0; pndx < numports; pndx++) { int intf_sw; struct sr_softc *sc; sc = &hc->sc[pndx]; 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; } } *fecrp = SR_FECR_DTR0 | SR_FECR_DTR1 | SR_FECR_TE0 | SR_FECR_TE1; srattach(hc); return hc; } /* * Register the ports on the adapter. * Fill in the info for each port. #ifndef NETGRAPH * Attach each port to sppp and bpf. #endif */ static int srattach(struct sr_hardc *hc) { struct sr_softc *sc = hc->sc; #ifndef NETGRAPH struct ifnet *ifp; #endif /* NETGRAPH */ int unit; /* index: channel w/in card */ /* * Report Card configuration information before we start configuring * each channel on the card... */ printf("src%d: %uK RAM (%d mempages) @ %08x-%08x, %u ports.\n", hc->cunit, hc->memsize / 1024, hc->mempages, (u_int)hc->mem_start, (u_int)hc->mem_end, hc->numports); src_init(hc); sr_init_sca(hc); /* * 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; ifp->if_unit = sc->unit; ifp->if_name = "sr"; 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) return (0); sc->node->private = 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", MTX_DEF); mtx_init(&sc->xmitq_hipri.ifq_mtx, "sr_xmitq_hipri", MTX_DEF); sprintf(sc->nodename, "%s%d", NG_SR_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { ng_rmnode(sc->node); ng_unref(sc->node); return (0); } sc->running = 0; #endif /* NETGRAPH */ } if (hc->mempages) SRC_SET_OFF(hc->iobase); return 1; } /* * 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(int unit) { struct sr_hardc *hc; hc = &sr_hardc[unit]; srintr_hc(hc); return; } void srintr_hc(struct sr_hardc *hc) { 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_base, sca->isr0); isr1 = SRC_GET8(hc->sca_base, sca->isr1); isr2 = SRC_GET8(hc->sca_base, 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->sca_base, dmac->cda, cda_value); SRC_PUT16(hc->sca_base, dmac->eda, eda_value); /* * Now we'll let the DMA status register know about this change */ SRC_PUT8(hc->sca_base, 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->iobase); SRC_SET_MEM(hc->iobase, 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->iobase); #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->iobase); 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 if (ifp->if_bpf) 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 *)blkp->txdesc; blkp->txeda = (u_short)((u_int)&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 printf("sr%d: srioctl(ifp=%08x, cmd=%08x, data=%08x)\n", ifp->if_unit, ifp, cmd, data); #endif was_up = ifp->if_flags & IFF_RUNNING; error = sppp_ioctl(ifp, cmd, data); #if BUGGY > 1 printf("sr%d: ioctl: ifsppp.pp_flags = %08x, if_flags %08x.\n", ifp->if_unit, ((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->sca_base, msci->st0); got_st1 = SRC_GET8(hc->sca_base, msci->st1); got_st3 = SRC_GET8(hc->sca_base, msci->st3); got_dsr = SRC_GET8(hc->sca_base, 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->sca_base, msci->st1) & SCA_ST1_UDRN) { SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_TXABORT); SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_TXENABLE); SRC_PUT8(hc->sca_base, 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) { u_int *fecrp; 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->sca_base, msci->ctl, SRC_GET8(hc->sca_base, msci->ctl) & ~SCA_CTL_RTS); if (sc->scachan == 0) switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) & ~SR_MCR_DTR0)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp &= ~SR_FECR_DTR0; break; } else switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) & ~SR_MCR_DTR1)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp &= ~SR_FECR_DTR1; break; } if (sc->scachan == 0) { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) | 0x000F); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, sca->ier1) | 0x000F); } else { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) | 0x00F0); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, sca->ier1) | 0x00F0); } SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_RXENABLE); inb(hc->iobase); /* XXX slow it down a bit. */ SRC_PUT8(hc->sca_base, 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) { u_int *fecrp; 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->sca_base, msci->cmd, SCA_CMD_RXDISABLE); inb(hc->iobase); /* XXX slow it down a bit. */ SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_TXDISABLE); SRC_PUT8(hc->sca_base, msci->ctl, SRC_GET8(hc->sca_base, msci->ctl) | SCA_CTL_RTS); if (sc->scachan == 0) switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) | SR_MCR_DTR0)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= SR_FECR_DTR0; break; } else switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) | SR_MCR_DTR1)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= SR_FECR_DTR1; break; } if (sc->scachan == 0) { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) & ~0x0F); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, sca->ier1) & ~0x0F); } else { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) & ~0xF0); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, 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_base, sca->pabr0, 0); SRC_PUT8(hc->sca_base, sca->pabr1, 0); SRC_PUT8(hc->sca_base, sca->wcrl, 0); SRC_PUT8(hc->sca_base, sca->wcrm, 0); SRC_PUT8(hc->sca_base, sca->wcrh, 0); /* * Configure the interrupt registers. Most are cleared until the * interface is configured. */ SRC_PUT8(hc->sca_base, sca->ier0, 0x00); /* MSCI interrupts. */ SRC_PUT8(hc->sca_base, sca->ier1, 0x00); /* DMAC interrupts */ SRC_PUT8(hc->sca_base, sca->ier2, 0x00); /* TIMER interrupts. */ SRC_PUT8(hc->sca_base, sca->itcr, 0x00); /* Use ivr and no intr * ack */ SRC_PUT8(hc->sca_base, sca->ivr, 0x40); /* Interrupt vector. */ SRC_PUT8(hc->sca_base, 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_base, sca->pcr, SCA_PCR_PR2); SRC_PUT8(hc->sca_base, 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 */ u_int *fecrp; /* pointer for PCI's MCR i/o */ 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->sca_base, msci->cmd, SCA_CMD_RESET); SRC_PUT8(hc->sca_base, msci->md0, SCA_MD0_CRC_1 | SCA_MD0_CRC_CCITT | SCA_MD0_CRC_ENABLE | SCA_MD0_MODE_HDLC); SRC_PUT8(hc->sca_base, msci->md1, SCA_MD1_NOADDRCHK); SRC_PUT8(hc->sca_base, 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->sca_base, msci->cmd, SCA_CMD_RXRESET); SRC_PUT8(hc->sca_base, 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->sca_base, msci->rxs, 0); SRC_PUT8(hc->sca_base, msci->txs, 0); break; case SR_FLAGS_EXT_SEP_CLK: #if BUGGY > 0 printf("sr%d: Split Clocking Selected.\n", portndx); #endif #if 1 SRC_PUT8(hc->sca_base, msci->rxs, 0); SRC_PUT8(hc->sca_base, msci->txs, 0); #else SRC_PUT8(hc->sca_base, msci->rxs, SCA_RXS_CLK_RXC0 | SCA_RXS_DIV1); /* * We need to configure the internal bit clock for the * transmitter's channel... */ SRC_PUT8(hc->sca_base, msci->txs, SCA_TXS_CLK_RX | SCA_TXS_DIV1); #endif 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: fecrp = (u_int *)(hc->sca_base + SR_FECR); mcr_v = *fecrp; etcndx = 2; break; case SR_CRD_N2: default: mcr_v = inb(hc->iobase + 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->sca_base, msci->tmc, tmc_v); SRC_PUT8(hc->sca_base, msci->trc0, fifo_v); SRC_PUT8(hc->sca_base, msci->rxs, SCA_RXS_CLK_INT + br_v); SRC_PUT8(hc->sca_base, msci->txs, SCA_TXS_CLK_INT + br_v); switch (hc->cardtype) { case SR_CRD_N2PCI: *fecrp = mcr_v; break; case SR_CRD_N2: default: outb(hc->iobase + 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->sca_base, msci->rxs, SCA_RXS_CLK_INT | SCA_RXS_DIV1); SRC_PUT8(hc->sca_base, msci->txs, SCA_TXS_CLK_INT | SCA_TXS_DIV1); SRC_PUT8(hc->sca_base, msci->tmc, 5); if (portndx == 0) switch (hc->cardtype) { case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= SR_FECR_ETC0; break; case SR_CRD_N2: default: mcr_v = inb(hc->iobase + SR_MCR); mcr_v |= SR_MCR_ETC0; outb(hc->iobase + SR_MCR, mcr_v); } else switch (hc->cardtype) { case SR_CRD_N2: mcr_v = inb(hc->iobase + SR_MCR); mcr_v |= SR_MCR_ETC1; outb(hc->iobase + SR_MCR, mcr_v); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= 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->sca_base, msci->ie0, 0); SRC_PUT8(hc->sca_base, msci->ie1, 0x0C); SRC_PUT8(hc->sca_base, msci->ie2, 0); SRC_PUT8(hc->sca_base, msci->fie, 0); SRC_PUT8(hc->sca_base, msci->sa0, 0); SRC_PUT8(hc->sca_base, msci->sa1, 0); SRC_PUT8(hc->sca_base, msci->idl, 0x7E); /* set flags value */ SRC_PUT8(hc->sca_base, msci->rrc, 0x0E); SRC_PUT8(hc->sca_base, msci->trc0, 0x10); SRC_PUT8(hc->sca_base, 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->iobase, 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 = (u_int) hc->mem_start - (sc->rxdesc & ~hc->winmsk); for (rxbuf = sc->rxstart; rxbuf < sc->rxend; rxbuf += SR_BUF_SIZ, rxd++) { /* * construct the circular chain... */ rxda = (u_int) & 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->sca_base, dmac->dsr, 0); /* Disable DMA transfer */ SRC_PUT8(hc->sca_base, dmac->dcr, SCA_DCR_ABRT); /* XXX maybe also SCA_DMR_CNTE */ SRC_PUT8(hc->sca_base, dmac->dmr, SCA_DMR_TMOD | SCA_DMR_NF); SRC_PUT16(hc->sca_base, 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->sca_base, dmac->cda, cda_v); SRC_PUT8(hc->sca_base, dmac->sarb, sarb_v); rxd = (sca_descriptor *)sc->rxstart; SRC_PUT16(hc->sca_base, dmac->eda, (u_short)((u_int) & rxd[sc->rxmax - 1] & 0xffff)); SRC_PUT8(hc->sca_base, dmac->dir, 0xF0); SRC_PUT8(hc->sca_base, 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->iobase, 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 = (u_int) hc->mem_start - (blkp->txdesc & ~hc->winmsk); x = 0; txbuf = blkp->txstart; for (; txbuf < blkp->txend; txbuf += SR_BUF_SIZ, txd++) { txda = (u_int) & 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 = (u_int)txd - (u_int)hc->mem_start; } SRC_PUT8(hc->sca_base, dmac->dsr, 0); /* Disable DMA */ SRC_PUT8(hc->sca_base, dmac->dcr, SCA_DCR_ABRT); SRC_PUT8(hc->sca_base, dmac->dmr, SCA_DMR_TMOD | SCA_DMR_NF); SRC_PUT8(hc->sca_base, 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->sca_base, 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->sca_base, 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->iobase, sc->rxdesc); SRC_SET_ON(hc->iobase); } 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->iobase, 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->iobase, 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->sca_base, 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->iobase, 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 %x, " "rxdesc %x, stat %x.\n", sc->unit, loopcnt, (u_int) cda, (u_int) 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 *)sc->rxdesc; rxdesc = &rxdesc[(sc->rxhind + sc->rxmax - 2) % sc->rxmax]; SRC_PUT16(hc->sca_base, hc->sca->dmac[DMAC_RXCH(sc->scachan)].eda, (u_short)((u_int)(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->iobase, sc->rxdesc); SRC_SET_ON(hc->iobase); /* 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 if (ifp->if_bpf) 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_queue_data(sc->hook, m, NULL); + 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 *)sc->rxdesc; rxndx = (sc->rxhind + sc->rxmax - 2) % sc->rxmax; rxdesc = &rxdesc[rxndx]; SRC_PUT16(hc->sca_base, hc->sca->dmac[DMAC_RXCH(sc->scachan)].eda, (u_short)((u_int)(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->sca_base, hc->sca->msci[sc->scachan].st3); got_cda = SRC_GET16(hc->sca_base, hc->sca->dmac[DMAC_RXCH(sc->scachan)].cda); got_eda = SRC_GET16(hc->sca_base, 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->iobase); } /* * 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->sca_base, dmac->dsr); SRC_PUT8(hc->sca_base, 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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, 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->sca_base, dmac->dsr); SRC_PUT8(hc->sca_base, 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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, dmac->eda)); if (hc->mempages) { SRC_SET_ON(hc->iobase); SRC_SET_MEM(hc->iobase, 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->iobase); } #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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, dmac->eda), dsr); /* * Make sure we eat as many as possible. * Then get the system running again. */ if (hc->mempages) SRC_SET_ON(hc->iobase); sr_eat_packet(sc, 0); #ifndef NETGRAPH sc->ifsppp.pp_if.if_ierrors++; #else /* NETGRAPH */ sc->ierrors[2]++; #endif /* NETGRAPH */ SRC_PUT8(hc->sca_base, sca->msci[mch].cmd, SCA_CMD_RXMSGREJ); SRC_PUT8(hc->sca_base, 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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, dmac->eda), SRC_GET8(hc->sca_base, dmac->dsr)); #endif if (hc->mempages) SRC_SET_OFF(hc->iobase); } /* * 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 /* * 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->sca_base, msci->st0); got_st1 = SRC_GET8(hc->sca_base, msci->st1); got_st2 = SRC_GET8(hc->sca_base, msci->st2); got_st3 = SRC_GET8(hc->sca_base, 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); } #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->sca_base, 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 *nodep) { 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 = node->private; /* * check if it's our friend the debug hook */ if (strcmp(name, NG_SR_HOOK_DEBUG) == 0) { hook->private = NULL; /* paranoid */ sc->debug_hook = hook; return (0); } /* * Check for raw mode hook. */ if (strcmp(name, NG_SR_HOOK_RAW) != 0) { return (EINVAL); } hook->private = 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, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook) { struct sr_softc * sc; int error = 0; sc = node->private; 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; MALLOC(*resp, struct ng_mesg *, resplen, M_NETGRAPH, M_NOWAIT | M_ZERO); if (*resp == NULL) { error = ENOMEM; break; } arg = (*resp)->data; /* * Put in the throughput information. */ 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.version = NG_VERSION; (*resp)->header.arglen = strlen(arg) + 1; (*resp)->header.token = msg->header.token; (*resp)->header.typecookie = NG_SR_COOKIE; (*resp)->header.cmd = msg->header.cmd; strncpy((*resp)->header.cmdstr, "status", NG_CMDSTRLEN); } break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } free(msg, M_NETGRAPH); return (error); } /* * get data from another node and transmit it to the correct channel */ static int ngsr_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { int s; int error = 0; struct sr_softc * sc = hook->node->private; struct ifqueue *xmitq_p; /* * data doesn't come in from just anywhere (e.g control hook) */ if ( hook->private == NULL) { error = ENETDOWN; goto bad; } /* * Now queue the data for when it can be sent */ if (meta && meta->priority > 0) { xmitq_p = (&sc->xmitq_hipri); } 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_DATA(m, 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_rmnode(node_p node) { struct sr_softc * sc = node->private; sr_down(sc); ng_cutlinks(node); node->flags &= ~NG_INVALID; /* bounce back to life */ return (0); } /* already linked */ static int ngsr_connect(hook_p hook) { + /* probably not at splnet, force outward queueing */ + hook->peer->flags |= HK_QUEUE; /* 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 = hook->node->private; int s; /* * If it's the data hook, then free resources etc. */ if (hook->private) { 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/sr/if_sr_isa.c =================================================================== --- head/sys/dev/sr/if_sr_isa.c (revision 69921) +++ head/sys/dev/sr/if_sr_isa.c (revision 69922) @@ -1,3359 +1,3363 @@ /* * Copyright (c) 1996 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. * * $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 "sr.h" #include "opt_netgraph.h" #ifdef NETGRAPH #include #endif /* NETGRAPH */ #ifndef NETGRAPH #include "sppp.h" #if NSPPP <= 0 #error Device 'sr' requires sppp. #endif #endif /* NETGRAPH */ #include #include #include #include #include #include #include #include #include #ifdef NETGRAPH #include #else /* NETGRAPH */ #include #include #endif /* NETGRAPH */ #include #include #include #include #ifdef NETGRAPH #include #include #endif /* NETGRAPH */ /* #define USE_MODEMCK */ #ifndef COMPAT_OLDISA #error "The sr device requires the old isa compatibility shims" #endif #ifndef BUGGY #define BUGGY 0 #endif #ifndef NETGRAPH #define PPP_HEADER_LEN 4 #endif /* NETGRAPH */ /* * These macros are used to hide the difference between the way the * ISA N2 cards and the PCI N2 cards access the Hitachi 64570 SCA. */ #define SRC_GET8(base,off) (*hc->src_get8)(base,(u_int)&off) #define SRC_GET16(base,off) (*hc->src_get16)(base,(u_int)&off) #define SRC_PUT8(base,off,d) (*hc->src_put8)(base,(u_int)&off,d) #define SRC_PUT16(base,off,d) (*hc->src_put16)(base,(u_int)&off,d) /* * These macros enable/disable the DPRAM and select the correct * DPRAM page. */ #define SRC_GET_WIN(addr) ((addr >> SRC_WIN_SHFT) & SR_PG_MSK) #define SRC_SET_ON(iobase) outb(iobase+SR_PCR, \ SR_PCR_MEM_WIN | inb(iobase+SR_PCR)) #define SRC_SET_MEM(iobase,win) outb(iobase+SR_PSR, SRC_GET_WIN(win) | \ (inb(iobase+SR_PSR) & ~SR_PG_MSK)) #define SRC_SET_OFF(iobase) outb(iobase+SR_PCR, \ ~SR_PCR_MEM_WIN & inb(iobase+SR_PCR)) /* * Define the hardware (card information) structure needed to keep * track of the device itself... There is only one per card. */ struct sr_hardc { struct sr_hardc *next; /* PCI card linkage */ struct sr_softc *sc; /* software channels */ int cunit; /* card w/in system */ u_short iobase; /* I/O Base Address */ int cardtype; int numports; /* # of ports on cd */ int mempages; u_int memsize; /* DPRAM size: bytes */ u_int winmsk; vm_offset_t sca_base; vm_offset_t mem_pstart; /* start of buffer */ caddr_t mem_start; /* start of DP RAM */ caddr_t mem_end; /* end of DP RAM */ caddr_t plx_base; sca_regs *sca; /* register array */ /* * We vectorize the following functions to allow re-use between the * ISA card's needs and those of the PCI card. */ void (*src_put8)(u_int base, u_int off, u_int val); void (*src_put16)(u_int base, u_int off, u_int val); u_int (*src_get8)(u_int base, u_int off); u_int (*src_get16)(u_int base, u_int off); }; static int next_sc_unit = 0; #ifndef NETGRAPH static int sr_watcher = 0; #endif /* NETGRAPH */ static struct sr_hardc sr_hardc[NSR]; static struct sr_hardc *sr_hardc_pci; /* * 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_NODELEN + 1]; /* 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 */ #if ( __FreeBSD__ >= 3 ) struct callout_handle handle; /* timeout(9) handle */ #endif 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 */ /* * List of valid interrupt numbers for the N2 ISA card. */ static int sr_irqtable[16] = { 0, /* 0 */ 0, /* 1 */ 0, /* 2 */ 1, /* 3 */ 1, /* 4 */ 1, /* 5 */ 0, /* 6 */ 1, /* 7 */ 0, /* 8 */ 0, /* 9 */ 1, /* 10 */ 1, /* 11 */ 1, /* 12 */ 0, /* 13 */ 0, /* 14 */ 1 /* 15 */ }; static int srprobe(struct isa_device *id); static int srattach_isa(struct isa_device *id); struct isa_driver srdriver = { INTR_TYPE_NET, srprobe, srattach_isa, "sr" }; COMPAT_ISA_DRIVER(sr, srdriver); /* * 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 struct sr_hardc *srattach_pci(int unit, vm_offset_t plx_vaddr, vm_offset_t sca_vaddr); void srintr_hc(struct sr_hardc *hc); static ointhand2_t srintr; static int srattach(struct sr_hardc *hc); 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 static void sr_modemck(void *x); #else static void sr_modemck(struct sr_softc *x); #endif /* NETGRAPH */ static u_int src_get8_io(u_int base, u_int off); static u_int src_get16_io(u_int base, u_int off); static void src_put8_io(u_int base, u_int off, u_int val); static void src_put16_io(u_int base, u_int off, u_int val); static u_int src_get8_mem(u_int base, u_int off); static u_int src_get16_mem(u_int base, u_int off); static void src_put8_mem(u_int base, u_int off, u_int val); static void src_put16_mem(u_int base, u_int off, u_int val); #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_rmnode; 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 = { NG_VERSION, NG_SR_NODE_TYPE, NULL, ngsr_constructor, ngsr_rcvmsg, ngsr_rmnode, ngsr_newhook, NULL, ngsr_connect, ngsr_rcvdata, ngsr_rcvdata, ngsr_disconnect, NULL }; static int ngsr_done_init = 0; #endif /* NETGRAPH */ /* * I/O for ISA N2 card(s) */ #define SRC_REG(iobase,y) ((((y) & 0xf) + (((y) & 0xf0) << 6) + \ (iobase)) | 0x8000) static u_int src_get8_io(u_int base, u_int off) { return inb(SRC_REG(base, off)); } static u_int src_get16_io(u_int base, u_int off) { return inw(SRC_REG(base, off)); } static void src_put8_io(u_int base, u_int off, u_int val) { outb(SRC_REG(base, off), val); } static void src_put16_io(u_int base, u_int off, u_int val) { outw(SRC_REG(base, off), val); } /* * I/O for PCI N2 card(s) */ #define SRC_PCI_SCA_REG(y) ((y & 2) ? ((y & 0xfd) + 0x100) : y) static u_int src_get8_mem(u_int base, u_int off) { return *((u_char *)(base + SRC_PCI_SCA_REG(off))); } static u_int src_get16_mem(u_int base, u_int off) { return *((u_short *)(base + SRC_PCI_SCA_REG(off))); } static void src_put8_mem(u_int base, u_int off, u_int val) { *((u_char *)(base + SRC_PCI_SCA_REG(off))) = (u_char)val; } static void src_put16_mem(u_int base, u_int off, u_int val) { *((u_short *)(base + SRC_PCI_SCA_REG(off))) = (u_short)val; } /* * Probe for an ISA card. If it is there, size its memory. Then get the * rest of its information and fill it in. */ static int srprobe(struct isa_device *id) { struct sr_hardc *hc = &sr_hardc[id->id_unit]; u_int pgs, i, tmp; u_short port; u_short *smem; u_char mar; sca_regs *sca = 0; /* * Now see if the card is realy there. */ hc->cardtype = SR_CRD_N2; /* * We have to fill these in early because the SRC_PUT* and SRC_GET* * macros use them. */ hc->src_get8 = src_get8_io; hc->src_get16 = src_get16_io; hc->src_put8 = src_put8_io; hc->src_put16 = src_put16_io; hc->sca = 0; port = id->id_iobase; hc->numports = NCHAN; /* assumed # of channels on the card */ if (id->id_flags & SR_FLAGS_NCHAN_MSK) hc->numports = id->id_flags & SR_FLAGS_NCHAN_MSK; outb(port + SR_PCR, 0); /* turn off the card */ /* * Next, we'll test the Base Address Register to retension of * data... ... seeing if we're *really* talking to an N2. */ for (i = 0; i < 0x100; i++) { outb(port + SR_BAR, i); inb(port + SR_PCR); tmp = inb(port + SR_BAR); if (tmp != i) { printf("sr%d: probe failed BAR %x, %x.\n", id->id_unit, i, tmp); return 0; } } /* * Now see if we can see the SCA. */ outb(port + SR_PCR, SR_PCR_SCARUN | inb(port + SR_PCR)); SRC_PUT8(port, sca->wcrl, 0); SRC_PUT8(port, sca->wcrm, 0); SRC_PUT8(port, sca->wcrh, 0); SRC_PUT8(port, sca->pcr, 0); SRC_PUT8(port, sca->msci[0].tmc, 0); inb(port); tmp = SRC_GET8(port, sca->msci[0].tmc); if (tmp != 0) { printf("sr%d: Error reading SCA 0, %x\n", id->id_unit, tmp); return 0; } SRC_PUT8(port, sca->msci[0].tmc, 0x5A); inb(port); tmp = SRC_GET8(port, sca->msci[0].tmc); if (tmp != 0x5A) { printf("sr%d: Error reading SCA 0x5A, %x\n", id->id_unit, tmp); return 0; } SRC_PUT16(port, sca->dmac[0].cda, 0); inb(port); tmp = SRC_GET16(port, sca->dmac[0].cda); if (tmp != 0) { printf("sr%d: Error reading SCA 0, %x\n", id->id_unit, tmp); return 0; } SRC_PUT16(port, sca->dmac[0].cda, 0x55AA); inb(port); tmp = SRC_GET16(port, sca->dmac[0].cda); if (tmp != 0x55AA) { printf("sr%d: Error reading SCA 0x55AA, %x\n", id->id_unit, tmp); return 0; } /* * OK, the board's interface registers seem to work. Now we'll see * if the Dual-Ported RAM is fully accessible... */ outb(port + SR_PCR, SR_PCR_EN_VPM | SR_PCR_ISA16); outb(port + SR_PSR, SR_PSR_WIN_16K); /* * Take the kernel "virtual" address supplied to us and convert * it to a "real" address. Then program the card to use that. */ mar = (kvtop(id->id_maddr) >> 16) & SR_PCR_16M_SEL; outb(port + SR_PCR, mar | inb(port + SR_PCR)); mar = kvtop(id->id_maddr) >> 12; outb(port + SR_BAR, mar); outb(port + SR_PCR, inb(port + SR_PCR) | SR_PCR_MEM_WIN); smem = (u_short *)id->id_maddr; /* DP RAM Address */ /* * Here we will perform the memory scan to size the device. * * This is done by marking each potential page with a magic number. * We then loop through the pages looking for that magic number. As * soon as we no longer see that magic number, we'll quit the scan, * knowing that no more memory is present. This provides the number * of pages present on the card. * * Note: We're sizing 16K memory granules. */ for (i = 0; i <= SR_PSR_PG_SEL; i++) { outb(port + SR_PSR, (inb(port + SR_PSR) & ~SR_PSR_PG_SEL) | i); *smem = 0xAA55; } for (i = 0; i <= SR_PSR_PG_SEL; i++) { outb(port + SR_PSR, (inb(port + SR_PSR) & ~SR_PSR_PG_SEL) | i); if (*smem != 0xAA55) { /* * If we have less than 64k of memory, give up. That * is 4 x 16k pages. */ if (i < 4) { printf("sr%d: Bad mem page %d, mem %x, %x.\n", id->id_unit, i, 0xAA55, *smem); return 0; } break; } *smem = i; } hc->mempages = i; hc->memsize = i * SRC_WIN_SIZ; hc->winmsk = SRC_WIN_MSK; pgs = i; /* final count of 16K pages */ /* * This next loop erases the contents of that page in DPRAM */ for (i = 0; i <= pgs; i++) { outb(port + SR_PSR, (inb(port + SR_PSR) & ~SR_PSR_PG_SEL) | i); bzero(smem, SRC_WIN_SIZ); } SRC_SET_OFF(port); /* * We have a card here, fill in what we can. */ id->id_msize = SRC_WIN_SIZ; hc->iobase = id->id_iobase; hc->sca_base = id->id_iobase; hc->mem_start = id->id_maddr; hc->mem_end = (id->id_maddr + id->id_msize) - 1; hc->mem_pstart = 0; hc->cunit = id->id_unit; /* * Do a little sanity check. */ if (sr_irqtable[ffs(id->id_irq) - 1] == 0) printf("sr%d: Warning: illegal interrupt %d chosen.\n", id->id_unit, ffs(id->id_irq) - 1); /* * Bogus card configuration */ if ((hc->numports > NCHAN) /* only 2 ports/card */ ||(hc->memsize > (512 * 1024))) /* no more than 256K */ return 0; return SRC_IO_SIZ; /* return the amount of IO addresses used. */ } /* * srattach_isa and srattach_pci allocate memory for hardc, softc and * data buffers. It also does any initialization that is bus specific. * At the end they call the common srattach() function. */ static int srattach_isa(struct isa_device *id) { u_char mar; struct sr_hardc *hc = &sr_hardc[id->id_unit]; /* * Allocate the software interface table(s) */ MALLOC(hc->sc, struct sr_softc *, hc->numports * sizeof(struct sr_softc), M_DEVBUF, M_WAITOK | M_ZERO); if (hc->sc == NULL) return(0); id->id_ointr = srintr; outb(hc->iobase + SR_PCR, inb(hc->iobase + SR_PCR) | SR_PCR_SCARUN); outb(hc->iobase + SR_PSR, inb(hc->iobase + SR_PSR) | SR_PSR_EN_SCA_DMA); outb(hc->iobase + SR_MCR, SR_MCR_DTR0 | SR_MCR_DTR1 | SR_MCR_TE0 | SR_MCR_TE1); SRC_SET_ON(hc->iobase); /* * Configure the card. Mem address, irq, */ mar = (kvtop(id->id_maddr) >> 16) & SR_PCR_16M_SEL; outb(hc->iobase + SR_PCR, mar | (inb(hc->iobase + SR_PCR) & ~SR_PCR_16M_SEL)); mar = kvtop(id->id_maddr) >> 12; outb(hc->iobase + SR_BAR, mar); /* * Get the TX clock direction and configuration. The default is a * single external clock which is used by RX and TX. */ #ifdef N2_TEST_SPEED if (sr_test_speed[0] > 0) hc->sc[0].clk_cfg = SR_FLAGS_INT_CLK; else if (id->id_flags & SR_FLAGS_0_CLK_MSK) hc->sc[0].clk_cfg = (id->id_flags & SR_FLAGS_0_CLK_MSK) >> SR_FLAGS_CLK_SHFT; #else if (id->id_flags & SR_FLAGS_0_CLK_MSK) hc->sc[0].clk_cfg = (id->id_flags & SR_FLAGS_0_CLK_MSK) >> SR_FLAGS_CLK_SHFT; #endif 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 (id->id_flags & SR_FLAGS_1_CLK_MSK) hc->sc[1].clk_cfg = (id->id_flags & SR_FLAGS_1_CLK_MSK) >> (SR_FLAGS_CLK_SHFT + SR_FLAGS_CLK_CHAN_SHFT); return srattach(hc); } struct sr_hardc * srattach_pci(int unit, vm_offset_t plx_vaddr, vm_offset_t sca_vaddr) { int numports, pndx; u_int fecr, *fecrp = (u_int *)(sca_vaddr + SR_FECR); struct sr_hardc *hc, **hcp; /* * Configure the PLX. This is magic. I'm doing it just like I'm told * to. :-) * * offset * 0x00 - Map Range - Mem-mapped to locate anywhere * 0x04 - Re-Map - PCI address decode enable * 0x18 - Bus Region - 32-bit bus, ready enable * 0x1c - Master Range - include all 16 MB * 0x20 - Master RAM - Map SCA Base at 0 * 0x28 - Master Remap - direct master memory enable * 0x68 - Interrupt - Enable interrupt (0 to disable) * * Note: This is "cargo cult" stuff. - jrc */ *((u_int *)(plx_vaddr + 0x00)) = 0xfffff000; *((u_int *)(plx_vaddr + 0x04)) = 1; *((u_int *)(plx_vaddr + 0x18)) = 0x40030043; *((u_int *)(plx_vaddr + 0x1c)) = 0xff000000; *((u_int *)(plx_vaddr + 0x20)) = 0; *((u_int *)(plx_vaddr + 0x28)) = 0xe9; *((u_int *)(plx_vaddr + 0x68)) = 0x10900; /* * Get info from card. * * Only look for the second port if the first exists. Too many things * will break if we have only a second port. */ fecr = *fecrp; numports = 0; if (((fecr & SR_FECR_ID0) >> SR_FE_ID0_SHFT) != SR_FE_ID_NONE) { numports++; if (((fecr & SR_FECR_ID1) >> SR_FE_ID1_SHFT) != SR_FE_ID_NONE) numports++; } if (numports == 0) return NULL; hc = sr_hardc_pci; hcp = &sr_hardc_pci; while (hc) { hcp = &hc->next; hc = hc->next; } MALLOC(hc, struct sr_hardc *, sizeof(*hc), M_DEVBUF, M_WAITOK | M_ZERO); if (hc == NULL) return NULL; MALLOC(hc->sc, struct sr_softc *, numports * sizeof(struct sr_softc), M_DEVBUF, M_WAITOK | M_ZERO); if (hc->sc == NULL) { FREE(hc, M_DEVBUF); return NULL; } *hcp = hc; hc->numports = numports; hc->cunit = unit; hc->cardtype = SR_CRD_N2PCI; hc->plx_base = (caddr_t)plx_vaddr; hc->sca_base = sca_vaddr; hc->src_put8 = src_put8_mem; hc->src_put16 = src_put16_mem; hc->src_get8 = src_get8_mem; hc->src_get16 = src_get16_mem; /* * Malloc area for tx and rx buffers. For now allocate SRC_WIN_SIZ * (16k) for each buffer. * * Allocate the block below 16M because the N2pci card can only access * 16M memory at a time. * * (We could actually allocate a contiguous block above the 16MB limit, * but this would complicate card programming more than we want to * right now -jrc) */ hc->memsize = 2 * hc->numports * SRC_WIN_SIZ; hc->mem_start = contigmalloc(hc->memsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 0x10000, 0x1000000); if (hc->mem_start == NULL) { printf("src%d: pci: failed to allocate buffer space.\n", unit); return NULL; } hc->winmsk = 0xffffffff; hc->mem_end = (caddr_t)((u_int)hc->mem_start + hc->memsize); hc->mem_pstart = kvtop(hc->mem_start); bzero(hc->mem_start, hc->memsize); for (pndx = 0; pndx < numports; pndx++) { int intf_sw; struct sr_softc *sc; sc = &hc->sc[pndx]; 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; } } *fecrp = SR_FECR_DTR0 | SR_FECR_DTR1 | SR_FECR_TE0 | SR_FECR_TE1; srattach(hc); return hc; } /* * Register the ports on the adapter. * Fill in the info for each port. #ifndef NETGRAPH * Attach each port to sppp and bpf. #endif */ static int srattach(struct sr_hardc *hc) { struct sr_softc *sc = hc->sc; #ifndef NETGRAPH struct ifnet *ifp; #endif /* NETGRAPH */ int unit; /* index: channel w/in card */ /* * Report Card configuration information before we start configuring * each channel on the card... */ printf("src%d: %uK RAM (%d mempages) @ %08x-%08x, %u ports.\n", hc->cunit, hc->memsize / 1024, hc->mempages, (u_int)hc->mem_start, (u_int)hc->mem_end, hc->numports); src_init(hc); sr_init_sca(hc); /* * 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; ifp->if_unit = sc->unit; ifp->if_name = "sr"; 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) return (0); sc->node->private = 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", MTX_DEF); mtx_init(&sc->xmitq_hipri.ifq_mtx, "sr_xmitq_hipri", MTX_DEF); sprintf(sc->nodename, "%s%d", NG_SR_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { ng_rmnode(sc->node); ng_unref(sc->node); return (0); } sc->running = 0; #endif /* NETGRAPH */ } if (hc->mempages) SRC_SET_OFF(hc->iobase); return 1; } /* * 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(int unit) { struct sr_hardc *hc; hc = &sr_hardc[unit]; srintr_hc(hc); return; } void srintr_hc(struct sr_hardc *hc) { 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_base, sca->isr0); isr1 = SRC_GET8(hc->sca_base, sca->isr1); isr2 = SRC_GET8(hc->sca_base, 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->sca_base, dmac->cda, cda_value); SRC_PUT16(hc->sca_base, dmac->eda, eda_value); /* * Now we'll let the DMA status register know about this change */ SRC_PUT8(hc->sca_base, 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->iobase); SRC_SET_MEM(hc->iobase, 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->iobase); #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->iobase); 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 if (ifp->if_bpf) 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 *)blkp->txdesc; blkp->txeda = (u_short)((u_int)&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 printf("sr%d: srioctl(ifp=%08x, cmd=%08x, data=%08x)\n", ifp->if_unit, ifp, cmd, data); #endif was_up = ifp->if_flags & IFF_RUNNING; error = sppp_ioctl(ifp, cmd, data); #if BUGGY > 1 printf("sr%d: ioctl: ifsppp.pp_flags = %08x, if_flags %08x.\n", ifp->if_unit, ((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->sca_base, msci->st0); got_st1 = SRC_GET8(hc->sca_base, msci->st1); got_st3 = SRC_GET8(hc->sca_base, msci->st3); got_dsr = SRC_GET8(hc->sca_base, 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->sca_base, msci->st1) & SCA_ST1_UDRN) { SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_TXABORT); SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_TXENABLE); SRC_PUT8(hc->sca_base, 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) { u_int *fecrp; 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->sca_base, msci->ctl, SRC_GET8(hc->sca_base, msci->ctl) & ~SCA_CTL_RTS); if (sc->scachan == 0) switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) & ~SR_MCR_DTR0)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp &= ~SR_FECR_DTR0; break; } else switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) & ~SR_MCR_DTR1)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp &= ~SR_FECR_DTR1; break; } if (sc->scachan == 0) { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) | 0x000F); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, sca->ier1) | 0x000F); } else { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) | 0x00F0); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, sca->ier1) | 0x00F0); } SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_RXENABLE); inb(hc->iobase); /* XXX slow it down a bit. */ SRC_PUT8(hc->sca_base, 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) { u_int *fecrp; 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->sca_base, msci->cmd, SCA_CMD_RXDISABLE); inb(hc->iobase); /* XXX slow it down a bit. */ SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_TXDISABLE); SRC_PUT8(hc->sca_base, msci->ctl, SRC_GET8(hc->sca_base, msci->ctl) | SCA_CTL_RTS); if (sc->scachan == 0) switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) | SR_MCR_DTR0)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= SR_FECR_DTR0; break; } else switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) | SR_MCR_DTR1)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= SR_FECR_DTR1; break; } if (sc->scachan == 0) { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) & ~0x0F); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, sca->ier1) & ~0x0F); } else { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) & ~0xF0); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, 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_base, sca->pabr0, 0); SRC_PUT8(hc->sca_base, sca->pabr1, 0); SRC_PUT8(hc->sca_base, sca->wcrl, 0); SRC_PUT8(hc->sca_base, sca->wcrm, 0); SRC_PUT8(hc->sca_base, sca->wcrh, 0); /* * Configure the interrupt registers. Most are cleared until the * interface is configured. */ SRC_PUT8(hc->sca_base, sca->ier0, 0x00); /* MSCI interrupts. */ SRC_PUT8(hc->sca_base, sca->ier1, 0x00); /* DMAC interrupts */ SRC_PUT8(hc->sca_base, sca->ier2, 0x00); /* TIMER interrupts. */ SRC_PUT8(hc->sca_base, sca->itcr, 0x00); /* Use ivr and no intr * ack */ SRC_PUT8(hc->sca_base, sca->ivr, 0x40); /* Interrupt vector. */ SRC_PUT8(hc->sca_base, 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_base, sca->pcr, SCA_PCR_PR2); SRC_PUT8(hc->sca_base, 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 */ u_int *fecrp; /* pointer for PCI's MCR i/o */ 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->sca_base, msci->cmd, SCA_CMD_RESET); SRC_PUT8(hc->sca_base, msci->md0, SCA_MD0_CRC_1 | SCA_MD0_CRC_CCITT | SCA_MD0_CRC_ENABLE | SCA_MD0_MODE_HDLC); SRC_PUT8(hc->sca_base, msci->md1, SCA_MD1_NOADDRCHK); SRC_PUT8(hc->sca_base, 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->sca_base, msci->cmd, SCA_CMD_RXRESET); SRC_PUT8(hc->sca_base, 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->sca_base, msci->rxs, 0); SRC_PUT8(hc->sca_base, msci->txs, 0); break; case SR_FLAGS_EXT_SEP_CLK: #if BUGGY > 0 printf("sr%d: Split Clocking Selected.\n", portndx); #endif #if 1 SRC_PUT8(hc->sca_base, msci->rxs, 0); SRC_PUT8(hc->sca_base, msci->txs, 0); #else SRC_PUT8(hc->sca_base, msci->rxs, SCA_RXS_CLK_RXC0 | SCA_RXS_DIV1); /* * We need to configure the internal bit clock for the * transmitter's channel... */ SRC_PUT8(hc->sca_base, msci->txs, SCA_TXS_CLK_RX | SCA_TXS_DIV1); #endif 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: fecrp = (u_int *)(hc->sca_base + SR_FECR); mcr_v = *fecrp; etcndx = 2; break; case SR_CRD_N2: default: mcr_v = inb(hc->iobase + 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->sca_base, msci->tmc, tmc_v); SRC_PUT8(hc->sca_base, msci->trc0, fifo_v); SRC_PUT8(hc->sca_base, msci->rxs, SCA_RXS_CLK_INT + br_v); SRC_PUT8(hc->sca_base, msci->txs, SCA_TXS_CLK_INT + br_v); switch (hc->cardtype) { case SR_CRD_N2PCI: *fecrp = mcr_v; break; case SR_CRD_N2: default: outb(hc->iobase + 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->sca_base, msci->rxs, SCA_RXS_CLK_INT | SCA_RXS_DIV1); SRC_PUT8(hc->sca_base, msci->txs, SCA_TXS_CLK_INT | SCA_TXS_DIV1); SRC_PUT8(hc->sca_base, msci->tmc, 5); if (portndx == 0) switch (hc->cardtype) { case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= SR_FECR_ETC0; break; case SR_CRD_N2: default: mcr_v = inb(hc->iobase + SR_MCR); mcr_v |= SR_MCR_ETC0; outb(hc->iobase + SR_MCR, mcr_v); } else switch (hc->cardtype) { case SR_CRD_N2: mcr_v = inb(hc->iobase + SR_MCR); mcr_v |= SR_MCR_ETC1; outb(hc->iobase + SR_MCR, mcr_v); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= 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->sca_base, msci->ie0, 0); SRC_PUT8(hc->sca_base, msci->ie1, 0x0C); SRC_PUT8(hc->sca_base, msci->ie2, 0); SRC_PUT8(hc->sca_base, msci->fie, 0); SRC_PUT8(hc->sca_base, msci->sa0, 0); SRC_PUT8(hc->sca_base, msci->sa1, 0); SRC_PUT8(hc->sca_base, msci->idl, 0x7E); /* set flags value */ SRC_PUT8(hc->sca_base, msci->rrc, 0x0E); SRC_PUT8(hc->sca_base, msci->trc0, 0x10); SRC_PUT8(hc->sca_base, 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->iobase, 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 = (u_int) hc->mem_start - (sc->rxdesc & ~hc->winmsk); for (rxbuf = sc->rxstart; rxbuf < sc->rxend; rxbuf += SR_BUF_SIZ, rxd++) { /* * construct the circular chain... */ rxda = (u_int) & 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->sca_base, dmac->dsr, 0); /* Disable DMA transfer */ SRC_PUT8(hc->sca_base, dmac->dcr, SCA_DCR_ABRT); /* XXX maybe also SCA_DMR_CNTE */ SRC_PUT8(hc->sca_base, dmac->dmr, SCA_DMR_TMOD | SCA_DMR_NF); SRC_PUT16(hc->sca_base, 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->sca_base, dmac->cda, cda_v); SRC_PUT8(hc->sca_base, dmac->sarb, sarb_v); rxd = (sca_descriptor *)sc->rxstart; SRC_PUT16(hc->sca_base, dmac->eda, (u_short)((u_int) & rxd[sc->rxmax - 1] & 0xffff)); SRC_PUT8(hc->sca_base, dmac->dir, 0xF0); SRC_PUT8(hc->sca_base, 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->iobase, 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 = (u_int) hc->mem_start - (blkp->txdesc & ~hc->winmsk); x = 0; txbuf = blkp->txstart; for (; txbuf < blkp->txend; txbuf += SR_BUF_SIZ, txd++) { txda = (u_int) & 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 = (u_int)txd - (u_int)hc->mem_start; } SRC_PUT8(hc->sca_base, dmac->dsr, 0); /* Disable DMA */ SRC_PUT8(hc->sca_base, dmac->dcr, SCA_DCR_ABRT); SRC_PUT8(hc->sca_base, dmac->dmr, SCA_DMR_TMOD | SCA_DMR_NF); SRC_PUT8(hc->sca_base, 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->sca_base, 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->sca_base, 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->iobase, sc->rxdesc); SRC_SET_ON(hc->iobase); } 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->iobase, 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->iobase, 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->sca_base, 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->iobase, 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 %x, " "rxdesc %x, stat %x.\n", sc->unit, loopcnt, (u_int) cda, (u_int) 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 *)sc->rxdesc; rxdesc = &rxdesc[(sc->rxhind + sc->rxmax - 2) % sc->rxmax]; SRC_PUT16(hc->sca_base, hc->sca->dmac[DMAC_RXCH(sc->scachan)].eda, (u_short)((u_int)(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->iobase, sc->rxdesc); SRC_SET_ON(hc->iobase); /* 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 if (ifp->if_bpf) 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_queue_data(sc->hook, m, NULL); + 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 *)sc->rxdesc; rxndx = (sc->rxhind + sc->rxmax - 2) % sc->rxmax; rxdesc = &rxdesc[rxndx]; SRC_PUT16(hc->sca_base, hc->sca->dmac[DMAC_RXCH(sc->scachan)].eda, (u_short)((u_int)(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->sca_base, hc->sca->msci[sc->scachan].st3); got_cda = SRC_GET16(hc->sca_base, hc->sca->dmac[DMAC_RXCH(sc->scachan)].cda); got_eda = SRC_GET16(hc->sca_base, 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->iobase); } /* * 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->sca_base, dmac->dsr); SRC_PUT8(hc->sca_base, 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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, 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->sca_base, dmac->dsr); SRC_PUT8(hc->sca_base, 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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, dmac->eda)); if (hc->mempages) { SRC_SET_ON(hc->iobase); SRC_SET_MEM(hc->iobase, 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->iobase); } #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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, dmac->eda), dsr); /* * Make sure we eat as many as possible. * Then get the system running again. */ if (hc->mempages) SRC_SET_ON(hc->iobase); sr_eat_packet(sc, 0); #ifndef NETGRAPH sc->ifsppp.pp_if.if_ierrors++; #else /* NETGRAPH */ sc->ierrors[2]++; #endif /* NETGRAPH */ SRC_PUT8(hc->sca_base, sca->msci[mch].cmd, SCA_CMD_RXMSGREJ); SRC_PUT8(hc->sca_base, 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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, dmac->eda), SRC_GET8(hc->sca_base, dmac->dsr)); #endif if (hc->mempages) SRC_SET_OFF(hc->iobase); } /* * 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 /* * 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->sca_base, msci->st0); got_st1 = SRC_GET8(hc->sca_base, msci->st1); got_st2 = SRC_GET8(hc->sca_base, msci->st2); got_st3 = SRC_GET8(hc->sca_base, 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); } #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->sca_base, 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 *nodep) { 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 = node->private; /* * check if it's our friend the debug hook */ if (strcmp(name, NG_SR_HOOK_DEBUG) == 0) { hook->private = NULL; /* paranoid */ sc->debug_hook = hook; return (0); } /* * Check for raw mode hook. */ if (strcmp(name, NG_SR_HOOK_RAW) != 0) { return (EINVAL); } hook->private = 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, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook) { struct sr_softc * sc; int error = 0; sc = node->private; 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; MALLOC(*resp, struct ng_mesg *, resplen, M_NETGRAPH, M_NOWAIT | M_ZERO); if (*resp == NULL) { error = ENOMEM; break; } arg = (*resp)->data; /* * Put in the throughput information. */ 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.version = NG_VERSION; (*resp)->header.arglen = strlen(arg) + 1; (*resp)->header.token = msg->header.token; (*resp)->header.typecookie = NG_SR_COOKIE; (*resp)->header.cmd = msg->header.cmd; strncpy((*resp)->header.cmdstr, "status", NG_CMDSTRLEN); } break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } free(msg, M_NETGRAPH); return (error); } /* * get data from another node and transmit it to the correct channel */ static int ngsr_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { int s; int error = 0; struct sr_softc * sc = hook->node->private; struct ifqueue *xmitq_p; /* * data doesn't come in from just anywhere (e.g control hook) */ if ( hook->private == NULL) { error = ENETDOWN; goto bad; } /* * Now queue the data for when it can be sent */ if (meta && meta->priority > 0) { xmitq_p = (&sc->xmitq_hipri); } 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_DATA(m, 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_rmnode(node_p node) { struct sr_softc * sc = node->private; sr_down(sc); ng_cutlinks(node); node->flags &= ~NG_INVALID; /* bounce back to life */ return (0); } /* already linked */ static int ngsr_connect(hook_p hook) { + /* probably not at splnet, force outward queueing */ + hook->peer->flags |= HK_QUEUE; /* 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 = hook->node->private; int s; /* * If it's the data hook, then free resources etc. */ if (hook->private) { 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 69921) +++ head/sys/dev/usb/udbp.c (revision 69922) @@ -1,815 +1,815 @@ /* * 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. * * $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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef UDBP_DEBUG #define DPRINTF(x) if (udbpdebug) logprintf x #define DPRINTFN(n,x) if (udbpdebug>(n)) logprintf x int udbpdebug = 9; #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_info ng_udbp_stat_type_info = NG_UDBP_STATS_TYPE_INFO; Static const struct ng_parse_type ng_udbp_stat_type = { &ng_parse_struct_type, &ng_udbp_stat_type_info }; /* 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 = { NG_VERSION, NG_UDBP_NODE_TYPE, NULL, ng_udbp_constructor, ng_udbp_rcvmsg, ng_udbp_rmnode, ng_udbp_newhook, NULL, ng_udbp_connect, ng_udbp_rcvdata, - ng_udbp_rcvdata, ng_udbp_disconnect, 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]; sc->node->private = sc; sc->xmitq.ifq_maxlen = IFQ_MAXLEN; sc->xmitq_hipri.ifq_maxlen = IFQ_MAXLEN; mtx_init(&sc->xmitq.ifq_mtx, "usb_xmitq", MTX_DEF); mtx_init(&sc->xmitq_hipri.ifq_mtx, "usb_xmitq_hipri", MTX_DEF); sprintf(nodename, "%s", USBDEVNAME(sc->sc_dev)); if ((err = ng_name_node(sc->node, nodename))) { ng_rmnode(sc->node); ng_unref(sc->node); } else { /* something to note it's done */ } } if (err) { goto bad; } 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_unname(sc->node); ng_udbp_rmnode(sc->node); sc->node->private = NULL; ng_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; - meta_p meta = NULL; 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_DATAQ(err, sc->hook, m, meta); + 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); /*********************************************************************** * 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 *nodep) { 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 = node->private; #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) { /* do something specific to a debug connection */ sc->hook = hook; hook->private = 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, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { const udbp_p sc = node->private; struct ng_mesg *resp = NULL; int error = 0; /* 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 */ if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); /* Free the message and return */ FREE(msg, M_NETGRAPH); return(error); } /* * Accept data from the hook and queue it for output. */ Static int ng_udbp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const udbp_p sc = hook->node->private; int error; struct ifqueue *xmitq_p; int s; /* * Now queue the data for when it can be sent */ if (meta && meta->priority > 0) { xmitq_p = (&sc->xmitq_hipri); } 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_DATA(m, 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 = node->private; struct mbuf *m; node->flags |= NG_INVALID; ng_cutlinks(node); /* Drain the queues */ IF_DRAIN(&sc->xmitq_hipri); IF_DRAIN(&sc->xmitq); sc->packets_in = 0; /* reset stats */ sc->packets_out = 0; node->flags &= ~NG_INVALID; /* reset invalid flag */ return (0); } /* * 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 */ + hook->peer->flags |= HK_QUEUE; /* 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 = hook->node->private; sc->hook = NULL; if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } Index: head/sys/i386/isa/if_ar.c =================================================================== --- head/sys/i386/isa/if_ar.c (revision 69921) +++ head/sys/i386/isa/if_ar.c (revision 69922) @@ -1,2403 +1,2407 @@ /* * Copyright (c) 1995, 1999 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. * * $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 "ar.h" #include #include #include #include #include #include #include #include #include #ifdef NETGRAPH #include #include #include #include #else /* NETGRAPH */ #include #include #endif /* NETGRAPH */ #include #include #include #include #ifndef COMPAT_OLDISA #error "The ar device requires the old isa compatibility shims" #endif #ifndef NETGRAPH #include "sppp.h" #if NSPPP <= 0 #error device 'ar' require sppp. #endif /* NSPPP <= 0 */ #endif /* NETGRAPH */ #ifdef TRACE #define TRC(x) x #else #define TRC(x) #endif #define TRCL(x) x #define PPP_HEADER_LEN 4 #define ARC_GET_WIN(addr) ((addr >> ARC_WIN_SHFT) & AR_WIN_MSK) #define ARC_SET_MEM(iobase,win) outb(iobase+AR_MSCA_EN, AR_ENA_MEM | \ ARC_GET_WIN(win)) #define ARC_SET_SCA(iobase,ch) outb(iobase+AR_MSCA_EN, AR_ENA_MEM | \ AR_ENA_SCA | (ch ? AR_SEL_SCA_1:AR_SEL_SCA_0)) #define ARC_SET_OFF(iobase) outb(iobase+AR_MSCA_EN, 0) struct ar_hardc { int cunit; struct ar_softc *sc; u_short iobase; int isa_irq; int numports; caddr_t mem_start; caddr_t mem_end; u_char *orbase; u_int memsize; /* in bytes */ u_int winsize; /* in bytes */ u_int winmsk; u_char bustype; /* ISA, MCA, PCI.... */ u_char interface[NPORT];/* X21, V.35, EIA-530.... */ u_char revision; u_char handshake; /* handshake lines supported by card. */ u_char txc_dtr[NPORT/NCHAN]; /* the register is write only */ u_int txc_dtr_off[NPORT/NCHAN]; sca_regs *sca[NPORT/NCHAN]; }; static int next_ar_unit = 0; static struct ar_hardc ar_hardc[NAR]; 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_NODELEN + 1]; /* 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 */ static int arprobe(struct isa_device *id); static int arattach_isa(struct isa_device *id); /* * 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 */ }; struct isa_driver ardriver = { INTR_TYPE_NET, arprobe, arattach_isa, "ar" }; COMPAT_ISA_DRIVER(ar, ardriver); struct ar_hardc *arattach_pci(int unit, vm_offset_t mem_addr); void arintr_hc(struct ar_hardc *hc); static ointhand2_t arintr; static int arattach(struct ar_hardc *hc); 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_rmnode; 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 = { NG_VERSION, NG_AR_NODE_TYPE, NULL, ngar_constructor, ngar_rcvmsg, ngar_rmnode, ngar_newhook, NULL, ngar_connect, ngar_rcvdata, - ngar_rcvdata, ngar_disconnect, NULL }; static int ngar_done_init = 0; #endif /* NETGRAPH */ /* * Register the Adapter. * Probe to see if it is there. * Get its information and fill it in. */ static int arprobe(struct isa_device *id) { struct ar_hardc *hc = &ar_hardc[id->id_unit]; u_int tmp; u_short port; /* * Register the card. */ /* * Now see if the card is realy there. * * XXX For now I just check the undocumented ports * for "570". We will probably have to do more checking. */ port = id->id_iobase; if((inb(port+AR_ID_5) != '5') || (inb(port+AR_ID_7) != '7') || (inb(port+AR_ID_0) != '0')) return 0; /* * We have a card here, fill in what we can. */ tmp = inb(port + AR_BMI); hc->bustype = tmp & AR_BUS_MSK; hc->memsize = (tmp & AR_MEM_MSK) >> AR_MEM_SHFT; hc->memsize = 1 << hc->memsize; hc->memsize <<= 16; hc->interface[0] = (tmp & AR_IFACE_MSK); hc->interface[1] = hc->interface[0]; hc->interface[2] = hc->interface[0]; hc->interface[3] = hc->interface[0]; tmp = inb(port + AR_REV); hc->revision = tmp & AR_REV_MSK; hc->winsize = 1 << ((tmp & AR_WSIZ_MSK) >> AR_WSIZ_SHFT); hc->winsize *= ARC_WIN_SIZ; hc->winmsk = hc->winsize - 1; hc->numports = inb(port + AR_PNUM); hc->handshake = inb(port + AR_HNDSH); id->id_msize = hc->winsize; hc->iobase = id->id_iobase; hc->mem_start = id->id_maddr; hc->mem_end = id->id_maddr + id->id_msize; hc->cunit = id->id_unit; hc->isa_irq = id->id_irq; switch(hc->interface[0]) { case AR_IFACE_EIA_232: printf("ar%d: The EIA 232 interface is not supported.\n", id->id_unit); return 0; case AR_IFACE_V_35: break; case AR_IFACE_EIA_530: printf("ar%d: WARNING: The EIA 530 interface is untested.\n", id->id_unit); break; case AR_IFACE_X_21: break; case AR_IFACE_COMBO: printf("ar%d: WARNING: The COMBO interface is untested.\n", id->id_unit); break; } /* * Do a little sanity check. */ if((hc->numports > NPORT) || (hc->memsize > (512*1024))) return 0; return ARC_IO_SIZ; /* return the amount of IO addresses used. */ } /* * Malloc memory for the softc structures. * Reset the card to put it in a known state. * Register the ports on the adapter. * Fill in the info for each port. * Attach each port to sppp and bpf. */ static int arattach_isa(struct isa_device *id) { struct ar_hardc *hc = &ar_hardc[id->id_unit]; id->id_ointr = arintr; return arattach(hc); } struct ar_hardc * arattach_pci(int unit, vm_offset_t mem_addr) { struct ar_hardc *hc; u_int i, tmp; hc = malloc(sizeof(struct ar_hardc), M_DEVBUF, M_WAITOK | M_ZERO); hc->cunit = unit; hc->mem_start = (caddr_t)mem_addr; hc->sca[0] = (sca_regs *)(mem_addr + AR_PCI_SCA_1_OFFSET); hc->sca[1] = (sca_regs *)(mem_addr + AR_PCI_SCA_2_OFFSET); hc->iobase = 0; hc->orbase = (u_char *)(mem_addr + AR_PCI_ORBASE_OFFSET); tmp = hc->orbase[AR_BMI * 4]; hc->bustype = tmp & AR_BUS_MSK; hc->memsize = (tmp & AR_MEM_MSK) >> AR_MEM_SHFT; hc->memsize = 1 << hc->memsize; hc->memsize <<= 16; hc->interface[0] = (tmp & AR_IFACE_MSK); tmp = hc->orbase[AR_REV * 4]; hc->revision = tmp & AR_REV_MSK; hc->winsize = (1 << ((tmp & AR_WSIZ_MSK) >> AR_WSIZ_SHFT)) * 16 * 1024; hc->mem_end = (caddr_t)(mem_addr + hc->winsize); hc->winmsk = hc->winsize - 1; hc->numports = hc->orbase[AR_PNUM * 4]; hc->handshake = hc->orbase[AR_HNDSH * 4]; for(i = 1; i < hc->numports; i++) hc->interface[i] = hc->interface[0]; TRC(printf("arp%d: bus %x, rev %d, memstart %p, winsize %d, " "winmsk %x, interface %x\n", unit, hc->bustype, hc->revision, hc->mem_start, hc->winsize, hc->winmsk, hc->interface[0])); arattach(hc); return hc; } static int arattach(struct ar_hardc *hc) { struct ar_softc *sc; #ifndef NETGRAPH struct ifnet *ifp; char *iface; #endif /* NETGRAPH */ int unit; printf("arc%d: %uK RAM, %u ports, rev %u.\n", hc->cunit, hc->memsize/1024, hc->numports, hc->revision); arc_init(hc); 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; ifp->if_unit = sc->unit; ifp->if_name = "ar"; 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 (0); sc->node->private = 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", MTX_DEF); mtx_init(&sc->xmitq_hipri.ifq_mtx, "ar_xmitq_hipri", MTX_DEF); sprintf(sc->nodename, "%s%d", NG_AR_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { ng_rmnode(sc->node); ng_unref(sc->node); return (0); } sc->running = 0; #endif /* NETGRAPH */ } if(hc->bustype == AR_BUS_ISA) ARC_SET_OFF(hc->iobase); return 1; } /* * 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(int unit) { struct ar_hardc *hc; hc = &ar_hardc[unit]; arintr_hc(hc); return; } void arintr_hc(struct ar_hardc *hc) { 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 = inb(hc->iobase + 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->iobase, 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 = inb(hc->iobase + AR_ISTAT); } } if(hc->bustype == AR_BUS_ISA) ARC_SET_OFF(hc->iobase); } /* * 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->iobase, 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->iobase); } /* * 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->iobase, 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 if(ifp->if_bpf) 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->iobase); 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(printf("ar%d: arioctl.\n", ifp->if_unit);) was_up = ifp->if_flags & IFF_RUNNING; error = sppp_ioctl(ifp, cmd, data); TRC(printf("ar%d: ioctl: ifsppp.pp_flags = %x, if_flags %x.\n", ifp->if_unit, ((struct sppp *)ifp)->pp_flags, ifp->if_flags);) if(error) return error; if((cmd != SIOCSIFFLAGS) && cmd != (SIOCSIFADDR)) return 0; TRC(printf("ar%d: arioctl %s.\n", ifp->if_unit, (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->iobase, 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->iobase, 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 outb(sc->hc->iobase + 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) inb(sc->hc->iobase + 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->iobase); #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->iobase, sc->scano); msci->cmd = SCA_CMD_RXDISABLE; if(sc->hc->bustype == AR_BUS_ISA) inb(sc->hc->iobase + 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 outb(sc->hc->iobase + 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->iobase); } 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; 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 outb(hc->iobase + 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 outb(hc->iobase + AR_TXC_DTR0, hc->txc_dtr[0]); if(hc->bustype == AR_BUS_ISA) { /* * Configure the card. * Mem address, irq, */ mar = kvtop(hc->mem_start) >> 16; isr = irqtable[ffs(hc->isa_irq) - 1] << 1; if(isr == 0) printf("ar%d: Warning illegal interrupt %d\n", hc->cunit, ffs(hc->isa_irq) - 1); isr = isr | ((kvtop(hc->mem_start) & 0xc000) >> 10); hc->sca[0] = (sca_regs *)hc->mem_start; hc->sca[1] = (sca_regs *)hc->mem_start; outb(hc->iobase + AR_MEM_SEL, mar); outb(hc->iobase + 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 outb(hc->iobase + 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 outb(hc->iobase + 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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 if(sc->ifsppp.pp_if.if_bpf) bpf_mtap(&sc->ifsppp.pp_if, m); sppp_input(&sc->ifsppp.pp_if, m); sc->ifsppp.pp_if.if_ipackets++; #else /* NETGRAPH */ - ng_queue_data(sc->hook, m, NULL); + 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->iobase, 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->iobase, 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->iobase, 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->iobase, 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); TRC( #ifndef NETGRAPH if(tt == sc->ifsppp.pp_if.if_ipackets) { #else /* NETGRAPH */ if(tt == sc->ipackets) { #endif /* NETGRAPH */ sca_descriptor *rxdesc; int i; if(hc->bustype == AR_BUS_ISA) ARC_SET_SCA(hc->iobase, 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->iobase, 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->iobase, 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->iobase, 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 *nodep) { 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 = node->private; /* * check if it's our friend the debug hook */ if (strcmp(name, NG_AR_HOOK_DEBUG) == 0) { hook->private = NULL; /* paranoid */ sc->debug_hook = hook; return (0); } /* * Check for raw mode hook. */ if (strcmp(name, NG_AR_HOOK_RAW) != 0) { return (EINVAL); } hook->private = 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, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook) { struct ar_softc * sc; int error = 0; sc = node->private; 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; MALLOC(*resp, struct ng_mesg *, resplen, M_NETGRAPH, M_NOWAIT | M_ZERO); if (*resp == NULL) { error = ENOMEM; break; } arg = (*resp)->data; /* * Put in the throughput information. */ 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.version = NG_VERSION; (*resp)->header.arglen = strlen(arg) + 1; (*resp)->header.token = msg->header.token; (*resp)->header.typecookie = NG_AR_COOKIE; (*resp)->header.cmd = msg->header.cmd; strncpy((*resp)->header.cmdstr, "status", NG_CMDSTRLEN); } break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } free(msg, M_NETGRAPH); return (error); } /* * get data from another node and transmit it to the correct channel */ static int ngar_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { int s; int error = 0; struct ar_softc * sc = hook->node->private; struct ifqueue *xmitq_p; /* * data doesn't come in from just anywhere (e.g control hook) */ if ( hook->private == NULL) { error = ENETDOWN; goto bad; } /* * Now queue the data for when it can be sent */ if (meta && meta->priority > 0) { xmitq_p = (&sc->xmitq_hipri); } 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_DATA(m, 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_rmnode(node_p node) { struct ar_softc * sc = node->private; ar_down(sc); ng_cutlinks(node); node->flags &= ~NG_INVALID; /* bounce back to life */ return (0); } /* already linked */ static int ngar_connect(hook_p hook) { + /* probably not at splnet, force outward queueing */ + hook->peer->flags |= HK_QUEUE; /* 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 = hook->node->private; int s; /* * If it's the data hook, then free resources etc. */ if (hook->private) { 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/i386/isa/if_sr.c =================================================================== --- head/sys/i386/isa/if_sr.c (revision 69921) +++ head/sys/i386/isa/if_sr.c (revision 69922) @@ -1,3359 +1,3363 @@ /* * Copyright (c) 1996 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. * * $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 "sr.h" #include "opt_netgraph.h" #ifdef NETGRAPH #include #endif /* NETGRAPH */ #ifndef NETGRAPH #include "sppp.h" #if NSPPP <= 0 #error Device 'sr' requires sppp. #endif #endif /* NETGRAPH */ #include #include #include #include #include #include #include #include #include #ifdef NETGRAPH #include #else /* NETGRAPH */ #include #include #endif /* NETGRAPH */ #include #include #include #include #ifdef NETGRAPH #include #include #endif /* NETGRAPH */ /* #define USE_MODEMCK */ #ifndef COMPAT_OLDISA #error "The sr device requires the old isa compatibility shims" #endif #ifndef BUGGY #define BUGGY 0 #endif #ifndef NETGRAPH #define PPP_HEADER_LEN 4 #endif /* NETGRAPH */ /* * These macros are used to hide the difference between the way the * ISA N2 cards and the PCI N2 cards access the Hitachi 64570 SCA. */ #define SRC_GET8(base,off) (*hc->src_get8)(base,(u_int)&off) #define SRC_GET16(base,off) (*hc->src_get16)(base,(u_int)&off) #define SRC_PUT8(base,off,d) (*hc->src_put8)(base,(u_int)&off,d) #define SRC_PUT16(base,off,d) (*hc->src_put16)(base,(u_int)&off,d) /* * These macros enable/disable the DPRAM and select the correct * DPRAM page. */ #define SRC_GET_WIN(addr) ((addr >> SRC_WIN_SHFT) & SR_PG_MSK) #define SRC_SET_ON(iobase) outb(iobase+SR_PCR, \ SR_PCR_MEM_WIN | inb(iobase+SR_PCR)) #define SRC_SET_MEM(iobase,win) outb(iobase+SR_PSR, SRC_GET_WIN(win) | \ (inb(iobase+SR_PSR) & ~SR_PG_MSK)) #define SRC_SET_OFF(iobase) outb(iobase+SR_PCR, \ ~SR_PCR_MEM_WIN & inb(iobase+SR_PCR)) /* * Define the hardware (card information) structure needed to keep * track of the device itself... There is only one per card. */ struct sr_hardc { struct sr_hardc *next; /* PCI card linkage */ struct sr_softc *sc; /* software channels */ int cunit; /* card w/in system */ u_short iobase; /* I/O Base Address */ int cardtype; int numports; /* # of ports on cd */ int mempages; u_int memsize; /* DPRAM size: bytes */ u_int winmsk; vm_offset_t sca_base; vm_offset_t mem_pstart; /* start of buffer */ caddr_t mem_start; /* start of DP RAM */ caddr_t mem_end; /* end of DP RAM */ caddr_t plx_base; sca_regs *sca; /* register array */ /* * We vectorize the following functions to allow re-use between the * ISA card's needs and those of the PCI card. */ void (*src_put8)(u_int base, u_int off, u_int val); void (*src_put16)(u_int base, u_int off, u_int val); u_int (*src_get8)(u_int base, u_int off); u_int (*src_get16)(u_int base, u_int off); }; static int next_sc_unit = 0; #ifndef NETGRAPH static int sr_watcher = 0; #endif /* NETGRAPH */ static struct sr_hardc sr_hardc[NSR]; static struct sr_hardc *sr_hardc_pci; /* * 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_NODELEN + 1]; /* 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 */ #if ( __FreeBSD__ >= 3 ) struct callout_handle handle; /* timeout(9) handle */ #endif 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 */ /* * List of valid interrupt numbers for the N2 ISA card. */ static int sr_irqtable[16] = { 0, /* 0 */ 0, /* 1 */ 0, /* 2 */ 1, /* 3 */ 1, /* 4 */ 1, /* 5 */ 0, /* 6 */ 1, /* 7 */ 0, /* 8 */ 0, /* 9 */ 1, /* 10 */ 1, /* 11 */ 1, /* 12 */ 0, /* 13 */ 0, /* 14 */ 1 /* 15 */ }; static int srprobe(struct isa_device *id); static int srattach_isa(struct isa_device *id); struct isa_driver srdriver = { INTR_TYPE_NET, srprobe, srattach_isa, "sr" }; COMPAT_ISA_DRIVER(sr, srdriver); /* * 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 struct sr_hardc *srattach_pci(int unit, vm_offset_t plx_vaddr, vm_offset_t sca_vaddr); void srintr_hc(struct sr_hardc *hc); static ointhand2_t srintr; static int srattach(struct sr_hardc *hc); 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 static void sr_modemck(void *x); #else static void sr_modemck(struct sr_softc *x); #endif /* NETGRAPH */ static u_int src_get8_io(u_int base, u_int off); static u_int src_get16_io(u_int base, u_int off); static void src_put8_io(u_int base, u_int off, u_int val); static void src_put16_io(u_int base, u_int off, u_int val); static u_int src_get8_mem(u_int base, u_int off); static u_int src_get16_mem(u_int base, u_int off); static void src_put8_mem(u_int base, u_int off, u_int val); static void src_put16_mem(u_int base, u_int off, u_int val); #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_rmnode; 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 = { NG_VERSION, NG_SR_NODE_TYPE, NULL, ngsr_constructor, ngsr_rcvmsg, ngsr_rmnode, ngsr_newhook, NULL, ngsr_connect, ngsr_rcvdata, ngsr_rcvdata, ngsr_disconnect, NULL }; static int ngsr_done_init = 0; #endif /* NETGRAPH */ /* * I/O for ISA N2 card(s) */ #define SRC_REG(iobase,y) ((((y) & 0xf) + (((y) & 0xf0) << 6) + \ (iobase)) | 0x8000) static u_int src_get8_io(u_int base, u_int off) { return inb(SRC_REG(base, off)); } static u_int src_get16_io(u_int base, u_int off) { return inw(SRC_REG(base, off)); } static void src_put8_io(u_int base, u_int off, u_int val) { outb(SRC_REG(base, off), val); } static void src_put16_io(u_int base, u_int off, u_int val) { outw(SRC_REG(base, off), val); } /* * I/O for PCI N2 card(s) */ #define SRC_PCI_SCA_REG(y) ((y & 2) ? ((y & 0xfd) + 0x100) : y) static u_int src_get8_mem(u_int base, u_int off) { return *((u_char *)(base + SRC_PCI_SCA_REG(off))); } static u_int src_get16_mem(u_int base, u_int off) { return *((u_short *)(base + SRC_PCI_SCA_REG(off))); } static void src_put8_mem(u_int base, u_int off, u_int val) { *((u_char *)(base + SRC_PCI_SCA_REG(off))) = (u_char)val; } static void src_put16_mem(u_int base, u_int off, u_int val) { *((u_short *)(base + SRC_PCI_SCA_REG(off))) = (u_short)val; } /* * Probe for an ISA card. If it is there, size its memory. Then get the * rest of its information and fill it in. */ static int srprobe(struct isa_device *id) { struct sr_hardc *hc = &sr_hardc[id->id_unit]; u_int pgs, i, tmp; u_short port; u_short *smem; u_char mar; sca_regs *sca = 0; /* * Now see if the card is realy there. */ hc->cardtype = SR_CRD_N2; /* * We have to fill these in early because the SRC_PUT* and SRC_GET* * macros use them. */ hc->src_get8 = src_get8_io; hc->src_get16 = src_get16_io; hc->src_put8 = src_put8_io; hc->src_put16 = src_put16_io; hc->sca = 0; port = id->id_iobase; hc->numports = NCHAN; /* assumed # of channels on the card */ if (id->id_flags & SR_FLAGS_NCHAN_MSK) hc->numports = id->id_flags & SR_FLAGS_NCHAN_MSK; outb(port + SR_PCR, 0); /* turn off the card */ /* * Next, we'll test the Base Address Register to retension of * data... ... seeing if we're *really* talking to an N2. */ for (i = 0; i < 0x100; i++) { outb(port + SR_BAR, i); inb(port + SR_PCR); tmp = inb(port + SR_BAR); if (tmp != i) { printf("sr%d: probe failed BAR %x, %x.\n", id->id_unit, i, tmp); return 0; } } /* * Now see if we can see the SCA. */ outb(port + SR_PCR, SR_PCR_SCARUN | inb(port + SR_PCR)); SRC_PUT8(port, sca->wcrl, 0); SRC_PUT8(port, sca->wcrm, 0); SRC_PUT8(port, sca->wcrh, 0); SRC_PUT8(port, sca->pcr, 0); SRC_PUT8(port, sca->msci[0].tmc, 0); inb(port); tmp = SRC_GET8(port, sca->msci[0].tmc); if (tmp != 0) { printf("sr%d: Error reading SCA 0, %x\n", id->id_unit, tmp); return 0; } SRC_PUT8(port, sca->msci[0].tmc, 0x5A); inb(port); tmp = SRC_GET8(port, sca->msci[0].tmc); if (tmp != 0x5A) { printf("sr%d: Error reading SCA 0x5A, %x\n", id->id_unit, tmp); return 0; } SRC_PUT16(port, sca->dmac[0].cda, 0); inb(port); tmp = SRC_GET16(port, sca->dmac[0].cda); if (tmp != 0) { printf("sr%d: Error reading SCA 0, %x\n", id->id_unit, tmp); return 0; } SRC_PUT16(port, sca->dmac[0].cda, 0x55AA); inb(port); tmp = SRC_GET16(port, sca->dmac[0].cda); if (tmp != 0x55AA) { printf("sr%d: Error reading SCA 0x55AA, %x\n", id->id_unit, tmp); return 0; } /* * OK, the board's interface registers seem to work. Now we'll see * if the Dual-Ported RAM is fully accessible... */ outb(port + SR_PCR, SR_PCR_EN_VPM | SR_PCR_ISA16); outb(port + SR_PSR, SR_PSR_WIN_16K); /* * Take the kernel "virtual" address supplied to us and convert * it to a "real" address. Then program the card to use that. */ mar = (kvtop(id->id_maddr) >> 16) & SR_PCR_16M_SEL; outb(port + SR_PCR, mar | inb(port + SR_PCR)); mar = kvtop(id->id_maddr) >> 12; outb(port + SR_BAR, mar); outb(port + SR_PCR, inb(port + SR_PCR) | SR_PCR_MEM_WIN); smem = (u_short *)id->id_maddr; /* DP RAM Address */ /* * Here we will perform the memory scan to size the device. * * This is done by marking each potential page with a magic number. * We then loop through the pages looking for that magic number. As * soon as we no longer see that magic number, we'll quit the scan, * knowing that no more memory is present. This provides the number * of pages present on the card. * * Note: We're sizing 16K memory granules. */ for (i = 0; i <= SR_PSR_PG_SEL; i++) { outb(port + SR_PSR, (inb(port + SR_PSR) & ~SR_PSR_PG_SEL) | i); *smem = 0xAA55; } for (i = 0; i <= SR_PSR_PG_SEL; i++) { outb(port + SR_PSR, (inb(port + SR_PSR) & ~SR_PSR_PG_SEL) | i); if (*smem != 0xAA55) { /* * If we have less than 64k of memory, give up. That * is 4 x 16k pages. */ if (i < 4) { printf("sr%d: Bad mem page %d, mem %x, %x.\n", id->id_unit, i, 0xAA55, *smem); return 0; } break; } *smem = i; } hc->mempages = i; hc->memsize = i * SRC_WIN_SIZ; hc->winmsk = SRC_WIN_MSK; pgs = i; /* final count of 16K pages */ /* * This next loop erases the contents of that page in DPRAM */ for (i = 0; i <= pgs; i++) { outb(port + SR_PSR, (inb(port + SR_PSR) & ~SR_PSR_PG_SEL) | i); bzero(smem, SRC_WIN_SIZ); } SRC_SET_OFF(port); /* * We have a card here, fill in what we can. */ id->id_msize = SRC_WIN_SIZ; hc->iobase = id->id_iobase; hc->sca_base = id->id_iobase; hc->mem_start = id->id_maddr; hc->mem_end = (id->id_maddr + id->id_msize) - 1; hc->mem_pstart = 0; hc->cunit = id->id_unit; /* * Do a little sanity check. */ if (sr_irqtable[ffs(id->id_irq) - 1] == 0) printf("sr%d: Warning: illegal interrupt %d chosen.\n", id->id_unit, ffs(id->id_irq) - 1); /* * Bogus card configuration */ if ((hc->numports > NCHAN) /* only 2 ports/card */ ||(hc->memsize > (512 * 1024))) /* no more than 256K */ return 0; return SRC_IO_SIZ; /* return the amount of IO addresses used. */ } /* * srattach_isa and srattach_pci allocate memory for hardc, softc and * data buffers. It also does any initialization that is bus specific. * At the end they call the common srattach() function. */ static int srattach_isa(struct isa_device *id) { u_char mar; struct sr_hardc *hc = &sr_hardc[id->id_unit]; /* * Allocate the software interface table(s) */ MALLOC(hc->sc, struct sr_softc *, hc->numports * sizeof(struct sr_softc), M_DEVBUF, M_WAITOK | M_ZERO); if (hc->sc == NULL) return(0); id->id_ointr = srintr; outb(hc->iobase + SR_PCR, inb(hc->iobase + SR_PCR) | SR_PCR_SCARUN); outb(hc->iobase + SR_PSR, inb(hc->iobase + SR_PSR) | SR_PSR_EN_SCA_DMA); outb(hc->iobase + SR_MCR, SR_MCR_DTR0 | SR_MCR_DTR1 | SR_MCR_TE0 | SR_MCR_TE1); SRC_SET_ON(hc->iobase); /* * Configure the card. Mem address, irq, */ mar = (kvtop(id->id_maddr) >> 16) & SR_PCR_16M_SEL; outb(hc->iobase + SR_PCR, mar | (inb(hc->iobase + SR_PCR) & ~SR_PCR_16M_SEL)); mar = kvtop(id->id_maddr) >> 12; outb(hc->iobase + SR_BAR, mar); /* * Get the TX clock direction and configuration. The default is a * single external clock which is used by RX and TX. */ #ifdef N2_TEST_SPEED if (sr_test_speed[0] > 0) hc->sc[0].clk_cfg = SR_FLAGS_INT_CLK; else if (id->id_flags & SR_FLAGS_0_CLK_MSK) hc->sc[0].clk_cfg = (id->id_flags & SR_FLAGS_0_CLK_MSK) >> SR_FLAGS_CLK_SHFT; #else if (id->id_flags & SR_FLAGS_0_CLK_MSK) hc->sc[0].clk_cfg = (id->id_flags & SR_FLAGS_0_CLK_MSK) >> SR_FLAGS_CLK_SHFT; #endif 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 (id->id_flags & SR_FLAGS_1_CLK_MSK) hc->sc[1].clk_cfg = (id->id_flags & SR_FLAGS_1_CLK_MSK) >> (SR_FLAGS_CLK_SHFT + SR_FLAGS_CLK_CHAN_SHFT); return srattach(hc); } struct sr_hardc * srattach_pci(int unit, vm_offset_t plx_vaddr, vm_offset_t sca_vaddr) { int numports, pndx; u_int fecr, *fecrp = (u_int *)(sca_vaddr + SR_FECR); struct sr_hardc *hc, **hcp; /* * Configure the PLX. This is magic. I'm doing it just like I'm told * to. :-) * * offset * 0x00 - Map Range - Mem-mapped to locate anywhere * 0x04 - Re-Map - PCI address decode enable * 0x18 - Bus Region - 32-bit bus, ready enable * 0x1c - Master Range - include all 16 MB * 0x20 - Master RAM - Map SCA Base at 0 * 0x28 - Master Remap - direct master memory enable * 0x68 - Interrupt - Enable interrupt (0 to disable) * * Note: This is "cargo cult" stuff. - jrc */ *((u_int *)(plx_vaddr + 0x00)) = 0xfffff000; *((u_int *)(plx_vaddr + 0x04)) = 1; *((u_int *)(plx_vaddr + 0x18)) = 0x40030043; *((u_int *)(plx_vaddr + 0x1c)) = 0xff000000; *((u_int *)(plx_vaddr + 0x20)) = 0; *((u_int *)(plx_vaddr + 0x28)) = 0xe9; *((u_int *)(plx_vaddr + 0x68)) = 0x10900; /* * Get info from card. * * Only look for the second port if the first exists. Too many things * will break if we have only a second port. */ fecr = *fecrp; numports = 0; if (((fecr & SR_FECR_ID0) >> SR_FE_ID0_SHFT) != SR_FE_ID_NONE) { numports++; if (((fecr & SR_FECR_ID1) >> SR_FE_ID1_SHFT) != SR_FE_ID_NONE) numports++; } if (numports == 0) return NULL; hc = sr_hardc_pci; hcp = &sr_hardc_pci; while (hc) { hcp = &hc->next; hc = hc->next; } MALLOC(hc, struct sr_hardc *, sizeof(*hc), M_DEVBUF, M_WAITOK | M_ZERO); if (hc == NULL) return NULL; MALLOC(hc->sc, struct sr_softc *, numports * sizeof(struct sr_softc), M_DEVBUF, M_WAITOK | M_ZERO); if (hc->sc == NULL) { FREE(hc, M_DEVBUF); return NULL; } *hcp = hc; hc->numports = numports; hc->cunit = unit; hc->cardtype = SR_CRD_N2PCI; hc->plx_base = (caddr_t)plx_vaddr; hc->sca_base = sca_vaddr; hc->src_put8 = src_put8_mem; hc->src_put16 = src_put16_mem; hc->src_get8 = src_get8_mem; hc->src_get16 = src_get16_mem; /* * Malloc area for tx and rx buffers. For now allocate SRC_WIN_SIZ * (16k) for each buffer. * * Allocate the block below 16M because the N2pci card can only access * 16M memory at a time. * * (We could actually allocate a contiguous block above the 16MB limit, * but this would complicate card programming more than we want to * right now -jrc) */ hc->memsize = 2 * hc->numports * SRC_WIN_SIZ; hc->mem_start = contigmalloc(hc->memsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 0x10000, 0x1000000); if (hc->mem_start == NULL) { printf("src%d: pci: failed to allocate buffer space.\n", unit); return NULL; } hc->winmsk = 0xffffffff; hc->mem_end = (caddr_t)((u_int)hc->mem_start + hc->memsize); hc->mem_pstart = kvtop(hc->mem_start); bzero(hc->mem_start, hc->memsize); for (pndx = 0; pndx < numports; pndx++) { int intf_sw; struct sr_softc *sc; sc = &hc->sc[pndx]; 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; } } *fecrp = SR_FECR_DTR0 | SR_FECR_DTR1 | SR_FECR_TE0 | SR_FECR_TE1; srattach(hc); return hc; } /* * Register the ports on the adapter. * Fill in the info for each port. #ifndef NETGRAPH * Attach each port to sppp and bpf. #endif */ static int srattach(struct sr_hardc *hc) { struct sr_softc *sc = hc->sc; #ifndef NETGRAPH struct ifnet *ifp; #endif /* NETGRAPH */ int unit; /* index: channel w/in card */ /* * Report Card configuration information before we start configuring * each channel on the card... */ printf("src%d: %uK RAM (%d mempages) @ %08x-%08x, %u ports.\n", hc->cunit, hc->memsize / 1024, hc->mempages, (u_int)hc->mem_start, (u_int)hc->mem_end, hc->numports); src_init(hc); sr_init_sca(hc); /* * 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; ifp->if_unit = sc->unit; ifp->if_name = "sr"; 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) return (0); sc->node->private = 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", MTX_DEF); mtx_init(&sc->xmitq_hipri.ifq_mtx, "sr_xmitq_hipri", MTX_DEF); sprintf(sc->nodename, "%s%d", NG_SR_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { ng_rmnode(sc->node); ng_unref(sc->node); return (0); } sc->running = 0; #endif /* NETGRAPH */ } if (hc->mempages) SRC_SET_OFF(hc->iobase); return 1; } /* * 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(int unit) { struct sr_hardc *hc; hc = &sr_hardc[unit]; srintr_hc(hc); return; } void srintr_hc(struct sr_hardc *hc) { 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_base, sca->isr0); isr1 = SRC_GET8(hc->sca_base, sca->isr1); isr2 = SRC_GET8(hc->sca_base, 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->sca_base, dmac->cda, cda_value); SRC_PUT16(hc->sca_base, dmac->eda, eda_value); /* * Now we'll let the DMA status register know about this change */ SRC_PUT8(hc->sca_base, 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->iobase); SRC_SET_MEM(hc->iobase, 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->iobase); #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->iobase); 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 if (ifp->if_bpf) 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 *)blkp->txdesc; blkp->txeda = (u_short)((u_int)&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 printf("sr%d: srioctl(ifp=%08x, cmd=%08x, data=%08x)\n", ifp->if_unit, ifp, cmd, data); #endif was_up = ifp->if_flags & IFF_RUNNING; error = sppp_ioctl(ifp, cmd, data); #if BUGGY > 1 printf("sr%d: ioctl: ifsppp.pp_flags = %08x, if_flags %08x.\n", ifp->if_unit, ((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->sca_base, msci->st0); got_st1 = SRC_GET8(hc->sca_base, msci->st1); got_st3 = SRC_GET8(hc->sca_base, msci->st3); got_dsr = SRC_GET8(hc->sca_base, 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->sca_base, msci->st1) & SCA_ST1_UDRN) { SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_TXABORT); SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_TXENABLE); SRC_PUT8(hc->sca_base, 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) { u_int *fecrp; 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->sca_base, msci->ctl, SRC_GET8(hc->sca_base, msci->ctl) & ~SCA_CTL_RTS); if (sc->scachan == 0) switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) & ~SR_MCR_DTR0)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp &= ~SR_FECR_DTR0; break; } else switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) & ~SR_MCR_DTR1)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp &= ~SR_FECR_DTR1; break; } if (sc->scachan == 0) { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) | 0x000F); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, sca->ier1) | 0x000F); } else { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) | 0x00F0); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, sca->ier1) | 0x00F0); } SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_RXENABLE); inb(hc->iobase); /* XXX slow it down a bit. */ SRC_PUT8(hc->sca_base, 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) { u_int *fecrp; 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->sca_base, msci->cmd, SCA_CMD_RXDISABLE); inb(hc->iobase); /* XXX slow it down a bit. */ SRC_PUT8(hc->sca_base, msci->cmd, SCA_CMD_TXDISABLE); SRC_PUT8(hc->sca_base, msci->ctl, SRC_GET8(hc->sca_base, msci->ctl) | SCA_CTL_RTS); if (sc->scachan == 0) switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) | SR_MCR_DTR0)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= SR_FECR_DTR0; break; } else switch (hc->cardtype) { case SR_CRD_N2: outb(hc->iobase + SR_MCR, (inb(hc->iobase + SR_MCR) | SR_MCR_DTR1)); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= SR_FECR_DTR1; break; } if (sc->scachan == 0) { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) & ~0x0F); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, sca->ier1) & ~0x0F); } else { SRC_PUT8(hc->sca_base, sca->ier0, SRC_GET8(hc->sca_base, sca->ier0) & ~0xF0); SRC_PUT8(hc->sca_base, sca->ier1, SRC_GET8(hc->sca_base, 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_base, sca->pabr0, 0); SRC_PUT8(hc->sca_base, sca->pabr1, 0); SRC_PUT8(hc->sca_base, sca->wcrl, 0); SRC_PUT8(hc->sca_base, sca->wcrm, 0); SRC_PUT8(hc->sca_base, sca->wcrh, 0); /* * Configure the interrupt registers. Most are cleared until the * interface is configured. */ SRC_PUT8(hc->sca_base, sca->ier0, 0x00); /* MSCI interrupts. */ SRC_PUT8(hc->sca_base, sca->ier1, 0x00); /* DMAC interrupts */ SRC_PUT8(hc->sca_base, sca->ier2, 0x00); /* TIMER interrupts. */ SRC_PUT8(hc->sca_base, sca->itcr, 0x00); /* Use ivr and no intr * ack */ SRC_PUT8(hc->sca_base, sca->ivr, 0x40); /* Interrupt vector. */ SRC_PUT8(hc->sca_base, 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_base, sca->pcr, SCA_PCR_PR2); SRC_PUT8(hc->sca_base, 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 */ u_int *fecrp; /* pointer for PCI's MCR i/o */ 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->sca_base, msci->cmd, SCA_CMD_RESET); SRC_PUT8(hc->sca_base, msci->md0, SCA_MD0_CRC_1 | SCA_MD0_CRC_CCITT | SCA_MD0_CRC_ENABLE | SCA_MD0_MODE_HDLC); SRC_PUT8(hc->sca_base, msci->md1, SCA_MD1_NOADDRCHK); SRC_PUT8(hc->sca_base, 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->sca_base, msci->cmd, SCA_CMD_RXRESET); SRC_PUT8(hc->sca_base, 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->sca_base, msci->rxs, 0); SRC_PUT8(hc->sca_base, msci->txs, 0); break; case SR_FLAGS_EXT_SEP_CLK: #if BUGGY > 0 printf("sr%d: Split Clocking Selected.\n", portndx); #endif #if 1 SRC_PUT8(hc->sca_base, msci->rxs, 0); SRC_PUT8(hc->sca_base, msci->txs, 0); #else SRC_PUT8(hc->sca_base, msci->rxs, SCA_RXS_CLK_RXC0 | SCA_RXS_DIV1); /* * We need to configure the internal bit clock for the * transmitter's channel... */ SRC_PUT8(hc->sca_base, msci->txs, SCA_TXS_CLK_RX | SCA_TXS_DIV1); #endif 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: fecrp = (u_int *)(hc->sca_base + SR_FECR); mcr_v = *fecrp; etcndx = 2; break; case SR_CRD_N2: default: mcr_v = inb(hc->iobase + 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->sca_base, msci->tmc, tmc_v); SRC_PUT8(hc->sca_base, msci->trc0, fifo_v); SRC_PUT8(hc->sca_base, msci->rxs, SCA_RXS_CLK_INT + br_v); SRC_PUT8(hc->sca_base, msci->txs, SCA_TXS_CLK_INT + br_v); switch (hc->cardtype) { case SR_CRD_N2PCI: *fecrp = mcr_v; break; case SR_CRD_N2: default: outb(hc->iobase + 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->sca_base, msci->rxs, SCA_RXS_CLK_INT | SCA_RXS_DIV1); SRC_PUT8(hc->sca_base, msci->txs, SCA_TXS_CLK_INT | SCA_TXS_DIV1); SRC_PUT8(hc->sca_base, msci->tmc, 5); if (portndx == 0) switch (hc->cardtype) { case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= SR_FECR_ETC0; break; case SR_CRD_N2: default: mcr_v = inb(hc->iobase + SR_MCR); mcr_v |= SR_MCR_ETC0; outb(hc->iobase + SR_MCR, mcr_v); } else switch (hc->cardtype) { case SR_CRD_N2: mcr_v = inb(hc->iobase + SR_MCR); mcr_v |= SR_MCR_ETC1; outb(hc->iobase + SR_MCR, mcr_v); break; case SR_CRD_N2PCI: fecrp = (u_int *)(hc->sca_base + SR_FECR); *fecrp |= 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->sca_base, msci->ie0, 0); SRC_PUT8(hc->sca_base, msci->ie1, 0x0C); SRC_PUT8(hc->sca_base, msci->ie2, 0); SRC_PUT8(hc->sca_base, msci->fie, 0); SRC_PUT8(hc->sca_base, msci->sa0, 0); SRC_PUT8(hc->sca_base, msci->sa1, 0); SRC_PUT8(hc->sca_base, msci->idl, 0x7E); /* set flags value */ SRC_PUT8(hc->sca_base, msci->rrc, 0x0E); SRC_PUT8(hc->sca_base, msci->trc0, 0x10); SRC_PUT8(hc->sca_base, 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->iobase, 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 = (u_int) hc->mem_start - (sc->rxdesc & ~hc->winmsk); for (rxbuf = sc->rxstart; rxbuf < sc->rxend; rxbuf += SR_BUF_SIZ, rxd++) { /* * construct the circular chain... */ rxda = (u_int) & 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->sca_base, dmac->dsr, 0); /* Disable DMA transfer */ SRC_PUT8(hc->sca_base, dmac->dcr, SCA_DCR_ABRT); /* XXX maybe also SCA_DMR_CNTE */ SRC_PUT8(hc->sca_base, dmac->dmr, SCA_DMR_TMOD | SCA_DMR_NF); SRC_PUT16(hc->sca_base, 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->sca_base, dmac->cda, cda_v); SRC_PUT8(hc->sca_base, dmac->sarb, sarb_v); rxd = (sca_descriptor *)sc->rxstart; SRC_PUT16(hc->sca_base, dmac->eda, (u_short)((u_int) & rxd[sc->rxmax - 1] & 0xffff)); SRC_PUT8(hc->sca_base, dmac->dir, 0xF0); SRC_PUT8(hc->sca_base, 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->iobase, 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 = (u_int) hc->mem_start - (blkp->txdesc & ~hc->winmsk); x = 0; txbuf = blkp->txstart; for (; txbuf < blkp->txend; txbuf += SR_BUF_SIZ, txd++) { txda = (u_int) & 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 = (u_int)txd - (u_int)hc->mem_start; } SRC_PUT8(hc->sca_base, dmac->dsr, 0); /* Disable DMA */ SRC_PUT8(hc->sca_base, dmac->dcr, SCA_DCR_ABRT); SRC_PUT8(hc->sca_base, dmac->dmr, SCA_DMR_TMOD | SCA_DMR_NF); SRC_PUT8(hc->sca_base, 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->sca_base, 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->sca_base, 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->iobase, sc->rxdesc); SRC_SET_ON(hc->iobase); } 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->iobase, 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->iobase, 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->sca_base, 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->iobase, 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 %x, " "rxdesc %x, stat %x.\n", sc->unit, loopcnt, (u_int) cda, (u_int) 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 *)sc->rxdesc; rxdesc = &rxdesc[(sc->rxhind + sc->rxmax - 2) % sc->rxmax]; SRC_PUT16(hc->sca_base, hc->sca->dmac[DMAC_RXCH(sc->scachan)].eda, (u_short)((u_int)(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->iobase, sc->rxdesc); SRC_SET_ON(hc->iobase); /* 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 if (ifp->if_bpf) 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_queue_data(sc->hook, m, NULL); + 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 *)sc->rxdesc; rxndx = (sc->rxhind + sc->rxmax - 2) % sc->rxmax; rxdesc = &rxdesc[rxndx]; SRC_PUT16(hc->sca_base, hc->sca->dmac[DMAC_RXCH(sc->scachan)].eda, (u_short)((u_int)(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->sca_base, hc->sca->msci[sc->scachan].st3); got_cda = SRC_GET16(hc->sca_base, hc->sca->dmac[DMAC_RXCH(sc->scachan)].cda); got_eda = SRC_GET16(hc->sca_base, 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->iobase); } /* * 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->sca_base, dmac->dsr); SRC_PUT8(hc->sca_base, 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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, 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->sca_base, dmac->dsr); SRC_PUT8(hc->sca_base, 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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, dmac->eda)); if (hc->mempages) { SRC_SET_ON(hc->iobase); SRC_SET_MEM(hc->iobase, 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->iobase); } #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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, dmac->eda), dsr); /* * Make sure we eat as many as possible. * Then get the system running again. */ if (hc->mempages) SRC_SET_ON(hc->iobase); sr_eat_packet(sc, 0); #ifndef NETGRAPH sc->ifsppp.pp_if.if_ierrors++; #else /* NETGRAPH */ sc->ierrors[2]++; #endif /* NETGRAPH */ SRC_PUT8(hc->sca_base, sca->msci[mch].cmd, SCA_CMD_RXMSGREJ); SRC_PUT8(hc->sca_base, 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->sca_base, dmac->cda), SRC_GET16(hc->sca_base, dmac->eda), SRC_GET8(hc->sca_base, dmac->dsr)); #endif if (hc->mempages) SRC_SET_OFF(hc->iobase); } /* * 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 /* * 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->sca_base, msci->st0); got_st1 = SRC_GET8(hc->sca_base, msci->st1); got_st2 = SRC_GET8(hc->sca_base, msci->st2); got_st3 = SRC_GET8(hc->sca_base, 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); } #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->sca_base, 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 *nodep) { 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 = node->private; /* * check if it's our friend the debug hook */ if (strcmp(name, NG_SR_HOOK_DEBUG) == 0) { hook->private = NULL; /* paranoid */ sc->debug_hook = hook; return (0); } /* * Check for raw mode hook. */ if (strcmp(name, NG_SR_HOOK_RAW) != 0) { return (EINVAL); } hook->private = 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, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook) { struct sr_softc * sc; int error = 0; sc = node->private; 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; MALLOC(*resp, struct ng_mesg *, resplen, M_NETGRAPH, M_NOWAIT | M_ZERO); if (*resp == NULL) { error = ENOMEM; break; } arg = (*resp)->data; /* * Put in the throughput information. */ 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.version = NG_VERSION; (*resp)->header.arglen = strlen(arg) + 1; (*resp)->header.token = msg->header.token; (*resp)->header.typecookie = NG_SR_COOKIE; (*resp)->header.cmd = msg->header.cmd; strncpy((*resp)->header.cmdstr, "status", NG_CMDSTRLEN); } break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } free(msg, M_NETGRAPH); return (error); } /* * get data from another node and transmit it to the correct channel */ static int ngsr_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { int s; int error = 0; struct sr_softc * sc = hook->node->private; struct ifqueue *xmitq_p; /* * data doesn't come in from just anywhere (e.g control hook) */ if ( hook->private == NULL) { error = ENETDOWN; goto bad; } /* * Now queue the data for when it can be sent */ if (meta && meta->priority > 0) { xmitq_p = (&sc->xmitq_hipri); } 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_DATA(m, 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_rmnode(node_p node) { struct sr_softc * sc = node->private; sr_down(sc); ng_cutlinks(node); node->flags &= ~NG_INVALID; /* bounce back to life */ return (0); } /* already linked */ static int ngsr_connect(hook_p hook) { + /* probably not at splnet, force outward queueing */ + hook->peer->flags |= HK_QUEUE; /* 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 = hook->node->private; int s; /* * If it's the data hook, then free resources etc. */ if (hook->private) { 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/i4b/driver/i4b_ing.c =================================================================== --- head/sys/i4b/driver/i4b_ing.c (revision 69921) +++ head/sys/i4b/driver/i4b_ing.c (revision 69922) @@ -1,860 +1,863 @@ /* * Copyright (c) 1999, 2000 Hellmuth Michaelis. 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. * * 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. * *--------------------------------------------------------------------------- * * i4b_ing.c - isdn4bsd B-channel to netgraph driver * ------------------------------------------------- * * $Id: i4b_ing.c,v 1.10 2000/04/27 11:35:00 hm Exp $ * * $FreeBSD$ * * last edit-date: [Thu Nov 9 11:29:12 2000] * *---------------------------------------------------------------------------*/ #include "i4bing.h" #if NI4BING > 0 #include "opt_i4b.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define I4BINGACCT 1 /* enable accounting messages */ #define I4BINGACCTINTVL 2 /* accounting msg interval in secs */ #define I4BINGMAXQLEN 50 /* max queue length */ /* initialized by L4 */ static drvr_link_t ing_drvr_linktab[NI4BING]; static isdn_link_t *isdn_linktab[NI4BING]; struct ing_softc { int sc_unit; /* unit number */ int sc_state; /* state of the interface */ call_desc_t *sc_cdp; /* ptr to call descriptor */ int sc_updown; /* soft state of interface */ struct ifqueue sc_fastq; /* interactive traffic */ int sc_dialresp; /* dialresponse */ int sc_lastdialresp;/* last dialresponse */ #if I4BINGACCT struct callout_handle sc_callout; int sc_iinb; /* isdn driver # of inbytes */ int sc_ioutb; /* isdn driver # of outbytes */ int sc_inb; /* # of bytes rx'd */ int sc_outb; /* # of bytes tx'd */ int sc_linb; /* last # of bytes rx'd */ int sc_loutb; /* last # of bytes tx'd */ int sc_fn; /* flag, first null acct */ #endif int sc_inpkt; /* incoming packets */ int sc_outpkt; /* outgoing packets */ struct ifqueue xmitq_hipri; /* hi-priority transmit queue */ struct ifqueue xmitq; /* transmit queue */ node_p node; /* back pointer to node */ char nodename[NG_NODELEN + 1]; /* store our node name */ hook_p debughook; hook_p hook; u_int packets_in; /* packets in from downstream */ u_int packets_out; /* packets out towards downstream */ u_int32_t flags; } ing_softc[NI4BING]; enum ing_states { ST_IDLE, /* initialized, ready, idle */ ST_DIALING, /* dialling out to remote */ ST_CONNECTED /* connected to remote */ }; static void i4bingattach(void *); PSEUDO_SET(i4bingattach, i4b_ing); static void ing_init_linktab(int unit); static void ing_tx_queue_empty(int unit); /* ========= NETGRAPH ============= */ #define NG_ING_NODE_TYPE "i4bing" /* node type name */ #define NGM_ING_COOKIE 947513046 /* node type cookie */ /* Hook names */ #define NG_ING_HOOK_DEBUG "debug" #define NG_ING_HOOK_RAW "rawdata" /* Netgraph commands understood by this node type */ enum { NGM_ING_SET_FLAG = 1, NGM_ING_GET_STATUS, }; /* This structure is returned by the NGM_ING_GET_STATUS command */ struct ngingstat { u_int packets_in; /* packets in from downstream */ u_int packets_out; /* packets out towards downstream */ }; /* * This is used to define the 'parse type' for a struct ngingstat, which * is bascially a description of how to convert a binary struct ngingstat * to an ASCII string and back. See ng_parse.h for more info. * * This needs to be kept in sync with the above structure definition */ #define NG_ING_STATS_TYPE_INFO { \ { \ { "packets_in", &ng_parse_int32_type }, \ { "packets_out", &ng_parse_int32_type }, \ { NULL }, \ } \ } /* * This section contains the netgraph method declarations for the * sample node. These methods define the netgraph 'type'. */ static ng_constructor_t ng_ing_constructor; static ng_rcvmsg_t ng_ing_rcvmsg; static ng_shutdown_t ng_ing_rmnode; static ng_newhook_t ng_ing_newhook; static ng_connect_t ng_ing_connect; static ng_rcvdata_t ng_ing_rcvdata; static ng_disconnect_t ng_ing_disconnect; /* Parse type for struct ngingstat */ static const struct ng_parse_struct_info ng_ing_stat_type_info = NG_ING_STATS_TYPE_INFO; static const struct ng_parse_type ng_ing_stat_type = { &ng_parse_struct_type, &ng_ing_stat_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_ing_cmdlist[] = { { NGM_ING_COOKIE, NGM_ING_GET_STATUS, "getstatus", NULL, &ng_ing_stat_type, }, { NGM_ING_COOKIE, NGM_ING_SET_FLAG, "setflag", &ng_parse_int32_type, NULL }, { 0 } }; /* Netgraph node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_ING_NODE_TYPE, NULL, ng_ing_constructor, ng_ing_rcvmsg, ng_ing_rmnode, ng_ing_newhook, NULL, ng_ing_connect, ng_ing_rcvdata, ng_ing_rcvdata, ng_ing_disconnect, ng_ing_cmdlist }; NETGRAPH_INIT_ORDERED(ing, &typestruct, SI_SUB_DRIVERS, SI_ORDER_ANY); /*===========================================================================* * DEVICE DRIVER ROUTINES *===========================================================================*/ /*---------------------------------------------------------------------------* * interface attach routine at kernel boot time *---------------------------------------------------------------------------*/ static void i4bingattach(void *dummy) { struct ing_softc *sc = ing_softc; int i; int ret; printf("i4bing: %d i4b NetGraph ISDN B-channel device(s) attached\n", NI4BING); for(i=0; i < NI4BING; sc++, i++) { sc->sc_unit = i; ing_init_linktab(i); NDBGL4(L4_DIALST, "setting dial state to ST_IDLE"); sc->sc_state = ST_IDLE; sc->sc_fastq.ifq_maxlen = I4BINGMAXQLEN; mtx_init(&sc->sc_fastq.ifq_mtx, "i4b_ing_fastq", MTX_DEF); #if I4BINGACCT callout_handle_init(&sc->sc_callout); sc->sc_iinb = 0; sc->sc_ioutb = 0; sc->sc_inb = 0; sc->sc_outb = 0; sc->sc_linb = 0; sc->sc_loutb = 0; sc->sc_fn = 1; #endif sc->sc_inpkt = 0; sc->sc_outpkt = 0; sc->sc_updown = SOFT_ENA; /* soft enabled */ sc->sc_dialresp = DSTAT_NONE; /* no response */ sc->sc_lastdialresp = DSTAT_NONE; /* setup a netgraph node */ if ((ret = ng_make_node_common(&typestruct, &sc->node))) { printf("ing: ng_make_node_common, ret = %d\n!", ret); } sc->node->private = sc; sc->xmitq.ifq_maxlen = IFQ_MAXLEN; sc->xmitq_hipri.ifq_maxlen = IFQ_MAXLEN; mtx_init(&sc->xmitq.ifq_mtx, "i4b_ing_xmitq", MTX_DEF); mtx_init(&sc->xmitq_hipri.ifq_mtx, "i4b_ing_hipri", MTX_DEF); /* name the netgraph node */ sprintf(sc->nodename, "%s%d", NG_ING_NODE_TYPE, sc->sc_unit); if(ng_name_node(sc->node, sc->nodename)) { ng_rmnode(sc->node); ng_unref(sc->node); } } } #ifdef I4BINGACCT /*---------------------------------------------------------------------------* * accounting timeout routine *---------------------------------------------------------------------------*/ static void ing_timeout(struct ing_softc *sc) { bchan_statistics_t bs; int unit = sc->sc_unit; /* get # of bytes in and out from the HSCX driver */ (*isdn_linktab[unit]->bch_stat) (isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs); sc->sc_ioutb += bs.outbytes; sc->sc_iinb += bs.inbytes; if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn) { int ri = (sc->sc_iinb - sc->sc_linb)/I4BINGACCTINTVL; int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BINGACCTINTVL; if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb)) sc->sc_fn = 0; else sc->sc_fn = 1; sc->sc_linb = sc->sc_iinb; sc->sc_loutb = sc->sc_ioutb; i4b_l4_accounting(BDRV_ING, unit, ACCT_DURING, sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_ioutb, sc->sc_iinb); } sc->sc_callout = timeout((TIMEOUT_FUNC_T)ing_timeout, (void *)sc, I4BINGACCTINTVL*hz); } #endif /* I4BINGACCT */ #if 0 /*---------------------------------------------------------------------------* * clear the interface's send queues *---------------------------------------------------------------------------*/ static void ingclearqueue(struct ifqueue *iq) { int x; x = splimp(); IF_DRAIN(iq); splx(x); } #endif /*===========================================================================* * ISDN INTERFACE ROUTINES *===========================================================================*/ /*---------------------------------------------------------------------------* * this routine is called from L4 handler at connect time *---------------------------------------------------------------------------*/ static void ing_connect(int unit, void *cdp) { struct ing_softc *sc = &ing_softc[unit]; int s; sc->sc_cdp = (call_desc_t *)cdp; s = SPLI4B(); NDBGL4(L4_DIALST, "ing%d: setting dial state to ST_CONNECTED", unit); sc->sc_dialresp = DSTAT_NONE; sc->sc_lastdialresp = DSTAT_NONE; #if I4BINGACCT sc->sc_iinb = 0; sc->sc_ioutb = 0; sc->sc_inb = 0; sc->sc_outb = 0; sc->sc_linb = 0; sc->sc_loutb = 0; sc->sc_callout = timeout((TIMEOUT_FUNC_T)ing_timeout, (void *)sc, I4BINGACCTINTVL*hz); #endif sc->sc_state = ST_CONNECTED; splx(s); } /*---------------------------------------------------------------------------* * this routine is called from L4 handler at disconnect time *---------------------------------------------------------------------------*/ static void ing_disconnect(int unit, void *cdp) { call_desc_t *cd = (call_desc_t *)cdp; struct ing_softc *sc = &ing_softc[unit]; /* new stuff to check that the active channel is being closed */ if (cd != sc->sc_cdp) { NDBGL4(L4_INGDBG, "ing%d: channel %d not active", cd->driver_unit, cd->channelid); return; } #if I4BINGACCT untimeout((TIMEOUT_FUNC_T)ing_timeout, (void *)sc, sc->sc_callout); #endif i4b_l4_accounting(BDRV_ING, cd->driver_unit, ACCT_FINAL, sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_outb, sc->sc_inb); sc->sc_cdp = (call_desc_t *)0; NDBGL4(L4_DIALST, "setting dial state to ST_IDLE"); sc->sc_dialresp = DSTAT_NONE; sc->sc_lastdialresp = DSTAT_NONE; sc->sc_state = ST_IDLE; } /*---------------------------------------------------------------------------* * this routine is used to give a feedback from userland daemon * in case of dial problems *---------------------------------------------------------------------------*/ static void ing_dialresponse(int unit, int status, cause_t cause) { struct ing_softc *sc = &ing_softc[unit]; sc->sc_dialresp = status; NDBGL4(L4_INGDBG, "ing%d: last=%d, this=%d", unit, sc->sc_lastdialresp, sc->sc_dialresp); if(status != DSTAT_NONE) { NDBGL4(L4_INGDBG, "ing%d: clearing queues", unit); /* ingclearqueues(sc); */ } } /*---------------------------------------------------------------------------* * interface soft up/down *---------------------------------------------------------------------------*/ static void ing_updown(int unit, int updown) { struct ing_softc *sc = &ing_softc[unit]; sc->sc_updown = updown; } /*---------------------------------------------------------------------------* * this routine is called from the HSCX interrupt handler * when a new frame (mbuf) has been received and was put on * the rx queue. It is assumed that this routines runs at * pri level splimp() ! Keep it short ! *---------------------------------------------------------------------------*/ static void ing_rx_data_rdy(int unit) { register struct ing_softc *sc = &ing_softc[unit]; register struct mbuf *m; + int error; if((m = *isdn_linktab[unit]->rx_mbuf) == NULL) return; #if I4BINGACCT sc->sc_inb += m->m_pkthdr.len; #endif m->m_pkthdr.rcvif = NULL; sc->sc_inpkt++; - ng_queue_data(sc->hook, m, NULL); + NG_SEND_DATA_ONLY(error, sc->hook, m); } /*---------------------------------------------------------------------------* * this routine is called from the HSCX interrupt handler * when the last frame has been sent out and there is no * further frame (mbuf) in the tx queue. *---------------------------------------------------------------------------*/ static void ing_tx_queue_empty(int unit) { register struct ing_softc *sc = &ing_softc[unit]; register struct mbuf *m; int x = 0; if(sc->sc_state != ST_CONNECTED) return; for(;;) { IF_DEQUEUE(&sc->xmitq_hipri, m); if(m == NULL) { IF_DEQUEUE(&sc->xmitq, m); if(m == NULL) break; } #if I4BINGACCT sc->sc_outb += m->m_pkthdr.len; #endif x = 1; if(! IF_HANDOFF(isdn_linktab[unit]->tx_queue, m, NULL)) { NDBGL4(L4_INGDBG, "ing%d: tx queue full!", unit); } } if(x) (*isdn_linktab[unit]->bch_tx_start)(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel); } /*---------------------------------------------------------------------------* * this routine is called from the HSCX interrupt handler * each time a packet is received or transmitted. It should * be used to implement an activity timeout mechanism. *---------------------------------------------------------------------------*/ static void ing_activity(int unit, int rxtx) { ing_softc[unit].sc_cdp->last_active_time = SECOND; } /*---------------------------------------------------------------------------* * return this drivers linktab address *---------------------------------------------------------------------------*/ drvr_link_t * ing_ret_linktab(int unit) { return(&ing_drvr_linktab[unit]); } /*---------------------------------------------------------------------------* * setup the isdn_linktab for this driver *---------------------------------------------------------------------------*/ void ing_set_linktab(int unit, isdn_link_t *ilt) { isdn_linktab[unit] = ilt; } /*---------------------------------------------------------------------------* * initialize this drivers linktab *---------------------------------------------------------------------------*/ static void ing_init_linktab(int unit) { ing_drvr_linktab[unit].unit = unit; ing_drvr_linktab[unit].bch_rx_data_ready = ing_rx_data_rdy; ing_drvr_linktab[unit].bch_tx_queue_empty = ing_tx_queue_empty; ing_drvr_linktab[unit].bch_activity = ing_activity; ing_drvr_linktab[unit].line_connected = ing_connect; ing_drvr_linktab[unit].line_disconnected = ing_disconnect; ing_drvr_linktab[unit].dial_response = ing_dialresponse; ing_drvr_linktab[unit].updown_ind = ing_updown; } /*===========================================================================* * NETGRAPH INTERFACE ROUTINES *===========================================================================*/ /*---------------------------------------------------------------------------* * 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 ng_ing_constructor(node_p *nodep) { return(EINVAL); } /*---------------------------------------------------------------------------* * Give our ok for a hook to be added... * Add the hook's private info to the hook structure. *---------------------------------------------------------------------------*/ static int ng_ing_newhook(node_p node, hook_p hook, const char *name) { struct ing_softc *sc = node->private; /* * check if it's our friend the debug hook */ if(strcmp(name, NG_ING_HOOK_DEBUG) == 0) { hook->private = NULL; /* paranoid */ sc->debughook = hook; return (0); } /* * Check for raw mode hook. */ if(strcmp(name, NG_ING_HOOK_RAW) == 0) { hook->private = sc; sc->hook = hook; return (0); } return (EINVAL); } /*---------------------------------------------------------------------------* * 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. *---------------------------------------------------------------------------*/ #if defined(__FreeBSD_version) && __FreeBSD_version >= 500000 static int ng_ing_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) #else static int ng_ing_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) #endif { struct ing_softc *sc = node->private; struct ng_mesg *resp = NULL; int error = 0; if(msg->header.typecookie == NGM_GENERIC_COOKIE) { switch(msg->header.cmd) { case NGM_TEXT_STATUS: { char *arg; char *p; int pos = 0; NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg) + NG_TEXTRESPONSE, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } arg = (char *) resp->data; switch(sc->sc_state) { case ST_IDLE: p = "idle"; break; case ST_DIALING: p = "dialing"; break; case ST_CONNECTED: p = "connected"; break; default: p = "???"; break; } pos = sprintf(arg, "state = %s (%d)\n", p, sc->sc_state); #if I4BINGACCT pos += sprintf(arg + pos, "%d bytes in, %d bytes out\n", sc->sc_inb, sc->sc_outb); #endif pos += sprintf(arg + pos, "%d pkts in, %d pkts out\n", sc->sc_inpkt, sc->sc_outpkt); resp->header.arglen = pos + 1; break; } default: error = EINVAL; break; } } else if(msg->header.typecookie == NGM_ING_COOKIE) { switch (msg->header.cmd) { case NGM_ING_GET_STATUS: { struct ngingstat *stats; NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); if (!resp) { error = ENOMEM; break; } stats = (struct ngingstat *) resp->data; stats->packets_in = sc->packets_in; stats->packets_out = sc->packets_out; break; } case NGM_ING_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; } } else { error = EINVAL; /* unknown cookie type */ } /* Take care of synchronous response, if any */ if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); /* Free the message and return */ FREE(msg, M_NETGRAPH); return(error); } /*---------------------------------------------------------------------------* * get data from another node and transmit it out on a B-channel *---------------------------------------------------------------------------*/ #if defined(__FreeBSD_version) && __FreeBSD_version >= 500000 static int ng_ing_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) #else static int ng_ing_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) #endif { struct ing_softc *sc = hook->node->private; struct ifqueue *xmitq_p; int s; if(hook->private == NULL) { NG_FREE_DATA(m, meta); return(ENETDOWN); } if(sc->sc_state == ST_IDLE || sc->sc_state == ST_DIALING) { i4b_l4_dialout(BDRV_ING, sc->sc_unit); sc->sc_state = ST_DIALING; } sc->sc_outpkt++; /* * Now queue the data for when it can be sent */ if (meta && meta->priority > 0) { xmitq_p = (&sc->xmitq_hipri); } 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); NG_FREE_DATA(m, meta); return(ENOBUFS); } _IF_ENQUEUE(xmitq_p, m); IF_UNLOCK(xmitq_p); ing_tx_queue_empty(sc->sc_unit); splx(s); return (0); } /*---------------------------------------------------------------------------* * Do local shutdown processing.. * If we are a persistant device, we might refuse to go away, and * we'd only remove our links and reset ourself. *---------------------------------------------------------------------------*/ static int ng_ing_rmnode(node_p node) { struct ing_softc *sc = node->private; node->flags |= NG_INVALID; ng_cutlinks(node); sc->packets_in = 0; /* reset stats */ sc->packets_out = 0; node->flags &= ~NG_INVALID; /* reset invalid flag */ return (0); } /*---------------------------------------------------------------------------* * This is called once we've already connected a new hook to the other node. *---------------------------------------------------------------------------*/ static int ng_ing_connect(hook_p hook) { + /* probably not at splnet, force outward queueing */ + hook->peer->flags |= HK_QUEUE; return (0); } /* * Dook disconnection * * For this type, removal of the last link destroys the node */ static int ng_ing_disconnect(hook_p hook) { struct ing_softc *sc = hook->node->private; int s; if(hook->private) { s = splimp(); splx(s); } else { sc->debughook = NULL; } return (0); } /*===========================================================================*/ #endif /* NI4BING > 0 */ Index: head/sys/netgraph/netgraph.h =================================================================== --- head/sys/netgraph/netgraph.h (revision 69921) +++ head/sys/netgraph/netgraph.h (revision 69922) @@ -1,289 +1,315 @@ /* * 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_ 1 #include #include #include #ifndef _KERNEL #error "This file should not be included in user level programs" #endif /* * Structure of a hook */ struct ng_hook { char *name; /* what this node knows this link as */ void *private; /* node dependant ID for this hook */ int flags; /* info about this hook/link */ int refs; /* dont actually free this till 0 */ struct ng_hook *peer; /* the other end of this link */ struct ng_node *node; /* The node this hook is attached to */ LIST_ENTRY(ng_hook) hooks; /* linked list of all hooks on node */ }; typedef struct ng_hook *hook_p; /* Flags for a hook */ #define HK_INVALID 0x0001 /* don't trust it! */ +#define HK_QUEUE 0x0002 /* queue for later delivery */ /* * Structure of a node */ struct ng_node { char *name; /* optional globally unique name */ struct ng_type *type; /* the installed 'type' */ int flags; /* see below for bit definitions */ int sleepers; /* #procs sleeping on this node */ int refs; /* number of references to this node */ int numhooks; /* number of hooks */ int colour; /* for graph colouring algorithms */ void *private; /* node type dependant node ID */ ng_ID_t ID; /* Unique per node */ LIST_HEAD(hooks, ng_hook) hooks; /* linked list of node hooks */ LIST_ENTRY(ng_node) nodes; /* linked list of all nodes */ LIST_ENTRY(ng_node) idnodes; /* ID hash collision list */ }; typedef struct ng_node *node_p; /* Flags for a node */ #define NG_INVALID 0x001 /* free when all sleepers and refs go to 0 */ #define NG_BUSY 0x002 /* callers should sleep or wait */ #define NG_TOUCHED 0x004 /* to avoid cycles when 'flooding' */ #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 */ /* * 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 method definitions */ typedef int ng_constructor_t(node_p *node); typedef int ng_rcvmsg_t(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook); 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_rcvdata_t(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta); + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp); typedef int ng_disconnect_t(hook_p hook); /* * 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_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_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; /* date comes here */ - ng_rcvdata_t *rcvdataq; /* or here if being queued */ + 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 */ }; /* Send data packet with meta-data */ -#define NG_SEND_DATA(error, hook, m, a) \ +#define NG_SEND_DATA(err, hook, m, meta) \ do { \ - (error) = ng_send_data((hook), (m), (a), NULL, NULL); \ + (err) = ng_send_data((hook), (m), (meta), \ + NULL, NULL, NULL); \ (m) = NULL; \ - (a) = NULL; \ + (meta) = NULL; \ } while (0) -/* Send queued data packet with meta-data */ -#define NG_SEND_DATAQ(error, hook, m, a) \ +/* Send data packet with no meta-data */ +#define NG_SEND_DATA_ONLY(err, hook, m) \ do { \ - (error) = ng_send_dataq((hook), (m), (a), NULL, NULL); \ + (err) = ng_send_data((hook), (m), NULL, \ + NULL, NULL, NULL); \ (m) = NULL; \ - (a) = NULL; \ } while (0) -#define NG_SEND_DATA_RET(error, hook, m, a) \ +/* Send data packet including a possible sync response pointer */ +#define NG_SEND_DATA_RESP(err, hook, m, meta, rmsg) \ do { \ + (err) = ng_send_data((hook), (m), (meta), \ + NULL, NULL, rmsg); \ + (m) = NULL; \ + (meta) = NULL; \ + } while (0) + +/* + * Send data packet including a possible sync response pointer + * Allow the callee to replace the data and pass it back + * or to simply 'reject it' or 'keep it' + */ +#define NG_SEND_DATA_RET(err, hook, m, meta, rmsg) \ + do { \ struct mbuf *rm = NULL; \ - meta_p ra = NULL; \ - (error) = ng_send_data((hook), (m), (a), &rm, &ra); \ + meta_p rmeta = NULL; \ + (err) = ng_send_data((hook), (m), (meta), \ + &rm, &rmeta, (rmsg)); \ (m) = rm; \ - (a) = ra; \ + (meta) = rmeta; \ } while (0) + /* Free metadata */ -#define NG_FREE_META(a) \ +#define NG_FREE_META(meta) \ do { \ - if ((a)) { \ - FREE((a), M_NETGRAPH); \ - a = NULL; \ + if ((meta)) { \ + FREE((meta), M_NETGRAPH); \ + meta = NULL; \ } \ } while (0) /* Free any data packet and/or meta-data */ -#define NG_FREE_DATA(m, a) \ +#define NG_FREE_DATA(m, meta) \ do { \ if ((m)) { \ m_freem((m)); \ m = NULL; \ } \ - NG_FREE_META((a)); \ + NG_FREE_META((meta)); \ } while (0) /* * 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, 1, 1, 1) #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 */ MALLOC_DECLARE(M_NETGRAPH); int ng_bypass(hook_p hook1, hook_p hook2); void ng_cutlinks(node_p node); int ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2); meta_p ng_copy_meta(meta_p meta); void ng_destroy_hook(hook_p hook); hook_p ng_findhook(node_p node, const char *name); node_p ng_findname(node_p node, const char *name); struct ng_type *ng_findtype(const char *type); int ng_make_node(const char *type, node_p *nodepp); int ng_make_node_common(struct ng_type *typep, node_p *nodep); int ng_mkpeer(node_p node, const char *name, const char *name2, char *type); int ng_mod_event(module_t mod, int what, void *arg); 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); -int ng_path2node(node_p here, const char *path, node_p *dest, char **rtnp, - hook_p *lasthook); +int ng_path2node(node_p here, const char *path, + node_p *dest, hook_p *lasthook); int ng_path_parse(char *addr, char **node, char **path, char **hook); int ng_queue_data(hook_p hook, struct mbuf *m, meta_p meta); -int ng_queue_msg(node_p here, struct ng_mesg *msg, const char *address); +int ng_queue_msg(node_p here, struct ng_mesg *msg, const char *address, + hook_p hook, char *retaddr); void ng_release_node(node_p node); void ng_rmnode(node_p node); int ng_send_data(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta); -int ng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta); -int ng_send_msg(node_p here, struct ng_mesg *msg, - const char *address, struct ng_mesg **resp); + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp); +int ng_send_msg(node_p here, struct ng_mesg *msg, const char *address, + hook_p hook, char *retaddr, struct ng_mesg **resp); void ng_unname(node_p node); void ng_unref(node_p node); int ng_wait_node(node_p node, char *msg); #endif /* _NETGRAPH_NETGRAPH_H_ */ Index: head/sys/netgraph/ng_UI.c =================================================================== --- head/sys/netgraph/ng_UI.c (revision 69921) +++ head/sys/netgraph/ng_UI.c (revision 69922) @@ -1,237 +1,252 @@ /* * ng_UI.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_UI.c,v 1.14 1999/11/01 09:24:51 julian Exp $ */ #include #include #include #include #include #include #include #include #include #include /* * DEFINITIONS */ /* Everything, starting with sdlc on has defined UI as 0x03 */ #define HDLC_UI 0x03 /* Node private data */ struct ng_UI_private { hook_p downlink; hook_p uplink; }; typedef struct ng_UI_private *priv_p; /* Netgraph node methods */ static ng_constructor_t ng_UI_constructor; static ng_rcvmsg_t ng_UI_rcvmsg; static ng_shutdown_t ng_UI_rmnode; static ng_newhook_t ng_UI_newhook; static ng_rcvdata_t ng_UI_rcvdata; static ng_disconnect_t ng_UI_disconnect; /* Node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_UI_NODE_TYPE, NULL, ng_UI_constructor, ng_UI_rcvmsg, ng_UI_rmnode, ng_UI_newhook, NULL, NULL, ng_UI_rcvdata, - ng_UI_rcvdata, ng_UI_disconnect, NULL }; NETGRAPH_INIT(UI, &typestruct); /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Create a newborn node. We start with an implicit reference. */ static int ng_UI_constructor(node_p *nodep) { priv_p priv; int error; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv == NULL) return (ENOMEM); /* Call generic node constructor */ if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Done */ return (0); } /* * Give our ok for a hook to be added */ static int ng_UI_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; if (!strcmp(name, NG_UI_HOOK_DOWNSTREAM)) { if (priv->downlink) return (EISCONN); priv->downlink = hook; } else if (!strcmp(name, NG_UI_HOOK_UPSTREAM)) { if (priv->uplink) return (EISCONN); priv->uplink = hook; } else return (EINVAL); return (0); } /* * Receive a control message */ static int ng_UI_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rp, hook_p lasthook) { + const priv_p priv = node->private; + + if ((msg->header.typecookie == NGM_FLOW_COOKIE) && lasthook) { + if (lasthook == priv->downlink) { + if (priv->uplink) { + return (ng_send_msg(node, msg, NULL, + priv->uplink, raddr, rp)); + } + } else { + if (priv->downlink) { + return (ng_send_msg(node, msg, NULL, + priv->downlink, raddr, rp)); + } + } + } + FREE(msg, M_NETGRAPH); return (EINVAL); } #define MAX_ENCAPS_HDR 1 #define ERROUT(x) do { error = (x); goto done; } while (0) /* * Receive a data frame */ static int ng_UI_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const node_p node = hook->node; const priv_p priv = node->private; int error = 0; if (hook == priv->downlink) { u_char *start, *ptr; if (!m || (m->m_len < MAX_ENCAPS_HDR && !(m = m_pullup(m, MAX_ENCAPS_HDR)))) ERROUT(ENOBUFS); ptr = start = mtod(m, u_char *); /* Must be UI frame */ if (*ptr++ != HDLC_UI) ERROUT(0); m_adj(m, ptr - start); NG_SEND_DATA(error, priv->uplink, m, meta); /* m -> NULL */ } else if (hook == priv->uplink) { M_PREPEND(m, 1, M_DONTWAIT); /* Prepend IP NLPID */ if (!m) ERROUT(ENOBUFS); mtod(m, u_char *)[0] = HDLC_UI; NG_SEND_DATA(error, priv->downlink, m, meta); /* m -> NULL */ } else panic(__FUNCTION__); done: NG_FREE_DATA(m, meta); /* does nothing if m == NULL */ return (error); } /* * Shutdown node */ static int ng_UI_rmnode(node_p node) { const priv_p priv = node->private; /* Take down netgraph node */ node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); bzero(priv, sizeof(*priv)); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); return (0); } /* * Hook disconnection */ static int ng_UI_disconnect(hook_p hook) { const priv_p priv = hook->node->private; if (hook->node->numhooks == 0) ng_rmnode(hook->node); else if (hook == priv->downlink) priv->downlink = NULL; else if (hook == priv->uplink) priv->uplink = NULL; else panic(__FUNCTION__); return (0); } Index: head/sys/netgraph/ng_async.c =================================================================== --- head/sys/netgraph/ng_async.c (revision 69921) +++ head/sys/netgraph/ng_async.c (revision 69922) @@ -1,614 +1,613 @@ /* * ng_async.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_async.c,v 1.17 1999/11/01 09:24:51 julian Exp $ */ /* * This node type implements a PPP style sync <-> async converter. * See RFC 1661 for details of how asynchronous encoding works. */ #include #include #include #include #include #include #include #include #include #include #include /* Async decode state */ #define MODE_HUNT 0 #define MODE_NORMAL 1 #define MODE_ESC 2 /* Private data structure */ struct ng_async_private { node_p node; /* Our node */ hook_p async; /* Asynchronous side */ hook_p sync; /* Synchronous side */ u_char amode; /* Async hunt/esape mode */ u_int16_t fcs; /* Decoded async FCS (so far) */ u_char *abuf; /* Buffer to encode sync into */ u_char *sbuf; /* Buffer to decode async into */ u_int slen; /* Length of data in sbuf */ long lasttime; /* Time of last async packet sent */ struct ng_async_cfg cfg; /* Configuration */ struct ng_async_stat stats; /* Statistics */ }; typedef struct ng_async_private *sc_p; /* Useful macros */ #define ASYNC_BUF_SIZE(smru) (2 * (smru) + 10) #define SYNC_BUF_SIZE(amru) ((amru) + 10) #define ERROUT(x) do { error = (x); goto done; } while (0) /* Netgraph methods */ static ng_constructor_t nga_constructor; static ng_rcvdata_t nga_rcvdata; static ng_rcvmsg_t nga_rcvmsg; static ng_shutdown_t nga_shutdown; static ng_newhook_t nga_newhook; static ng_disconnect_t nga_disconnect; /* Helper stuff */ static int nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta); static int nga_rcv_async(const sc_p sc, struct mbuf *m, meta_p meta); /* Parse type for struct ng_async_cfg */ static const struct ng_parse_struct_info nga_config_type_info = NG_ASYNC_CONFIG_TYPE_INFO; static const struct ng_parse_type nga_config_type = { &ng_parse_struct_type, &nga_config_type_info }; /* Parse type for struct ng_async_stat */ static const struct ng_parse_struct_info nga_stats_type_info = NG_ASYNC_STATS_TYPE_INFO; static const struct ng_parse_type nga_stats_type = { &ng_parse_struct_type, &nga_stats_type_info, }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist nga_cmdlist[] = { { NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_SET_CONFIG, "setconfig", &nga_config_type, NULL }, { NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_GET_CONFIG, "getconfig", NULL, &nga_config_type }, { NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_GET_STATS, "getstats", NULL, &nga_stats_type }, { NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_CLR_STATS, "clrstats", &nga_stats_type, NULL }, { 0 } }; /* Define the netgraph node type */ static struct ng_type typestruct = { NG_VERSION, NG_ASYNC_NODE_TYPE, NULL, nga_constructor, nga_rcvmsg, nga_shutdown, nga_newhook, NULL, NULL, nga_rcvdata, - nga_rcvdata, nga_disconnect, nga_cmdlist }; NETGRAPH_INIT(async, &typestruct); /* CRC table */ static const u_int16_t fcstab[]; /****************************************************************** NETGRAPH NODE METHODS ******************************************************************/ /* * Initialize a new node */ static int nga_constructor(node_p *nodep) { sc_p sc; int error; if ((error = ng_make_node_common(&typestruct, nodep))) return (error); MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); if (sc == NULL) return (ENOMEM); sc->amode = MODE_HUNT; sc->cfg.accm = ~0; sc->cfg.amru = NG_ASYNC_DEFAULT_MRU; sc->cfg.smru = NG_ASYNC_DEFAULT_MRU; MALLOC(sc->abuf, u_char *, ASYNC_BUF_SIZE(sc->cfg.smru), M_NETGRAPH, M_NOWAIT); if (sc->abuf == NULL) goto fail; MALLOC(sc->sbuf, u_char *, SYNC_BUF_SIZE(sc->cfg.amru), M_NETGRAPH, M_NOWAIT); if (sc->sbuf == NULL) { FREE(sc->abuf, M_NETGRAPH); fail: FREE(sc, M_NETGRAPH); return (ENOMEM); } (*nodep)->private = sc; sc->node = *nodep; return (0); } /* * Reserve a hook for a pending connection */ static int nga_newhook(node_p node, hook_p hook, const char *name) { const sc_p sc = node->private; hook_p *hookp; if (!strcmp(name, NG_ASYNC_HOOK_ASYNC)) hookp = &sc->async; else if (!strcmp(name, NG_ASYNC_HOOK_SYNC)) hookp = &sc->sync; else return (EINVAL); if (*hookp) return (EISCONN); *hookp = hook; return (0); } /* * Receive incoming data */ static int nga_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const sc_p sc = hook->node->private; if (hook == sc->sync) return (nga_rcv_sync(sc, m, meta)); if (hook == sc->async) return (nga_rcv_async(sc, m, meta)); panic(__FUNCTION__); } /* * Receive incoming control message */ static int nga_rcvmsg(node_p node, struct ng_mesg *msg, const char *rtn, struct ng_mesg **rptr, hook_p lasthook) { const sc_p sc = (sc_p) node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_ASYNC_COOKIE: switch (msg->header.cmd) { case NGM_ASYNC_CMD_GET_STATS: NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); *((struct ng_async_stat *) resp->data) = sc->stats; break; case NGM_ASYNC_CMD_CLR_STATS: bzero(&sc->stats, sizeof(sc->stats)); break; case NGM_ASYNC_CMD_SET_CONFIG: { struct ng_async_cfg *const cfg = (struct ng_async_cfg *) msg->data; u_char *buf; if (msg->header.arglen != sizeof(*cfg)) ERROUT(EINVAL); if (cfg->amru < NG_ASYNC_MIN_MRU || cfg->amru > NG_ASYNC_MAX_MRU || cfg->smru < NG_ASYNC_MIN_MRU || cfg->smru > NG_ASYNC_MAX_MRU) ERROUT(EINVAL); cfg->enabled = !!cfg->enabled; /* normalize */ if (cfg->smru > sc->cfg.smru) { /* reallocate buffer */ MALLOC(buf, u_char *, ASYNC_BUF_SIZE(cfg->smru), M_NETGRAPH, M_NOWAIT); if (!buf) ERROUT(ENOMEM); FREE(sc->abuf, M_NETGRAPH); sc->abuf = buf; } if (cfg->amru > sc->cfg.amru) { /* reallocate buffer */ MALLOC(buf, u_char *, SYNC_BUF_SIZE(cfg->amru), M_NETGRAPH, M_NOWAIT); if (!buf) ERROUT(ENOMEM); FREE(sc->sbuf, M_NETGRAPH); sc->sbuf = buf; sc->amode = MODE_HUNT; sc->slen = 0; } if (!cfg->enabled) { sc->amode = MODE_HUNT; sc->slen = 0; } sc->cfg = *cfg; break; } case NGM_ASYNC_CMD_GET_CONFIG: NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT); if (!resp) ERROUT(ENOMEM); *((struct ng_async_cfg *) resp->data) = sc->cfg; break; default: ERROUT(EINVAL); } break; default: ERROUT(EINVAL); } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /* * Shutdown this node */ static int nga_shutdown(node_p node) { const sc_p sc = node->private; ng_cutlinks(node); ng_unname(node); FREE(sc->abuf, M_NETGRAPH); FREE(sc->sbuf, M_NETGRAPH); bzero(sc, sizeof(*sc)); FREE(sc, M_NETGRAPH); node->private = NULL; ng_unref(node); return (0); } /* * Lose a hook. When both hooks go away, we disappear. */ static int nga_disconnect(hook_p hook) { const sc_p sc = hook->node->private; hook_p *hookp; if (hook == sc->async) hookp = &sc->async; else if (hook == sc->sync) hookp = &sc->sync; else panic(__FUNCTION__); if (!*hookp) panic(__FUNCTION__ "2"); *hookp = NULL; bzero(&sc->stats, sizeof(sc->stats)); sc->lasttime = 0; if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } /****************************************************************** INTERNAL HELPER STUFF ******************************************************************/ /* * Encode a byte into the async buffer */ static __inline__ void nga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x) { *fcs = PPP_FCS(*fcs, x); if ((x < 32 && ((1 << x) & accm)) || (x == PPP_ESCAPE) || (x == PPP_FLAG)) { sc->abuf[(*len)++] = PPP_ESCAPE; x ^= PPP_TRANS; } sc->abuf[(*len)++] = x; } /* * Receive incoming synchronous data. */ static int nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta) { struct ifnet *const rcvif = m->m_pkthdr.rcvif; int alen, error = 0; struct timeval time; u_int16_t fcs, fcs0; u_int32_t accm; #define ADD_BYTE(x) nga_async_add(sc, &fcs, accm, &alen, (x)) /* Check for bypass mode */ if (!sc->cfg.enabled) { NG_SEND_DATA(error, sc->async, m, meta); return (error); } /* Get ACCM; special case LCP frames, which use full ACCM */ accm = sc->cfg.accm; if (m->m_pkthdr.len >= 4) { static const u_char lcphdr[4] = { PPP_ALLSTATIONS, PPP_UI, (u_char)(PPP_LCP >> 8), (u_char)(PPP_LCP & 0xff) }; u_char buf[4]; m_copydata(m, 0, 4, (caddr_t)buf); if (bcmp(buf, &lcphdr, 4) == 0) accm = ~0; } /* Check for overflow */ if (m->m_pkthdr.len > sc->cfg.smru) { sc->stats.syncOverflows++; NG_FREE_DATA(m, meta); return (EMSGSIZE); } /* Update stats */ sc->stats.syncFrames++; sc->stats.syncOctets += m->m_pkthdr.len; /* Initialize async encoded version of input mbuf */ alen = 0; fcs = PPP_INITFCS; /* Add beginning sync flag if it's been long enough to need one */ getmicrotime(&time); if (time.tv_sec >= sc->lasttime + 1) { sc->abuf[alen++] = PPP_FLAG; sc->lasttime = time.tv_sec; } /* Add packet payload */ while (m != NULL) { struct mbuf *n; while (m->m_len > 0) { ADD_BYTE(*mtod(m, u_char *)); m->m_data++; m->m_len--; } MFREE(m, n); m = n; } /* Add checksum and final sync flag */ fcs0 = fcs; ADD_BYTE(~fcs0 & 0xff); ADD_BYTE(~fcs0 >> 8); sc->abuf[alen++] = PPP_FLAG; /* Put frame in an mbuf and ship it off */ if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) { NG_FREE_META(meta); error = ENOBUFS; } else NG_SEND_DATA(error, sc->async, m, meta); return (error); } /* * Receive incoming asynchronous data * XXX Technically, we should strip out incoming characters * that are in our ACCM. Not sure if this is good or not. */ static int nga_rcv_async(const sc_p sc, struct mbuf * m, meta_p meta) { struct ifnet *const rcvif = m->m_pkthdr.rcvif; int error; if (!sc->cfg.enabled) { NG_SEND_DATA(error, sc->sync, m, meta); return (error); } NG_FREE_META(meta); while (m) { struct mbuf *n; for (; m->m_len > 0; m->m_data++, m->m_len--) { u_char ch = *mtod(m, u_char *); sc->stats.asyncOctets++; if (ch == PPP_FLAG) { /* Flag overrides everything */ int skip = 0; /* Check for runts */ if (sc->slen < 2) { if (sc->slen > 0) sc->stats.asyncRunts++; goto reset; } /* Verify CRC */ if (sc->fcs != PPP_GOODFCS) { sc->stats.asyncBadCheckSums++; goto reset; } sc->slen -= 2; /* Strip address and control fields */ if (sc->slen >= 2 && sc->sbuf[0] == PPP_ALLSTATIONS && sc->sbuf[1] == PPP_UI) skip = 2; /* Check for frame too big */ if (sc->slen - skip > sc->cfg.amru) { sc->stats.asyncOverflows++; goto reset; } /* OK, ship it out */ if ((n = m_devget(sc->sbuf + skip, sc->slen - skip, 0, rcvif, NULL))) NG_SEND_DATA(error, sc->sync, n, meta); sc->stats.asyncFrames++; reset: sc->amode = MODE_NORMAL; sc->fcs = PPP_INITFCS; sc->slen = 0; continue; } switch (sc->amode) { case MODE_NORMAL: if (ch == PPP_ESCAPE) { sc->amode = MODE_ESC; continue; } break; case MODE_ESC: ch ^= PPP_TRANS; sc->amode = MODE_NORMAL; break; case MODE_HUNT: default: continue; } /* Add byte to frame */ if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) { sc->stats.asyncOverflows++; sc->amode = MODE_HUNT; sc->slen = 0; } else { sc->sbuf[sc->slen++] = ch; sc->fcs = PPP_FCS(sc->fcs, ch); } } MFREE(m, n); m = n; } return (0); } /* * CRC table * * Taken from RFC 1171 Appendix B */ static const u_int16_t fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; Index: head/sys/netgraph/ng_base.c =================================================================== --- head/sys/netgraph/ng_base.c (revision 69921) +++ head/sys/netgraph/ng_base.c (revision 69922) @@ -1,2050 +1,2082 @@ /* * ng_base.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. * * Authors: Julian Elischer * Archie Cobbs * * $FreeBSD$ * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ */ /* * This file implements the base netgraph code. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_VERSION(netgraph, 1); /* List of all nodes */ static LIST_HEAD(, ng_node) nodelist; /* List of installed types */ static LIST_HEAD(, ng_type) typelist; /* Hash releted definitions */ #define ID_HASH_SIZE 32 /* most systems wont need even this many */ static LIST_HEAD(, ng_node) ID_hash[ID_HASH_SIZE]; /* Don't nead to initialise them because it's a LIST */ /* Internal functions */ static int ng_add_hook(node_p node, const char *name, hook_p * hookp); static int ng_connect(hook_p hook1, hook_p hook2); static void ng_disconnect_hook(hook_p hook); static int ng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, struct ng_mesg ** resp, hook_p hook); static ng_ID_t ng_decodeidname(const char *name); static int ngb_mod_event(module_t mod, int event, void *data); +static int ng_send_data_dont_queue(hook_p hook, struct mbuf *m, + meta_p meta, struct mbuf **ret_m, meta_p *ret_meta, + struct ng_mesg **resp); static void ngintr(void); /* Our own netgraph malloc type */ MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); /* Set this to Debugger("X") to catch all errors as they occur */ #ifndef TRAP_ERROR #define TRAP_ERROR #endif static ng_ID_t nextID = 1; #ifdef INVARIANTS #define CHECK_DATA_MBUF(m) do { \ struct mbuf *n; \ int total; \ \ if (((m)->m_flags & M_PKTHDR) == 0) \ panic("%s: !PKTHDR", __FUNCTION__); \ for (total = 0, n = (m); n != NULL; n = n->m_next) \ total += n->m_len; \ if ((m)->m_pkthdr.len != total) { \ panic("%s: %d != %d", \ __FUNCTION__, (m)->m_pkthdr.len, total); \ } \ } while (0) #else #define CHECK_DATA_MBUF(m) #endif /************************************************************************ Parse type definitions for generic messages ************************************************************************/ /* Handy structure parse type defining macro */ #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ static const struct ng_parse_struct_info \ ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args; \ static const struct ng_parse_type ng_generic_ ## lo ## _type = { \ &ng_parse_struct_type, \ &ng_ ## lo ## _type_info \ } DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); DEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); /* Get length of an array when the length is stored as a 32 bit value immediately preceeding the array -- as with struct namelist and struct typelist. */ static int ng_generic_list_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { return *((const u_int32_t *)(buf - 4)); } /* Get length of the array of struct linkinfo inside a struct hooklist */ static int ng_generic_linkinfo_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { const struct hooklist *hl = (const struct hooklist *)start; return hl->nodeinfo.hooks; } /* Array type for a variable length array of struct namelist */ static const struct ng_parse_array_info ng_nodeinfoarray_type_info = { &ng_generic_nodeinfo_type, &ng_generic_list_getLength }; static const struct ng_parse_type ng_generic_nodeinfoarray_type = { &ng_parse_array_type, &ng_nodeinfoarray_type_info }; /* Array type for a variable length array of struct typelist */ static const struct ng_parse_array_info ng_typeinfoarray_type_info = { &ng_generic_typeinfo_type, &ng_generic_list_getLength }; static const struct ng_parse_type ng_generic_typeinfoarray_type = { &ng_parse_array_type, &ng_typeinfoarray_type_info }; /* Array type for array of struct linkinfo in struct hooklist */ static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { &ng_generic_linkinfo_type, &ng_generic_linkinfo_getLength }; static const struct ng_parse_type ng_generic_linkinfo_array_type = { &ng_parse_array_type, &ng_generic_linkinfo_array_type_info }; DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, (&ng_generic_nodeinfoarray_type)); /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_generic_cmds[] = { { NGM_GENERIC_COOKIE, NGM_SHUTDOWN, "shutdown", NULL, NULL }, { NGM_GENERIC_COOKIE, NGM_MKPEER, "mkpeer", &ng_generic_mkpeer_type, NULL }, { NGM_GENERIC_COOKIE, NGM_CONNECT, "connect", &ng_generic_connect_type, NULL }, { NGM_GENERIC_COOKIE, NGM_NAME, "name", &ng_generic_name_type, NULL }, { NGM_GENERIC_COOKIE, NGM_RMHOOK, "rmhook", &ng_generic_rmhook_type, NULL }, { NGM_GENERIC_COOKIE, NGM_NODEINFO, "nodeinfo", NULL, &ng_generic_nodeinfo_type }, { NGM_GENERIC_COOKIE, NGM_LISTHOOKS, "listhooks", NULL, &ng_generic_hooklist_type }, { NGM_GENERIC_COOKIE, NGM_LISTNAMES, "listnames", NULL, &ng_generic_listnodes_type /* same as NGM_LISTNODES */ }, { NGM_GENERIC_COOKIE, NGM_LISTNODES, "listnodes", NULL, &ng_generic_listnodes_type }, { NGM_GENERIC_COOKIE, NGM_LISTTYPES, "listtypes", NULL, &ng_generic_typeinfo_type }, { NGM_GENERIC_COOKIE, NGM_TEXT_CONFIG, "textconfig", NULL, &ng_parse_string_type }, { NGM_GENERIC_COOKIE, NGM_TEXT_STATUS, "textstatus", NULL, &ng_parse_string_type }, { NGM_GENERIC_COOKIE, NGM_ASCII2BINARY, "ascii2binary", &ng_parse_ng_mesg_type, &ng_parse_ng_mesg_type }, { NGM_GENERIC_COOKIE, NGM_BINARY2ASCII, "binary2ascii", &ng_parse_ng_mesg_type, &ng_parse_ng_mesg_type }, { 0 } }; /************************************************************************ Node routines ************************************************************************/ /* * Instantiate a node of the requested type */ int ng_make_node(const char *typename, node_p *nodepp) { struct ng_type *type; /* Check that the type makes sense */ if (typename == NULL) { TRAP_ERROR; return (EINVAL); } /* Locate the node type */ if ((type = ng_findtype(typename)) == NULL) { char filename[NG_TYPELEN + 4]; linker_file_t lf; int error; /* Not found, try to load it as a loadable module */ - snprintf(filename, sizeof(filename), "ng_%s", typename); + snprintf(filename, sizeof(filename), "/boot/kernel/ng_%s", typename); error = linker_load_file(filename, &lf); if (error != 0) return (error); lf->userrefs++; /* pretend loaded by the syscall */ /* Try again, as now the type should have linked itself in */ if ((type = ng_findtype(typename)) == NULL) return (ENXIO); } /* Call the constructor */ if (type->constructor != NULL) return ((*type->constructor)(nodepp)); else return (ng_make_node_common(type, nodepp)); } /* * Generic node creation. Called by node constructors. * The returned node has a reference count of 1. */ int ng_make_node_common(struct ng_type *type, node_p *nodepp) { node_p node; /* Require the node type to have been already installed */ if (ng_findtype(type->name) == NULL) { TRAP_ERROR; return (EINVAL); } /* Make a node and try attach it to the type */ MALLOC(node, node_p, sizeof(*node), M_NETGRAPH, M_NOWAIT | M_ZERO); if (node == NULL) { TRAP_ERROR; return (ENOMEM); } node->type = type; node->refs++; /* note reference */ type->refs++; /* Link us into the node linked list */ LIST_INSERT_HEAD(&nodelist, node, nodes); /* Initialize hook list for new node */ LIST_INIT(&node->hooks); /* get an ID and put us in the hash chain */ node->ID = nextID++; /* 137 per second for 1 year before wrap */ LIST_INSERT_HEAD(&ID_hash[node->ID % ID_HASH_SIZE], node, idnodes); /* Done */ *nodepp = node; return (0); } /* * Forceably start the shutdown process on a node. Either call * it's shutdown method, or do the default shutdown if there is * no type-specific method. * * Persistent nodes must have a type-specific method which * resets the NG_INVALID flag. */ void ng_rmnode(node_p node) { /* Check if it's already shutting down */ if ((node->flags & NG_INVALID) != 0) return; /* Add an extra reference so it doesn't go away during this */ node->refs++; /* Mark it invalid so any newcomers know not to try use it */ node->flags |= NG_INVALID; /* Ask the type if it has anything to do in this case */ if (node->type && node->type->shutdown) (*node->type->shutdown)(node); else { /* do the default thing */ ng_unname(node); ng_cutlinks(node); ng_unref(node); } /* Remove extra reference, possibly the last */ ng_unref(node); } /* * Called by the destructor to remove any STANDARD external references */ void ng_cutlinks(node_p node) { hook_p hook; /* Make sure that this is set to stop infinite loops */ node->flags |= NG_INVALID; /* If we have sleepers, wake them up; they'll see NG_INVALID */ if (node->sleepers) wakeup(node); /* Notify all remaining connected nodes to disconnect */ while ((hook = LIST_FIRST(&node->hooks)) != NULL) ng_destroy_hook(hook); } /* * Remove a reference to the node, possibly the last */ void ng_unref(node_p node) { int s; s = splhigh(); if (--node->refs <= 0) { node->type->refs--; LIST_REMOVE(node, nodes); LIST_REMOVE(node, idnodes); FREE(node, M_NETGRAPH); } splx(s); } /* * Wait for a node to come ready. Returns a node with a reference count; * don't forget to drop it when we are done with it using ng_release_node(). */ int ng_wait_node(node_p node, char *msg) { int s, error = 0; if (msg == NULL) msg = "netgraph"; s = splnet(); node->sleepers++; node->refs++; /* the sleeping process counts as a reference */ while ((node->flags & (NG_BUSY | NG_INVALID)) == NG_BUSY) error = tsleep(node, (PZERO + 1) | PCATCH, msg, 0); node->sleepers--; if (node->flags & NG_INVALID) { TRAP_ERROR; error = ENXIO; } else { KASSERT(node->refs > 1, ("%s: refs=%d", __FUNCTION__, node->refs)); node->flags |= NG_BUSY; } splx(s); /* Release the reference we had on it */ if (error != 0) ng_unref(node); return error; } /* * Release a node acquired via ng_wait_node() */ void ng_release_node(node_p node) { /* Declare that we don't want it */ node->flags &= ~NG_BUSY; /* If we have sleepers, then wake them up */ if (node->sleepers) wakeup(node); /* We also have a reference.. drop it too */ ng_unref(node); } /************************************************************************ Node ID handling ************************************************************************/ static node_p ng_ID2node(ng_ID_t ID) { node_p np; LIST_FOREACH(np, &ID_hash[ID % ID_HASH_SIZE], idnodes) { if (np->ID == ID) break; } return(np); } ng_ID_t ng_node2ID(node_p node) { return (node->ID); } /************************************************************************ Node name handling ************************************************************************/ /* * Assign a node a name. Once assigned, the name cannot be changed. */ int ng_name_node(node_p node, const char *name) { int i; /* Check the name is valid */ for (i = 0; i < NG_NODELEN + 1; i++) { if (name[i] == '\0' || name[i] == '.' || name[i] == ':') break; } if (i == 0 || name[i] != '\0') { TRAP_ERROR; return (EINVAL); } if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ TRAP_ERROR; return (EINVAL); } /* Check the node isn't already named */ if (node->name != NULL) { TRAP_ERROR; return (EISCONN); } /* Check the name isn't already being used */ if (ng_findname(node, name) != NULL) { TRAP_ERROR; return (EADDRINUSE); } /* Allocate space and copy it */ MALLOC(node->name, char *, strlen(name) + 1, M_NETGRAPH, M_NOWAIT); if (node->name == NULL) { TRAP_ERROR; return (ENOMEM); } strcpy(node->name, name); /* The name counts as a reference */ node->refs++; return (0); } /* * Find a node by absolute name. The name should NOT end with ':' * The name "." means "this node" and "[xxx]" means "the node * with ID (ie, at address) xxx". * * Returns the node if found, else NULL. */ node_p ng_findname(node_p this, const char *name) { node_p node; ng_ID_t temp; /* "." means "this node" */ if (strcmp(name, ".") == 0) return(this); /* Check for name-by-ID */ if ((temp = ng_decodeidname(name)) != 0) { return (ng_ID2node(temp)); } /* Find node by name */ LIST_FOREACH(node, &nodelist, nodes) { if (node->name != NULL && strcmp(node->name, name) == 0) break; } return (node); } /* * Decode a ID name, eg. "[f03034de]". Returns 0 if the * string is not valid, otherwise returns the value. */ static ng_ID_t ng_decodeidname(const char *name) { const int len = strlen(name); char *eptr; u_long val; /* Check for proper length, brackets, no leading junk */ if (len < 3 || name[0] != '[' || name[len - 1] != ']' || !isxdigit(name[1])) return (0); /* Decode number */ val = strtoul(name + 1, &eptr, 16); if (eptr - name != len - 1 || val == ULONG_MAX || val == 0) return ((ng_ID_t)0); return (ng_ID_t)val; } /* * Remove a name from a node. This should only be called * when shutting down and removing the node. */ void ng_unname(node_p node) { if (node->name) { FREE(node->name, M_NETGRAPH); node->name = NULL; ng_unref(node); } } /************************************************************************ Hook routines Names are not optional. Hooks are always connected, except for a brief moment within these routines. ************************************************************************/ /* * Remove a hook reference */ static void ng_unref_hook(hook_p hook) { int s; s = splhigh(); if (--hook->refs == 0) FREE(hook, M_NETGRAPH); splx(s); } /* * Add an unconnected hook to a node. Only used internally. */ static int ng_add_hook(node_p node, const char *name, hook_p *hookp) { hook_p hook; int error = 0; /* Check that the given name is good */ if (name == NULL) { TRAP_ERROR; return (EINVAL); } if (ng_findhook(node, name) != NULL) { TRAP_ERROR; return (EEXIST); } /* Allocate the hook and link it up */ MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH, M_NOWAIT | M_ZERO); if (hook == NULL) { TRAP_ERROR; return (ENOMEM); } hook->refs = 1; hook->flags = HK_INVALID; hook->node = node; node->refs++; /* each hook counts as a reference */ /* Check if the node type code has something to say about it */ if (node->type->newhook != NULL) if ((error = (*node->type->newhook)(node, hook, name)) != 0) goto fail; /* * The 'type' agrees so far, so go ahead and link it in. * We'll ask again later when we actually connect the hooks. */ LIST_INSERT_HEAD(&node->hooks, hook, hooks); node->numhooks++; /* Set hook name */ MALLOC(hook->name, char *, strlen(name) + 1, M_NETGRAPH, M_NOWAIT); if (hook->name == NULL) { error = ENOMEM; LIST_REMOVE(hook, hooks); node->numhooks--; fail: hook->node = NULL; ng_unref(node); ng_unref_hook(hook); /* this frees the hook */ return (error); } strcpy(hook->name, name); if (hookp) *hookp = hook; return (error); } /* * Connect a pair of hooks. Only used internally. */ static int ng_connect(hook_p hook1, hook_p hook2) { int error; hook1->peer = hook2; hook2->peer = hook1; /* Give each node the opportunity to veto the impending connection */ if (hook1->node->type->connect) { if ((error = (*hook1->node->type->connect) (hook1))) { ng_destroy_hook(hook1); /* also zaps hook2 */ return (error); } } if (hook2->node->type->connect) { if ((error = (*hook2->node->type->connect) (hook2))) { ng_destroy_hook(hook2); /* also zaps hook1 */ return (error); } } hook1->flags &= ~HK_INVALID; hook2->flags &= ~HK_INVALID; return (0); } /* * Find a hook * * Node types may supply their own optimized routines for finding * hooks. If none is supplied, we just do a linear search. */ hook_p ng_findhook(node_p node, const char *name) { hook_p hook; if (node->type->findhook != NULL) return (*node->type->findhook)(node, name); LIST_FOREACH(hook, &node->hooks, hooks) { if (hook->name != NULL && strcmp(hook->name, name) == 0) return (hook); } return (NULL); } /* * Destroy a hook * * As hooks are always attached, this really destroys two hooks. * The one given, and the one attached to it. Disconnect the hooks * from each other first. */ void ng_destroy_hook(hook_p hook) { hook_p peer = hook->peer; hook->flags |= HK_INVALID; /* as soon as possible */ if (peer) { peer->flags |= HK_INVALID; /* as soon as possible */ hook->peer = NULL; peer->peer = NULL; ng_disconnect_hook(peer); } ng_disconnect_hook(hook); } /* * Notify the node of the hook's demise. This may result in more actions * (e.g. shutdown) but we don't do that ourselves and don't know what * happens there. If there is no appropriate handler, then just remove it * (and decrement the reference count of it's node which in turn might * make something happen). */ static void ng_disconnect_hook(hook_p hook) { node_p node = hook->node; /* * Remove the hook from the node's list to avoid possible recursion * in case the disconnection results in node shutdown. */ LIST_REMOVE(hook, hooks); node->numhooks--; if (node->type->disconnect) { /* * The type handler may elect to destroy the peer so don't * trust its existance after this point. */ (*node->type->disconnect) (hook); } ng_unref(node); /* might be the last reference */ if (hook->name) FREE(hook->name, M_NETGRAPH); hook->node = NULL; /* may still be referenced elsewhere */ ng_unref_hook(hook); } /* * Take two hooks on a node and merge the connection so that the given node * is effectively bypassed. */ int ng_bypass(hook_p hook1, hook_p hook2) { if (hook1->node != hook2->node) return (EINVAL); hook1->peer->peer = hook2->peer; hook2->peer->peer = hook1->peer; /* XXX If we ever cache methods on hooks update them as well */ hook1->peer = NULL; hook2->peer = NULL; ng_destroy_hook(hook1); ng_destroy_hook(hook2); return (0); } /* * Install a new netgraph type */ int ng_newtype(struct ng_type *tp) { const size_t namelen = strlen(tp->name); /* Check version and type name fields */ if (tp->version != NG_VERSION || namelen == 0 || namelen > NG_TYPELEN) { TRAP_ERROR; return (EINVAL); } /* Check for name collision */ if (ng_findtype(tp->name) != NULL) { TRAP_ERROR; return (EEXIST); } /* Link in new type */ LIST_INSERT_HEAD(&typelist, tp, types); tp->refs = 0; return (0); } /* * Look for a type of the name given */ struct ng_type * ng_findtype(const char *typename) { struct ng_type *type; LIST_FOREACH(type, &typelist, types) { if (strcmp(type->name, typename) == 0) break; } return (type); } /************************************************************************ Composite routines ************************************************************************/ /* * Make a peer and connect. The order is arranged to minimise * the work needed to back out in case of error. */ int ng_mkpeer(node_p node, const char *name, const char *name2, char *type) { node_p node2; hook_p hook; hook_p hook2; int error; if ((error = ng_add_hook(node, name, &hook))) return (error); if ((error = ng_make_node(type, &node2))) { ng_destroy_hook(hook); return (error); } if ((error = ng_add_hook(node2, name2, &hook2))) { ng_rmnode(node2); ng_destroy_hook(hook); return (error); } /* * Actually link the two hooks together.. on failure they are * destroyed so we don't have to do that here. */ if ((error = ng_connect(hook, hook2))) ng_rmnode(node2); return (error); } /* * Connect two nodes using the specified hooks */ int ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) { int error; hook_p hook; hook_p hook2; if ((error = ng_add_hook(node, name, &hook))) return (error); if ((error = ng_add_hook(node2, name2, &hook2))) { ng_destroy_hook(hook); return (error); } return (ng_connect(hook, hook2)); } /* * Parse and verify a string of the form: * * Such a string can refer to a specific node or a specific hook * on a specific node, depending on how you look at it. In the * latter case, the PATH component must not end in a dot. * * Both and are optional. The is a string * of hook names separated by dots. This breaks out the original * string, setting *nodep to "NODE" (or NULL if none) and *pathp * to "PATH" (or NULL if degenerate). Also, *hookp will point to * the final hook component of , if any, otherwise NULL. * * This returns -1 if the path is malformed. The char ** are optional. */ int ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) { char *node, *path, *hook; int k; /* * Extract absolute NODE, if any */ for (path = addr; *path && *path != ':'; path++); if (*path) { node = addr; /* Here's the NODE */ *path++ = '\0'; /* Here's the PATH */ /* Node name must not be empty */ if (!*node) return -1; /* A name of "." is OK; otherwise '.' not allowed */ if (strcmp(node, ".") != 0) { for (k = 0; node[k]; k++) if (node[k] == '.') return -1; } } else { node = NULL; /* No absolute NODE */ path = addr; /* Here's the PATH */ } /* Snoop for illegal characters in PATH */ for (k = 0; path[k]; k++) if (path[k] == ':') return -1; /* Check for no repeated dots in PATH */ for (k = 0; path[k]; k++) if (path[k] == '.' && path[k + 1] == '.') return -1; /* Remove extra (degenerate) dots from beginning or end of PATH */ if (path[0] == '.') path++; if (*path && path[strlen(path) - 1] == '.') path[strlen(path) - 1] = 0; /* If PATH has a dot, then we're not talking about a hook */ if (*path) { for (hook = path, k = 0; path[k]; k++) if (path[k] == '.') { hook = NULL; break; } } else path = hook = NULL; /* Done */ if (nodep) *nodep = node; if (pathp) *pathp = path; if (hookp) *hookp = hook; return (0); } /* * Given a path, which may be absolute or relative, and a starting node, * return the destination node. Compute the "return address" if desired. */ int -ng_path2node(node_p here, const char *address, node_p *destp, char **rtnp, - hook_p *lasthook) +ng_path2node(node_p here, const char *address, node_p *destp, hook_p *lasthook) { - const node_p start = here; char fullpath[NG_PATHLEN + 1]; char *nodename, *path, pbuf[2]; node_p node; char *cp; hook_p hook = NULL; /* Initialize */ - if (rtnp) - *rtnp = NULL; if (destp == NULL) return EINVAL; *destp = NULL; /* Make a writable copy of address for ng_path_parse() */ strncpy(fullpath, address, sizeof(fullpath) - 1); fullpath[sizeof(fullpath) - 1] = '\0'; /* Parse out node and sequence of hooks */ if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { TRAP_ERROR; return EINVAL; } if (path == NULL) { pbuf[0] = '.'; /* Needs to be writable */ pbuf[1] = '\0'; path = pbuf; } /* For an absolute address, jump to the starting node */ if (nodename) { node = ng_findname(here, nodename); if (node == NULL) { TRAP_ERROR; return (ENOENT); } } else node = here; /* Now follow the sequence of hooks */ for (cp = path; node != NULL && *cp != '\0'; ) { char *segment; /* * Break out the next path segment. Replace the dot we just * found with a NUL; "cp" points to the next segment (or the * NUL at the end). */ for (segment = cp; *cp != '\0'; cp++) { if (*cp == '.') { *cp++ = '\0'; break; } } /* Empty segment */ if (*segment == '\0') continue; /* We have a segment, so look for a hook by that name */ hook = ng_findhook(node, segment); /* Can't get there from here... */ if (hook == NULL || hook->peer == NULL || (hook->flags & HK_INVALID) != 0) { TRAP_ERROR; return (ENOENT); } /* Hop on over to the next node */ node = hook->peer->node; } /* If node somehow missing, fail here (probably this is not needed) */ if (node == NULL) { TRAP_ERROR; return (ENXIO); } - /* Now compute return address, i.e., the path to the sender */ - if (rtnp != NULL) { - MALLOC(*rtnp, char *, NG_NODELEN + 2, M_NETGRAPH, M_NOWAIT); - if (*rtnp == NULL) { - TRAP_ERROR; - return (ENOMEM); - } - if (start->name != NULL) - sprintf(*rtnp, "%s:", start->name); - else - sprintf(*rtnp, "[%x]:", ng_node2ID(start)); - } - /* Done */ *destp = node; if (lasthook != NULL) *lasthook = hook ? hook->peer : NULL; return (0); } /* * Call the appropriate message handler for the object. * It is up to the message handler to free the message. * If it's a generic message, handle it generically, otherwise * call the type's message handler (if it exists) * XXX (race). Remember that a queued message may reference a node * or hook that has just been invalidated. It will exist * as the queue code is holding a reference, but.. */ #define CALL_MSG_HANDLER(error, node, msg, retaddr, resp, hook) \ do { \ if((msg)->header.typecookie == NGM_GENERIC_COOKIE) { \ (error) = ng_generic_msg((node), (msg), \ (retaddr), (resp), (hook)); \ } else { \ if ((node)->type->rcvmsg != NULL) { \ (error) = (*(node)->type->rcvmsg)((node), \ (msg), (retaddr), (resp), (hook)); \ } else { \ TRAP_ERROR; \ FREE((msg), M_NETGRAPH); \ (error) = EINVAL; \ } \ } \ } while (0) /* - * Send a control message to a node + * Send a control message to a node. + * If hook is supplied, use it in preference to the address. + * If the return address is not supplied it will be set to this node. */ int ng_send_msg(node_p here, struct ng_mesg *msg, const char *address, - struct ng_mesg **rptr) + hook_p hook, char *retaddr, struct ng_mesg **rptr) { node_p dest = NULL; - char *retaddr = NULL; int error; hook_p lasthook; - /* Find the target node */ - error = ng_path2node(here, address, &dest, &retaddr, &lasthook); - if (error) { - FREE(msg, M_NETGRAPH); - return error; + /* + * Find the target node. + * If there is a HOOK argument, then use that in preference + * to the address. + */ + if (hook) { + lasthook = hook->peer; + dest = lasthook->node; + } else { + error = ng_path2node(here, address, &dest, &lasthook); + if (error) { + FREE(msg, M_NETGRAPH); + return (error); + } } + /* If the user didn't supply a return addres, assume it's "here". */ + if (retaddr == NULL) { + /* + * Now fill out the return address, + * i.e. the name/ID of the sender. (If we didn't get one) + */ + MALLOC(retaddr, char *, NG_NODELEN + 2, M_NETGRAPH, M_NOWAIT); + if (retaddr == NULL) { + TRAP_ERROR; + return (ENOMEM); + } + if (here->name != NULL) + sprintf(retaddr, "%s:", here->name); + else + sprintf(retaddr, "[%x]:", ng_node2ID(here)); + } + /* Make sure the resp field is null before we start */ if (rptr != NULL) *rptr = NULL; CALL_MSG_HANDLER(error, dest, msg, retaddr, rptr, lasthook); /* Make sure that if there is a response, it has the RESP bit set */ if ((error == 0) && rptr && *rptr) (*rptr)->header.flags |= NGF_RESP; /* * If we had a return address it is up to us to free it. They should * have taken a copy if they needed to make a delayed response. */ if (retaddr) FREE(retaddr, M_NETGRAPH); return (error); } /* * Implement the 'generic' control messages */ static int ng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook) { int error = 0; if (msg->header.typecookie != NGM_GENERIC_COOKIE) { TRAP_ERROR; FREE(msg, M_NETGRAPH); return (EINVAL); } switch (msg->header.cmd) { case NGM_SHUTDOWN: ng_rmnode(here); break; case NGM_MKPEER: { struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; if (msg->header.arglen != sizeof(*mkp)) { TRAP_ERROR; return (EINVAL); } mkp->type[sizeof(mkp->type) - 1] = '\0'; mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); break; } case NGM_CONNECT: { struct ngm_connect *const con = (struct ngm_connect *) msg->data; node_p node2; if (msg->header.arglen != sizeof(*con)) { TRAP_ERROR; return (EINVAL); } con->path[sizeof(con->path) - 1] = '\0'; con->ourhook[sizeof(con->ourhook) - 1] = '\0'; con->peerhook[sizeof(con->peerhook) - 1] = '\0'; - error = ng_path2node(here, con->path, &node2, NULL, NULL); + error = ng_path2node(here, con->path, &node2, NULL); if (error) break; error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); break; } case NGM_NAME: { struct ngm_name *const nam = (struct ngm_name *) msg->data; if (msg->header.arglen != sizeof(*nam)) { TRAP_ERROR; return (EINVAL); } nam->name[sizeof(nam->name) - 1] = '\0'; error = ng_name_node(here, nam->name); break; } case NGM_RMHOOK: { struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; hook_p hook; if (msg->header.arglen != sizeof(*rmh)) { TRAP_ERROR; return (EINVAL); } rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) ng_destroy_hook(hook); break; } case NGM_NODEINFO: { struct nodeinfo *ni; struct ng_mesg *rp; /* Get response struct */ if (resp == NULL) { error = EINVAL; break; } NG_MKRESPONSE(rp, msg, sizeof(*ni), M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } /* Fill in node info */ ni = (struct nodeinfo *) rp->data; if (here->name != NULL) strncpy(ni->name, here->name, NG_NODELEN); strncpy(ni->type, here->type->name, NG_TYPELEN); ni->id = ng_node2ID(here); ni->hooks = here->numhooks; *resp = rp; break; } case NGM_LISTHOOKS: { const int nhooks = here->numhooks; struct hooklist *hl; struct nodeinfo *ni; struct ng_mesg *rp; hook_p hook; /* Get response struct */ if (resp == NULL) { error = EINVAL; break; } NG_MKRESPONSE(rp, msg, sizeof(*hl) + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } hl = (struct hooklist *) rp->data; ni = &hl->nodeinfo; /* Fill in node info */ if (here->name) strncpy(ni->name, here->name, NG_NODELEN); strncpy(ni->type, here->type->name, NG_TYPELEN); ni->id = ng_node2ID(here); /* Cycle through the linked list of hooks */ ni->hooks = 0; LIST_FOREACH(hook, &here->hooks, hooks) { struct linkinfo *const link = &hl->link[ni->hooks]; if (ni->hooks >= nhooks) { log(LOG_ERR, "%s: number of %s changed\n", __FUNCTION__, "hooks"); break; } if ((hook->flags & HK_INVALID) != 0) continue; strncpy(link->ourhook, hook->name, NG_HOOKLEN); strncpy(link->peerhook, hook->peer->name, NG_HOOKLEN); if (hook->peer->node->name != NULL) strncpy(link->nodeinfo.name, hook->peer->node->name, NG_NODELEN); strncpy(link->nodeinfo.type, hook->peer->node->type->name, NG_TYPELEN); link->nodeinfo.id = ng_node2ID(hook->peer->node); link->nodeinfo.hooks = hook->peer->node->numhooks; ni->hooks++; } *resp = rp; break; } case NGM_LISTNAMES: case NGM_LISTNODES: { const int unnamed = (msg->header.cmd == NGM_LISTNODES); struct namelist *nl; struct ng_mesg *rp; node_p node; int num = 0; if (resp == NULL) { error = EINVAL; break; } /* Count number of nodes */ LIST_FOREACH(node, &nodelist, nodes) { if (unnamed || node->name != NULL) num++; } /* Get response struct */ if (resp == NULL) { error = EINVAL; break; } NG_MKRESPONSE(rp, msg, sizeof(*nl) + (num * sizeof(struct nodeinfo)), M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } nl = (struct namelist *) rp->data; /* Cycle through the linked list of nodes */ nl->numnames = 0; LIST_FOREACH(node, &nodelist, nodes) { struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; if (nl->numnames >= num) { log(LOG_ERR, "%s: number of %s changed\n", __FUNCTION__, "nodes"); break; } if ((node->flags & NG_INVALID) != 0) continue; if (!unnamed && node->name == NULL) continue; if (node->name != NULL) strncpy(np->name, node->name, NG_NODELEN); strncpy(np->type, node->type->name, NG_TYPELEN); np->id = ng_node2ID(node); np->hooks = node->numhooks; nl->numnames++; } *resp = rp; break; } case NGM_LISTTYPES: { struct typelist *tl; struct ng_mesg *rp; struct ng_type *type; int num = 0; if (resp == NULL) { error = EINVAL; break; } /* Count number of types */ LIST_FOREACH(type, &typelist, types) num++; /* Get response struct */ if (resp == NULL) { error = EINVAL; break; } NG_MKRESPONSE(rp, msg, sizeof(*tl) + (num * sizeof(struct typeinfo)), M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } tl = (struct typelist *) rp->data; /* Cycle through the linked list of types */ tl->numtypes = 0; LIST_FOREACH(type, &typelist, types) { struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; if (tl->numtypes >= num) { log(LOG_ERR, "%s: number of %s changed\n", __FUNCTION__, "types"); break; } strncpy(tp->type_name, type->name, NG_TYPELEN); tp->numnodes = type->refs; tl->numtypes++; } *resp = rp; break; } case NGM_BINARY2ASCII: { int bufSize = 20 * 1024; /* XXX hard coded constant */ const struct ng_parse_type *argstype; const struct ng_cmdlist *c; struct ng_mesg *rp, *binary, *ascii; /* Data area must contain a valid netgraph message */ binary = (struct ng_mesg *)msg->data; if (msg->header.arglen < sizeof(struct ng_mesg) || msg->header.arglen - sizeof(struct ng_mesg) < binary->header.arglen) { error = EINVAL; break; } /* Get a response message with lots of room */ NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } ascii = (struct ng_mesg *)rp->data; /* Copy binary message header to response message payload */ bcopy(binary, ascii, sizeof(*binary)); /* Find command by matching typecookie and command number */ for (c = here->type->cmdlist; c != NULL && c->name != NULL; c++) { if (binary->header.typecookie == c->cookie && binary->header.cmd == c->cmd) break; } if (c == NULL || c->name == NULL) { for (c = ng_generic_cmds; c->name != NULL; c++) { if (binary->header.typecookie == c->cookie && binary->header.cmd == c->cmd) break; } if (c->name == NULL) { FREE(rp, M_NETGRAPH); error = ENOSYS; break; } } /* Convert command name to ASCII */ snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), "%s", c->name); /* Convert command arguments to ASCII */ argstype = (binary->header.flags & NGF_RESP) ? c->respType : c->mesgType; if (argstype == NULL) *ascii->data = '\0'; else { if ((error = ng_unparse(argstype, (u_char *)binary->data, ascii->data, bufSize)) != 0) { FREE(rp, M_NETGRAPH); break; } } /* Return the result as struct ng_mesg plus ASCII string */ bufSize = strlen(ascii->data) + 1; ascii->header.arglen = bufSize; rp->header.arglen = sizeof(*ascii) + bufSize; *resp = rp; break; } case NGM_ASCII2BINARY: { int bufSize = 2000; /* XXX hard coded constant */ const struct ng_cmdlist *c; const struct ng_parse_type *argstype; struct ng_mesg *rp, *ascii, *binary; int off = 0; /* Data area must contain at least a struct ng_mesg + '\0' */ ascii = (struct ng_mesg *)msg->data; if (msg->header.arglen < sizeof(*ascii) + 1 || ascii->header.arglen < 1 || msg->header.arglen < sizeof(*ascii) + ascii->header.arglen) { error = EINVAL; break; } ascii->data[ascii->header.arglen - 1] = '\0'; /* Get a response message with lots of room */ NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT); if (rp == NULL) { error = ENOMEM; break; } binary = (struct ng_mesg *)rp->data; /* Copy ASCII message header to response message payload */ bcopy(ascii, binary, sizeof(*ascii)); /* Find command by matching ASCII command string */ for (c = here->type->cmdlist; c != NULL && c->name != NULL; c++) { if (strcmp(ascii->header.cmdstr, c->name) == 0) break; } if (c == NULL || c->name == NULL) { for (c = ng_generic_cmds; c->name != NULL; c++) { if (strcmp(ascii->header.cmdstr, c->name) == 0) break; } if (c->name == NULL) { FREE(rp, M_NETGRAPH); error = ENOSYS; break; } } /* Convert command name to binary */ binary->header.cmd = c->cmd; binary->header.typecookie = c->cookie; /* Convert command arguments to binary */ argstype = (binary->header.flags & NGF_RESP) ? c->respType : c->mesgType; if (argstype == NULL) bufSize = 0; else { if ((error = ng_parse(argstype, ascii->data, &off, (u_char *)binary->data, &bufSize)) != 0) { FREE(rp, M_NETGRAPH); break; } } /* Return the result */ binary->header.arglen = bufSize; rp->header.arglen = sizeof(*binary) + bufSize; *resp = rp; break; } case NGM_TEXT_CONFIG: case NGM_TEXT_STATUS: /* * This one is tricky as it passes the command down to the * actual node, even though it is a generic type command. * This means we must assume that the msg is already freed * when control passes back to us. */ if (resp == NULL) { error = EINVAL; break; } if (here->type->rcvmsg != NULL) return((*here->type->rcvmsg)(here, msg, retaddr, resp, lasthook)); /* Fall through if rcvmsg not supported */ default: TRAP_ERROR; error = EINVAL; } FREE(msg, M_NETGRAPH); return (error); } /* * Send a data packet to a node. If the recipient has no * 'receive data' method, then silently discard the packet. + * The receiving node may elect to put the data onto the netgraph + * NETISR queue for later delivery. It may do this because it knows there + * is some recursion and wishes to unwind the stack, or because it has + * some suspicion that it is being called at (say) splimp instead of + * splnet. */ int ng_send_data(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { ng_rcvdata_t *rcvdata; - int error; CHECK_DATA_MBUF(m); - if (hook && (hook->flags & HK_INVALID) == 0) { - rcvdata = hook->peer->node->type->rcvdata; - if (rcvdata != NULL) - error = (*rcvdata)(hook->peer, m, meta, - ret_m, ret_meta); - else { - error = 0; - NG_FREE_DATA(m, meta); - } - } else { + if ((hook == NULL) + || ((hook->flags & HK_INVALID) != 0) + || ((rcvdata = hook->peer->node->type->rcvdata) == NULL)) { TRAP_ERROR; - error = ENOTCONN; NG_FREE_DATA(m, meta); + return (ENOTCONN); } - return (error); + if (hook->peer->flags & HK_QUEUE) { + return (ng_queue_data(hook, m, meta)); + } + return ( (*rcvdata)(hook->peer, m, meta, ret_m, ret_meta, resp)); } /* - * Send a queued data packet to a node. If the recipient has no - * 'receive queued data' method, then try the 'receive data' method above. + * Send a queued data packet to a node. + * + * This is meant for data that is being dequeued and should therefore NOT + * be queued again. It ignores the queue flag and should NOT be called + * outside of this file. (thus it is static) */ -int -ng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) +static int +ng_send_data_dont_queue(hook_p hook, struct mbuf *m, meta_p meta, + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { ng_rcvdata_t *rcvdata; - int error; CHECK_DATA_MBUF(m); - if (hook && (hook->flags & HK_INVALID) == 0) { - rcvdata = hook->peer->node->type->rcvdataq; - if (rcvdata != NULL) - error = (*rcvdata)(hook->peer, m, meta, - ret_m, ret_meta); - else { - rcvdata = hook->peer->node->type->rcvdata; - if (rcvdata != NULL) { - error = (*rcvdata)(hook->peer, m, meta, - ret_m, ret_meta); - } else { - error = 0; - NG_FREE_DATA(m, meta); - } - } - } else { + if ((hook == NULL) + || ((hook->flags & HK_INVALID) != 0) + || ((rcvdata = hook->peer->node->type->rcvdata) == NULL)) { TRAP_ERROR; - error = ENOTCONN; NG_FREE_DATA(m, meta); - } - return (error); + return (ENOTCONN); + } + return ((*rcvdata)(hook->peer, m, meta, ret_m, ret_meta, resp)); } /* * Copy a 'meta'. * * Returns new meta, or NULL if original meta is NULL or ENOMEM. */ meta_p ng_copy_meta(meta_p meta) { meta_p meta2; if (meta == NULL) return (NULL); MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH, M_NOWAIT); if (meta2 == NULL) return (NULL); meta2->allocated_len = meta->used_len; bcopy(meta, meta2, meta->used_len); return (meta2); } /************************************************************************ Module routines ************************************************************************/ /* * Handle the loading/unloading of a netgraph node type module */ int ng_mod_event(module_t mod, int event, void *data) { struct ng_type *const type = data; int s, error = 0; switch (event) { case MOD_LOAD: /* Register new netgraph node type */ s = splnet(); if ((error = ng_newtype(type)) != 0) { splx(s); break; } /* Call type specific code */ if (type->mod_event != NULL) if ((error = (*type->mod_event)(mod, event, data)) != 0) LIST_REMOVE(type, types); splx(s); break; case MOD_UNLOAD: s = splnet(); if (type->refs != 0) /* make sure no nodes exist! */ error = EBUSY; else { if (type->mod_event != NULL) { /* check with type */ error = (*type->mod_event)(mod, event, data); if (error != 0) { /* type refuses.. */ splx(s); break; } } LIST_REMOVE(type, types); } splx(s); break; default: if (type->mod_event != NULL) error = (*type->mod_event)(mod, event, data); else error = 0; /* XXX ? */ break; } return (error); } /* * Handle loading and unloading for this code. * The only thing we need to link into is the NETISR strucure. */ static int ngb_mod_event(module_t mod, int event, void *data) { int s, error = 0; switch (event) { case MOD_LOAD: /* Register line discipline */ s = splimp(); error = register_netisr(NETISR_NETGRAPH, ngintr); splx(s); break; case MOD_UNLOAD: /* You cant unload it because an interface may be using it. */ error = EBUSY; break; default: error = EOPNOTSUPP; break; } return (error); } static moduledata_t netgraph_mod = { "netgraph", ngb_mod_event, (NULL) }; DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); /************************************************************************ Queueing routines ************************************************************************/ /* The structure for queueing across ISR switches */ struct ng_queue_entry { u_long flags; struct ng_queue_entry *next; union { struct { hook_p da_hook; /* target hook */ struct mbuf *da_m; meta_p da_meta; } data; struct { struct ng_mesg *msg_msg; node_p msg_node; - void *msg_retaddr; hook_p msg_lasthook; + char *msg_retaddr; } msg; } body; }; #define NGQF_DATA 0x01 /* the queue element is data */ #define NGQF_MESG 0x02 /* the queue element is a message */ static struct ng_queue_entry *ngqbase; /* items to be unqueued */ static struct ng_queue_entry *ngqlast; /* last item queued */ static const int ngqroom = 64; /* max items to queue */ static int ngqsize; /* number of items in queue */ static struct ng_queue_entry *ngqfree; /* free ones */ static const int ngqfreemax = 16;/* cache at most this many */ static int ngqfreesize; /* number of cached entries */ /* * Get a queue entry */ static struct ng_queue_entry * ng_getqblk(void) { register struct ng_queue_entry *q; int s; /* Could be guarding against tty ints or whatever */ s = splhigh(); /* Try get a cached queue block, or else allocate a new one */ if ((q = ngqfree) == NULL) { splx(s); if (ngqsize < ngqroom) { /* don't worry about races */ MALLOC(q, struct ng_queue_entry *, sizeof(*q), M_NETGRAPH, M_NOWAIT); } } else { ngqfree = q->next; ngqfreesize--; splx(s); } return (q); } /* * Release a queue entry */ #define RETURN_QBLK(q) \ do { \ int s; \ if (ngqfreesize < ngqfreemax) { /* don't worry about races */ \ s = splhigh(); \ (q)->next = ngqfree; \ ngqfree = (q); \ ngqfreesize++; \ splx(s); \ } else { \ FREE((q), M_NETGRAPH); \ } \ } while (0) /* * Running at a raised (but we don't know which) processor priority level, * put the data onto a queue to be picked up by another PPL (probably splnet) */ int ng_queue_data(hook_p hook, struct mbuf *m, meta_p meta) { struct ng_queue_entry *q; int s; if (hook == NULL) { NG_FREE_DATA(m, meta); return (0); } if ((q = ng_getqblk()) == NULL) { NG_FREE_DATA(m, meta); return (ENOBUFS); } /* Fill out the contents */ q->flags = NGQF_DATA; q->next = NULL; q->body.data.da_hook = hook; q->body.data.da_m = m; q->body.data.da_meta = meta; s = splhigh(); /* protect refs and queue */ hook->refs++; /* don't let it go away while on the queue */ /* Put it on the queue */ if (ngqbase) { ngqlast->next = q; } else { ngqbase = q; } ngqlast = q; ngqsize++; splx(s); /* Schedule software interrupt to handle it later */ schednetisr(NETISR_NETGRAPH); return (0); } /* * Running at a raised (but we don't know which) processor priority level, * put the msg onto a queue to be picked up by another PPL (probably splnet) + * Either specify an address, or a hook to traverse. + * The return address can be specified, or it will be pointed at this node. */ int -ng_queue_msg(node_p here, struct ng_mesg *msg, const char *address) +ng_queue_msg(node_p here, struct ng_mesg *msg, const char *address, hook_p hook,char *retaddr) { register struct ng_queue_entry *q; int s; node_p dest = NULL; - char *retaddr = NULL; int error; hook_p lasthook = NULL; - /* Find the target node. */ - error = ng_path2node(here, address, &dest, &retaddr, &lasthook); - if (error) { - FREE(msg, M_NETGRAPH); - return (error); + /* + * Find the target node. + * If there is a HOOK argument, then use that in preference + * to the address. + */ + if (hook) { + lasthook = hook->peer; + dest = lasthook->node; + } else { + error = ng_path2node(here, address, &dest, &lasthook); + if (error) { + FREE(msg, M_NETGRAPH); + return (error); + } } + + if (retaddr == NULL) { + /* + * Now fill out the return address, + * i.e. the name/ID of the sender. (If we didn't get one) + */ + MALLOC(retaddr, char *, NG_NODELEN + 2, M_NETGRAPH, M_NOWAIT); + if (retaddr == NULL) { + TRAP_ERROR; + return (ENOMEM); + } + if (here->name != NULL) + sprintf(retaddr, "%s:", here->name); + else + sprintf(retaddr, "[%x]:", ng_node2ID(here)); + } + if ((q = ng_getqblk()) == NULL) { FREE(msg, M_NETGRAPH); if (retaddr) FREE(retaddr, M_NETGRAPH); return (ENOBUFS); } /* Fill out the contents */ q->flags = NGQF_MESG; q->next = NULL; q->body.msg.msg_node = dest; q->body.msg.msg_msg = msg; - q->body.msg.msg_retaddr = retaddr; - q->body.msg.msg_lasthook = lasthook; + q->body.msg.msg_retaddr = retaddr; /* XXX malloc'd, give it away */ + q->body.msg.msg_lasthook = lasthook; /* XXX needs reference */ s = splhigh(); /* protect refs and queue */ dest->refs++; /* don't let it go away while on the queue */ if (lasthook) lasthook->refs++; /* same for the hook */ /* Put it on the queue */ if (ngqbase) { ngqlast->next = q; } else { ngqbase = q; } ngqlast = q; ngqsize++; splx(s); /* Schedule software interrupt to handle it later */ schednetisr(NETISR_NETGRAPH); return (0); } /* * Pick an item off the queue, process it, and dispose of the queue entry. * Should be running at splnet. */ static void ngintr(void) { hook_p hook; struct ng_queue_entry *ngq; struct mbuf *m; meta_p meta; void *retaddr; struct ng_mesg *msg; node_p node; int error = 0; int s; while (1) { s = splhigh(); if ((ngq = ngqbase)) { ngqbase = ngq->next; ngqsize--; } splx(s); if (ngq == NULL) return; switch (ngq->flags) { case NGQF_DATA: hook = ngq->body.data.da_hook; m = ngq->body.data.da_m; meta = ngq->body.data.da_meta; RETURN_QBLK(ngq); - NG_SEND_DATAQ(error, hook, m, meta); + ng_send_data_dont_queue(hook, m, meta, + NULL, NULL, NULL); + m = NULL; + meta = NULL; ng_unref_hook(hook); break; case NGQF_MESG: node = ngq->body.msg.msg_node; msg = ngq->body.msg.msg_msg; retaddr = ngq->body.msg.msg_retaddr; hook = ngq->body.msg.msg_lasthook; RETURN_QBLK(ngq); if (hook) { if ((hook->flags & HK_INVALID) != 0) { /* If the hook has been zapped then we can't use it */ ng_unref_hook(hook); hook = NULL; } } /* similarly, if the node is a zombie.. */ if (node->flags & NG_INVALID) { FREE(msg, M_NETGRAPH); } else { CALL_MSG_HANDLER(error, node, msg, retaddr, NULL, hook); } if (hook) ng_unref_hook(hook); ng_unref(node); if (retaddr) FREE(retaddr, M_NETGRAPH); break; default: RETURN_QBLK(ngq); } } } Index: head/sys/netgraph/ng_bpf.c =================================================================== --- head/sys/netgraph/ng_bpf.c (revision 69921) +++ head/sys/netgraph/ng_bpf.c (revision 69922) @@ -1,502 +1,501 @@ /* * ng_bpf.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, GUARANBPF, 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_bpf.c,v 1.3 1999/12/03 20:30:23 archie Exp $ */ /* * BPF NETGRAPH NODE TYPE * * This node type accepts any number of hook connections. With each hook * is associated a bpf(4) filter program, and two hook names (each possibly * the empty string). Incoming packets are compared against the filter; * matching packets are delivered out the first named hook (or dropped if * the empty string), and non-matching packets are delivered out the second * named hook (or dropped if the empty string). * * Each hook also keeps statistics about how many packets have matched, etc. */ #include #include #include #include #include #include #include #include #include #include #include #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) #define ERROUT(x) do { error = (x); goto done; } while (0) /* Per hook private info */ struct ng_bpf_hookinfo { node_p node; hook_p hook; struct ng_bpf_hookprog *prog; struct ng_bpf_hookstat stats; }; typedef struct ng_bpf_hookinfo *hinfo_p; /* Netgraph methods */ static ng_constructor_t ng_bpf_constructor; static ng_rcvmsg_t ng_bpf_rcvmsg; static ng_shutdown_t ng_bpf_rmnode; static ng_newhook_t ng_bpf_newhook; static ng_rcvdata_t ng_bpf_rcvdata; static ng_disconnect_t ng_bpf_disconnect; /* Internal helper functions */ static int ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp); /* Parse type for one struct bfp_insn */ static const struct ng_parse_struct_info ng_bpf_insn_type_info = { { { "code", &ng_parse_hint16_type }, { "jt", &ng_parse_uint8_type }, { "jf", &ng_parse_uint8_type }, { "k", &ng_parse_uint32_type }, { NULL } } }; static const struct ng_parse_type ng_bpf_insn_type = { &ng_parse_struct_type, &ng_bpf_insn_type_info }; /* Parse type for the field 'bpf_prog' in struct ng_bpf_hookprog */ static int ng_bpf_hookprogary_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { const struct ng_bpf_hookprog *hp; hp = (const struct ng_bpf_hookprog *) (buf - OFFSETOF(struct ng_bpf_hookprog, bpf_prog)); return hp->bpf_prog_len; } static const struct ng_parse_array_info ng_bpf_hookprogary_info = { &ng_bpf_insn_type, &ng_bpf_hookprogary_getLength, NULL }; static const struct ng_parse_type ng_bpf_hookprogary_type = { &ng_parse_array_type, &ng_bpf_hookprogary_info }; /* Parse type for struct ng_bpf_hookprog */ static const struct ng_parse_struct_info ng_bpf_hookprog_type_info = NG_BPF_HOOKPROG_TYPE_INFO(&ng_bpf_hookprogary_type); static const struct ng_parse_type ng_bpf_hookprog_type = { &ng_parse_struct_type, &ng_bpf_hookprog_type_info }; /* Parse type for struct ng_bpf_hookstat */ static const struct ng_parse_struct_info ng_bpf_hookstat_type_info = NG_BPF_HOOKSTAT_TYPE_INFO; static const struct ng_parse_type ng_bpf_hookstat_type = { &ng_parse_struct_type, &ng_bpf_hookstat_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_bpf_cmdlist[] = { { NGM_BPF_COOKIE, NGM_BPF_SET_PROGRAM, "setprogram", &ng_bpf_hookprog_type, NULL }, { NGM_BPF_COOKIE, NGM_BPF_GET_PROGRAM, "getprogram", &ng_parse_hookbuf_type, &ng_bpf_hookprog_type }, { NGM_BPF_COOKIE, NGM_BPF_GET_STATS, "getstats", &ng_parse_hookbuf_type, &ng_bpf_hookstat_type }, { NGM_BPF_COOKIE, NGM_BPF_CLR_STATS, "clrstats", &ng_parse_hookbuf_type, NULL }, { NGM_BPF_COOKIE, NGM_BPF_GETCLR_STATS, "getclrstats", &ng_parse_hookbuf_type, &ng_bpf_hookstat_type }, { 0 } }; /* Netgraph type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_BPF_NODE_TYPE, NULL, ng_bpf_constructor, ng_bpf_rcvmsg, ng_bpf_rmnode, ng_bpf_newhook, NULL, NULL, ng_bpf_rcvdata, - ng_bpf_rcvdata, ng_bpf_disconnect, ng_bpf_cmdlist }; NETGRAPH_INIT(bpf, &typestruct); /* Default BPF program for a hook that matches nothing */ static const struct ng_bpf_hookprog ng_bpf_default_prog = { { '\0' }, /* to be filled in at hook creation time */ { '\0' }, { '\0' }, 1, { BPF_STMT(BPF_RET+BPF_K, 0) } }; /* * Node constructor * * We don't keep any per-node private data */ static int ng_bpf_constructor(node_p *nodep) { int error = 0; if ((error = ng_make_node_common(&typestruct, nodep))) return (error); (*nodep)->private = NULL; return (0); } /* * Add a hook */ static int ng_bpf_newhook(node_p node, hook_p hook, const char *name) { hinfo_p hip; int error; /* Create hook private structure */ MALLOC(hip, hinfo_p, sizeof(*hip), M_NETGRAPH, M_NOWAIT | M_ZERO); if (hip == NULL) return (ENOMEM); hip->hook = hook; hook->private = hip; hip->node = node; /* Attach the default BPF program */ if ((error = ng_bpf_setprog(hook, &ng_bpf_default_prog)) != 0) { FREE(hip, M_NETGRAPH); hook->private = NULL; return (error); } /* Set hook name */ strncpy(hip->prog->thisHook, name, sizeof(hip->prog->thisHook) - 1); hip->prog->thisHook[sizeof(hip->prog->thisHook) - 1] = '\0'; return (0); } /* * Receive a control message */ static int ng_bpf_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_BPF_COOKIE: switch (msg->header.cmd) { case NGM_BPF_SET_PROGRAM: { struct ng_bpf_hookprog *const hp = (struct ng_bpf_hookprog *)msg->data; hook_p hook; /* Sanity check */ if (msg->header.arglen < sizeof(*hp) || msg->header.arglen != NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)) ERROUT(EINVAL); /* Find hook */ if ((hook = ng_findhook(node, hp->thisHook)) == NULL) ERROUT(ENOENT); /* Set new program */ if ((error = ng_bpf_setprog(hook, hp)) != 0) ERROUT(error); break; } case NGM_BPF_GET_PROGRAM: { struct ng_bpf_hookprog *hp; hook_p hook; /* Sanity check */ if (msg->header.arglen == 0) ERROUT(EINVAL); msg->data[msg->header.arglen - 1] = '\0'; /* Find hook */ if ((hook = ng_findhook(node, msg->data)) == NULL) ERROUT(ENOENT); /* Build response */ hp = ((hinfo_p)hook->private)->prog; NG_MKRESPONSE(resp, msg, NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); bcopy(hp, resp->data, NG_BPF_HOOKPROG_SIZE(hp->bpf_prog_len)); break; } case NGM_BPF_GET_STATS: case NGM_BPF_CLR_STATS: case NGM_BPF_GETCLR_STATS: { struct ng_bpf_hookstat *stats; hook_p hook; /* Sanity check */ if (msg->header.arglen == 0) ERROUT(EINVAL); msg->data[msg->header.arglen - 1] = '\0'; /* Find hook */ if ((hook = ng_findhook(node, msg->data)) == NULL) ERROUT(ENOENT); stats = &((hinfo_p)hook->private)->stats; /* Build response (if desired) */ if (msg->header.cmd != NGM_BPF_CLR_STATS) { NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); bcopy(stats, resp->data, sizeof(*stats)); } /* Clear stats (if desired) */ if (msg->header.cmd != NGM_BPF_GET_STATS) bzero(stats, sizeof(*stats)); break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /* * Receive data on a hook * * Apply the filter, and then drop or forward packet as appropriate. */ static int ng_bpf_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const hinfo_p hip = hook->private; int totlen = m->m_pkthdr.len; int needfree = 0, error = 0; u_char *data, buf[256]; hinfo_p dhip; hook_p dest; u_int len; /* Update stats on incoming hook */ hip->stats.recvFrames++; hip->stats.recvOctets += totlen; /* Need to put packet in contiguous memory for bpf */ if (m->m_next != NULL) { if (totlen > sizeof(buf)) { MALLOC(data, u_char *, totlen, M_NETGRAPH, M_NOWAIT); if (data == NULL) { NG_FREE_DATA(m, meta); return (ENOMEM); } needfree = 1; } else data = buf; m_copydata(m, 0, totlen, (caddr_t)data); } else data = mtod(m, u_char *); /* Run packet through filter */ len = bpf_filter(hip->prog->bpf_prog, data, totlen, totlen); if (needfree) FREE(data, M_NETGRAPH); /* See if we got a match and find destination hook */ if (len > 0) { /* Update stats */ hip->stats.recvMatchFrames++; hip->stats.recvMatchOctets += totlen; /* Truncate packet length if required by the filter */ if (len < totlen) { m_adj(m, -(totlen - len)); totlen -= len; } dest = ng_findhook(hip->node, hip->prog->ifMatch); } else dest = ng_findhook(hip->node, hip->prog->ifNotMatch); if (dest == NULL) { NG_FREE_DATA(m, meta); return (0); } /* Deliver frame out destination hook */ dhip = (hinfo_p)dest->private; dhip->stats.xmitOctets += totlen; dhip->stats.xmitFrames++; NG_SEND_DATA(error, dest, m, meta); return (error); } /* * Shutdown processing */ static int ng_bpf_rmnode(node_p node) { node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); ng_unref(node); return (0); } /* * Hook disconnection */ static int ng_bpf_disconnect(hook_p hook) { const hinfo_p hip = hook->private; KASSERT(hip != NULL, ("%s: null info", __FUNCTION__)); FREE(hip->prog, M_NETGRAPH); bzero(hip, sizeof(*hip)); FREE(hip, M_NETGRAPH); hook->private = NULL; /* for good measure */ if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } /************************************************************************ HELPER STUFF ************************************************************************/ /* * Set the BPF program associated with a hook */ static int ng_bpf_setprog(hook_p hook, const struct ng_bpf_hookprog *hp0) { const hinfo_p hip = hook->private; struct ng_bpf_hookprog *hp; int size; /* Check program for validity */ if (!bpf_validate(hp0->bpf_prog, hp0->bpf_prog_len)) return (EINVAL); /* Make a copy of the program */ size = NG_BPF_HOOKPROG_SIZE(hp0->bpf_prog_len); MALLOC(hp, struct ng_bpf_hookprog *, size, M_NETGRAPH, M_NOWAIT); if (hp == NULL) return (ENOMEM); bcopy(hp0, hp, size); /* Free previous program, if any, and assign new one */ if (hip->prog != NULL) FREE(hip->prog, M_NETGRAPH); hip->prog = hp; return (0); } Index: head/sys/netgraph/ng_bridge.c =================================================================== --- head/sys/netgraph/ng_bridge.c (revision 69921) +++ head/sys/netgraph/ng_bridge.c (revision 69922) @@ -1,1010 +1,1009 @@ /* * ng_bridge.c * * Copyright (c) 2000 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$ */ /* * ng_bridge(4) netgraph node type * * The node performs standard intelligent Ethernet bridging over * each of its connected hooks, or links. A simple loop detection * algorithm is included which disables a link for priv->conf.loopTimeout * seconds when a host is seen to have jumped from one link to * another within priv->conf.minStableAge seconds. * * We keep a hashtable that maps Ethernet addresses to host info, * which is contained in struct ng_bridge_host's. These structures * tell us on which link the host may be found. A host's entry will * expire after priv->conf.maxStaleness seconds. * * This node is optimzed for stable networks, where machines jump * from one port to the other only rarely. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Per-link private data */ struct ng_bridge_link { hook_p hook; /* netgraph hook */ u_int16_t loopCount; /* loop ignore timer */ struct ng_bridge_link_stats stats; /* link stats */ }; /* Per-node private data */ struct ng_bridge_private { struct ng_bridge_bucket *tab; /* hash table bucket array */ struct ng_bridge_link *links[NG_BRIDGE_MAX_LINKS]; struct ng_bridge_config conf; /* node configuration */ node_p node; /* netgraph node */ u_int numHosts; /* num entries in table */ u_int numBuckets; /* num buckets in table */ u_int hashMask; /* numBuckets - 1 */ int numLinks; /* num connected links */ struct callout timer; /* one second periodic timer */ }; typedef struct ng_bridge_private *priv_p; /* Information about a host, stored in a hash table entry */ struct ng_bridge_hent { struct ng_bridge_host host; /* actual host info */ SLIST_ENTRY(ng_bridge_hent) next; /* next entry in bucket */ }; /* Hash table bucket declaration */ SLIST_HEAD(ng_bridge_bucket, ng_bridge_hent); /* Netgraph node methods */ static ng_constructor_t ng_bridge_constructor; static ng_rcvmsg_t ng_bridge_rcvmsg; static ng_shutdown_t ng_bridge_rmnode; static ng_newhook_t ng_bridge_newhook; static ng_rcvdata_t ng_bridge_rcvdata; static ng_disconnect_t ng_bridge_disconnect; /* Other internal functions */ static struct ng_bridge_host *ng_bridge_get(priv_p priv, const u_char *addr); static int ng_bridge_put(priv_p priv, const u_char *addr, int linkNum); static void ng_bridge_rehash(priv_p priv); static void ng_bridge_remove_hosts(priv_p priv, int linkNum); static void ng_bridge_timeout(void *arg); static const char *ng_bridge_nodename(node_p node); /* Ethernet broadcast */ static const u_char ng_bridge_bcast_addr[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /* Store each hook's link number in the private field */ #define LINK_NUM(hook) (*(u_int16_t *)(&(hook)->private)) /* Compare Ethernet addresses using 32 and 16 bit words instead of bytewise */ #define ETHER_EQUAL(a,b) (((const u_int32_t *)(a))[0] \ == ((const u_int32_t *)(b))[0] \ && ((const u_int16_t *)(a))[2] \ == ((const u_int16_t *)(b))[2]) /* Minimum and maximum number of hash buckets. Must be a power of two. */ #define MIN_BUCKETS (1 << 5) /* 32 */ #define MAX_BUCKETS (1 << 14) /* 16384 */ /* Configuration default values */ #define DEFAULT_LOOP_TIMEOUT 60 #define DEFAULT_MAX_STALENESS (15 * 60) /* same as ARP timeout */ #define DEFAULT_MIN_STABLE_AGE 1 /****************************************************************** NETGRAPH PARSE TYPES ******************************************************************/ /* * How to determine the length of the table returned by NGM_BRIDGE_GET_TABLE */ static int ng_bridge_getTableLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { const struct ng_bridge_host_ary *const hary = (const struct ng_bridge_host_ary *)(buf - sizeof(u_int32_t)); return hary->numHosts; } /* Parse type for struct ng_bridge_host_ary */ static const struct ng_parse_struct_info ng_bridge_host_type_info = NG_BRIDGE_HOST_TYPE_INFO(&ng_ether_enaddr_type); static const struct ng_parse_type ng_bridge_host_type = { &ng_parse_struct_type, &ng_bridge_host_type_info }; static const struct ng_parse_array_info ng_bridge_hary_type_info = { &ng_bridge_host_type, ng_bridge_getTableLength }; static const struct ng_parse_type ng_bridge_hary_type = { &ng_parse_array_type, &ng_bridge_hary_type_info }; static const struct ng_parse_struct_info ng_bridge_host_ary_type_info = NG_BRIDGE_HOST_ARY_TYPE_INFO(&ng_bridge_hary_type); static const struct ng_parse_type ng_bridge_host_ary_type = { &ng_parse_struct_type, &ng_bridge_host_ary_type_info }; /* Parse type for struct ng_bridge_config */ static const struct ng_parse_fixedarray_info ng_bridge_ipfwary_type_info = { &ng_parse_uint8_type, NG_BRIDGE_MAX_LINKS }; static const struct ng_parse_type ng_bridge_ipfwary_type = { &ng_parse_fixedarray_type, &ng_bridge_ipfwary_type_info }; static const struct ng_parse_struct_info ng_bridge_config_type_info = NG_BRIDGE_CONFIG_TYPE_INFO(&ng_bridge_ipfwary_type); static const struct ng_parse_type ng_bridge_config_type = { &ng_parse_struct_type, &ng_bridge_config_type_info }; /* Parse type for struct ng_bridge_link_stat */ static const struct ng_parse_struct_info ng_bridge_stats_type_info = NG_BRIDGE_STATS_TYPE_INFO; static const struct ng_parse_type ng_bridge_stats_type = { &ng_parse_struct_type, &ng_bridge_stats_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_bridge_cmdlist[] = { { NGM_BRIDGE_COOKIE, NGM_BRIDGE_SET_CONFIG, "setconfig", &ng_bridge_config_type, NULL }, { NGM_BRIDGE_COOKIE, NGM_BRIDGE_GET_CONFIG, "getconfig", NULL, &ng_bridge_config_type }, { NGM_BRIDGE_COOKIE, NGM_BRIDGE_RESET, "reset", NULL, NULL }, { NGM_BRIDGE_COOKIE, NGM_BRIDGE_GET_STATS, "getstats", &ng_parse_uint32_type, &ng_bridge_stats_type }, { NGM_BRIDGE_COOKIE, NGM_BRIDGE_CLR_STATS, "clrstats", &ng_parse_uint32_type, NULL }, { NGM_BRIDGE_COOKIE, NGM_BRIDGE_GETCLR_STATS, "getclrstats", &ng_parse_uint32_type, &ng_bridge_stats_type }, { NGM_BRIDGE_COOKIE, NGM_BRIDGE_GET_TABLE, "gettable", NULL, &ng_bridge_host_ary_type }, { 0 } }; /* Node type descriptor */ static struct ng_type ng_bridge_typestruct = { NG_VERSION, NG_BRIDGE_NODE_TYPE, NULL, ng_bridge_constructor, ng_bridge_rcvmsg, ng_bridge_rmnode, ng_bridge_newhook, NULL, NULL, ng_bridge_rcvdata, - ng_bridge_rcvdata, ng_bridge_disconnect, ng_bridge_cmdlist, }; NETGRAPH_INIT(bridge, &ng_bridge_typestruct); /* Depend on ng_ether so we can use the Ethernet parse type */ MODULE_DEPEND(ng_bridge, ng_ether, 1, 1, 1); /****************************************************************** NETGRAPH NODE METHODS ******************************************************************/ /* * Node constructor */ static int ng_bridge_constructor(node_p *nodep) { priv_p priv; int error; /* Allocate and initialize private info */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv == NULL) return (ENOMEM); callout_init(&priv->timer, 0); /* Allocate and initialize hash table, etc. */ MALLOC(priv->tab, struct ng_bridge_bucket *, MIN_BUCKETS * sizeof(*priv->tab), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv->tab == NULL) { FREE(priv, M_NETGRAPH); return (ENOMEM); } priv->numBuckets = MIN_BUCKETS; priv->hashMask = MIN_BUCKETS - 1; priv->conf.debugLevel = 1; priv->conf.loopTimeout = DEFAULT_LOOP_TIMEOUT; priv->conf.maxStaleness = DEFAULT_MAX_STALENESS; priv->conf.minStableAge = DEFAULT_MIN_STABLE_AGE; /* Call superclass constructor */ if ((error = ng_make_node_common(&ng_bridge_typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; priv->node = *nodep; /* Start timer by faking a timeout event */ (*nodep)->refs++; ng_bridge_timeout(*nodep); return (0); } /* * Method for attaching a new hook */ static int ng_bridge_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; /* Check for a link hook */ if (strncmp(name, NG_BRIDGE_HOOK_LINK_PREFIX, strlen(NG_BRIDGE_HOOK_LINK_PREFIX)) == 0) { const char *cp; char *eptr; u_long linkNum; cp = name + strlen(NG_BRIDGE_HOOK_LINK_PREFIX); if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) return (EINVAL); linkNum = strtoul(cp, &eptr, 10); if (*eptr != '\0' || linkNum >= NG_BRIDGE_MAX_LINKS) return (EINVAL); if (priv->links[linkNum] != NULL) return (EISCONN); MALLOC(priv->links[linkNum], struct ng_bridge_link *, sizeof(*priv->links[linkNum]), M_NETGRAPH, M_NOWAIT|M_ZERO); if (priv->links[linkNum] == NULL) return (ENOMEM); priv->links[linkNum]->hook = hook; LINK_NUM(hook) = linkNum; priv->numLinks++; return (0); } /* Unknown hook name */ return (EINVAL); } /* * Receive a control message */ static int ng_bridge_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { const priv_p priv = node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_BRIDGE_COOKIE: switch (msg->header.cmd) { case NGM_BRIDGE_GET_CONFIG: { struct ng_bridge_config *conf; NG_MKRESPONSE(resp, msg, sizeof(struct ng_bridge_config), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } conf = (struct ng_bridge_config *)resp->data; *conf = priv->conf; /* no sanity checking needed */ break; } case NGM_BRIDGE_SET_CONFIG: { struct ng_bridge_config *conf; int i; if (msg->header.arglen != sizeof(struct ng_bridge_config)) { error = EINVAL; break; } conf = (struct ng_bridge_config *)msg->data; priv->conf = *conf; for (i = 0; i < NG_BRIDGE_MAX_LINKS; i++) priv->conf.ipfw[i] = !!priv->conf.ipfw[i]; break; } case NGM_BRIDGE_RESET: { int i; /* Flush all entries in the hash table */ ng_bridge_remove_hosts(priv, -1); /* Reset all loop detection counters and stats */ for (i = 0; i < NG_BRIDGE_MAX_LINKS; i++) { if (priv->links[i] == NULL) continue; priv->links[i]->loopCount = 0; bzero(&priv->links[i]->stats, sizeof(priv->links[i]->stats)); } break; } case NGM_BRIDGE_GET_STATS: case NGM_BRIDGE_CLR_STATS: case NGM_BRIDGE_GETCLR_STATS: { struct ng_bridge_link *link; int linkNum; /* Get link number */ if (msg->header.arglen != sizeof(u_int32_t)) { error = EINVAL; break; } linkNum = *((u_int32_t *)msg->data); if (linkNum < 0 || linkNum >= NG_BRIDGE_MAX_LINKS) { error = EINVAL; break; } if ((link = priv->links[linkNum]) == NULL) { error = ENOTCONN; break; } /* Get/clear stats */ if (msg->header.cmd != NGM_BRIDGE_CLR_STATS) { NG_MKRESPONSE(resp, msg, sizeof(link->stats), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } bcopy(&link->stats, resp->data, sizeof(link->stats)); } if (msg->header.cmd != NGM_BRIDGE_GET_STATS) bzero(&link->stats, sizeof(link->stats)); break; } case NGM_BRIDGE_GET_TABLE: { struct ng_bridge_host_ary *ary; struct ng_bridge_hent *hent; int i = 0, bucket; NG_MKRESPONSE(resp, msg, sizeof(*ary) + (priv->numHosts * sizeof(*ary->hosts)), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } ary = (struct ng_bridge_host_ary *)resp->data; ary->numHosts = priv->numHosts; for (bucket = 0; bucket < priv->numBuckets; bucket++) { SLIST_FOREACH(hent, &priv->tab[bucket], next) ary->hosts[i++] = hent->host; } break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } /* Done */ if (rptr) *rptr = resp; else if (resp != NULL) FREE(resp, M_NETGRAPH); FREE(msg, M_NETGRAPH); return (error); } /* * Receive data on a hook */ static int ng_bridge_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const node_p node = hook->node; const priv_p priv = node->private; struct ng_bridge_host *host; struct ng_bridge_link *link; struct ether_header *eh; int error = 0, linkNum; int i, manycast; /* Get link number */ linkNum = LINK_NUM(hook); KASSERT(linkNum >= 0 && linkNum < NG_BRIDGE_MAX_LINKS, ("%s: linkNum=%u", __FUNCTION__, linkNum)); link = priv->links[linkNum]; KASSERT(link != NULL, ("%s: link%d null", __FUNCTION__, linkNum)); /* Sanity check packet and pull up header */ if (m->m_pkthdr.len < ETHER_HDR_LEN) { link->stats.recvRunts++; NG_FREE_DATA(m, meta); return (EINVAL); } if (m->m_len < ETHER_HDR_LEN && !(m = m_pullup(m, ETHER_HDR_LEN))) { link->stats.memoryFailures++; NG_FREE_META(meta); return (ENOBUFS); } eh = mtod(m, struct ether_header *); if ((eh->ether_shost[0] & 1) != 0) { link->stats.recvInvalid++; NG_FREE_DATA(m, meta); return (EINVAL); } /* Is link disabled due to a loopback condition? */ if (link->loopCount != 0) { link->stats.loopDrops++; NG_FREE_DATA(m, meta); return (ELOOP); /* XXX is this an appropriate error? */ } /* Update stats */ link->stats.recvPackets++; link->stats.recvOctets += m->m_pkthdr.len; if ((manycast = (eh->ether_dhost[0] & 1)) != 0) { if (ETHER_EQUAL(eh->ether_dhost, ng_bridge_bcast_addr)) { link->stats.recvBroadcasts++; manycast = 2; } else link->stats.recvMulticasts++; } /* Look up packet's source Ethernet address in hashtable */ if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL) { /* Update time since last heard from this host */ host->staleness = 0; /* Did host jump to a different link? */ if (host->linkNum != linkNum) { /* * If the host's old link was recently established * on the old link and it's already jumped to a new * link, declare a loopback condition. */ if (host->age < priv->conf.minStableAge) { /* Log the problem */ if (priv->conf.debugLevel >= 2) { struct ifnet *ifp = m->m_pkthdr.rcvif; char suffix[32]; if (ifp != NULL) snprintf(suffix, sizeof(suffix), " (%s%d)", ifp->if_name, ifp->if_unit); else *suffix = '\0'; log(LOG_WARNING, "ng_bridge: %s:" " loopback detected on %s%s\n", ng_bridge_nodename(node), hook->name, suffix); } /* Mark link as linka non grata */ link->loopCount = priv->conf.loopTimeout; link->stats.loopDetects++; /* Forget all hosts on this link */ ng_bridge_remove_hosts(priv, linkNum); /* Drop packet */ link->stats.loopDrops++; NG_FREE_DATA(m, meta); return (ELOOP); /* XXX appropriate? */ } /* Move host over to new link */ host->linkNum = linkNum; host->age = 0; } } else { if (!ng_bridge_put(priv, eh->ether_shost, linkNum)) { link->stats.memoryFailures++; NG_FREE_DATA(m, meta); return (ENOMEM); } } /* Run packet through ipfw processing, if enabled */ if (priv->conf.ipfw[linkNum] && fw_enable && ip_fw_chk_ptr != NULL) { /* XXX not implemented yet */ } /* * If unicast and destination host known, deliver to host's link, * unless it is the same link as the packet came in on. */ if (!manycast) { /* Determine packet destination link */ if ((host = ng_bridge_get(priv, eh->ether_dhost)) != NULL) { struct ng_bridge_link *const destLink = priv->links[host->linkNum]; /* If destination same as incoming link, do nothing */ KASSERT(destLink != NULL, ("%s: link%d null", __FUNCTION__, host->linkNum)); if (destLink == link) { NG_FREE_DATA(m, meta); return (0); } /* Deliver packet out the destination link */ destLink->stats.xmitPackets++; destLink->stats.xmitOctets += m->m_pkthdr.len; NG_SEND_DATA(error, destLink->hook, m, meta); return (error); } /* Destination host is not known */ link->stats.recvUnknown++; } /* Distribute unknown, multicast, broadcast pkts to all other links */ for (linkNum = i = 0; i < priv->numLinks - 1; linkNum++) { struct ng_bridge_link *const destLink = priv->links[linkNum]; meta_p meta2 = NULL; struct mbuf *m2; /* Skip incoming link and disconnected links */ if (destLink == NULL || destLink == link) continue; /* Copy mbuf and meta info */ if (++i == priv->numLinks - 1) { /* last link */ m2 = m; meta2 = meta; } else { m2 = m_dup(m, M_NOWAIT); /* XXX m_copypacket() */ if (m2 == NULL) { link->stats.memoryFailures++; NG_FREE_DATA(m, meta); return (ENOBUFS); } if (meta != NULL && (meta2 = ng_copy_meta(meta)) == NULL) { link->stats.memoryFailures++; m_freem(m2); NG_FREE_DATA(m, meta); return (ENOMEM); } } /* Update stats */ destLink->stats.xmitPackets++; destLink->stats.xmitOctets += m->m_pkthdr.len; switch (manycast) { case 0: /* unicast */ break; case 1: /* multicast */ destLink->stats.xmitMulticasts++; break; case 2: /* broadcast */ destLink->stats.xmitBroadcasts++; break; } /* Send packet */ NG_SEND_DATA(error, destLink->hook, m2, meta2); } return (error); } /* * Shutdown node */ static int ng_bridge_rmnode(node_p node) { const priv_p priv = node->private; ng_unname(node); ng_cutlinks(node); /* frees all link and host info */ KASSERT(priv->numLinks == 0 && priv->numHosts == 0, ("%s: numLinks=%d numHosts=%d", __FUNCTION__, priv->numLinks, priv->numHosts)); FREE(priv->tab, M_NETGRAPH); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); return (0); } /* * Hook disconnection. */ static int ng_bridge_disconnect(hook_p hook) { const priv_p priv = hook->node->private; int linkNum; /* Get link number */ linkNum = LINK_NUM(hook); KASSERT(linkNum >= 0 && linkNum < NG_BRIDGE_MAX_LINKS, ("%s: linkNum=%u", __FUNCTION__, linkNum)); /* Remove all hosts associated with this link */ ng_bridge_remove_hosts(priv, linkNum); /* Free associated link information */ KASSERT(priv->links[linkNum] != NULL, ("%s: no link", __FUNCTION__)); FREE(priv->links[linkNum], M_NETGRAPH); priv->links[linkNum] = NULL; priv->numLinks--; /* If no more hooks, go away */ if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } /****************************************************************** HASH TABLE FUNCTIONS ******************************************************************/ /* * Hash algorithm * * Only hashing bytes 3-6 of the Ethernet address is sufficient and fast. */ #define HASH(addr,mask) ( (((const u_int16_t *)(addr))[0] \ ^ ((const u_int16_t *)(addr))[1] \ ^ ((const u_int16_t *)(addr))[2]) & (mask) ) /* * Find a host entry in the table. */ static struct ng_bridge_host * ng_bridge_get(priv_p priv, const u_char *addr) { const int bucket = HASH(addr, priv->hashMask); struct ng_bridge_hent *hent; SLIST_FOREACH(hent, &priv->tab[bucket], next) { if (ETHER_EQUAL(hent->host.addr, addr)) return (&hent->host); } return (NULL); } /* * Add a new host entry to the table. This assumes the host doesn't * already exist in the table. Returns 1 on success, 0 if there * was a memory allocation failure. */ static int ng_bridge_put(priv_p priv, const u_char *addr, int linkNum) { const int bucket = HASH(addr, priv->hashMask); struct ng_bridge_hent *hent; #ifdef INVARIANTS /* Assert that entry does not already exist in hashtable */ SLIST_FOREACH(hent, &priv->tab[bucket], next) { KASSERT(!ETHER_EQUAL(hent->host.addr, addr), ("%s: entry %6D exists in table", __FUNCTION__, addr, ":")); } #endif /* Allocate and initialize new hashtable entry */ MALLOC(hent, struct ng_bridge_hent *, sizeof(*hent), M_NETGRAPH, M_NOWAIT); if (hent == NULL) return (0); bcopy(addr, hent->host.addr, ETHER_ADDR_LEN); hent->host.linkNum = linkNum; hent->host.staleness = 0; hent->host.age = 0; /* Add new element to hash bucket */ SLIST_INSERT_HEAD(&priv->tab[bucket], hent, next); priv->numHosts++; /* Resize table if necessary */ ng_bridge_rehash(priv); return (1); } /* * Resize the hash table. We try to maintain the number of buckets * such that the load factor is in the range 0.25 to 1.0. * * If we can't get the new memory then we silently fail. This is OK * because things will still work and we'll try again soon anyway. */ static void ng_bridge_rehash(priv_p priv) { struct ng_bridge_bucket *newTab; int oldBucket, newBucket; int newNumBuckets; u_int newMask; /* Is table too full or too empty? */ if (priv->numHosts > priv->numBuckets && (priv->numBuckets << 1) <= MAX_BUCKETS) newNumBuckets = priv->numBuckets << 1; else if (priv->numHosts < (priv->numBuckets >> 2) && (priv->numBuckets >> 2) >= MIN_BUCKETS) newNumBuckets = priv->numBuckets >> 2; else return; newMask = newNumBuckets - 1; /* Allocate and initialize new table */ MALLOC(newTab, struct ng_bridge_bucket *, newNumBuckets * sizeof(*newTab), M_NETGRAPH, M_NOWAIT | M_ZERO); if (newTab == NULL) return; /* Move all entries from old table to new table */ for (oldBucket = 0; oldBucket < priv->numBuckets; oldBucket++) { struct ng_bridge_bucket *const oldList = &priv->tab[oldBucket]; while (!SLIST_EMPTY(oldList)) { struct ng_bridge_hent *const hent = SLIST_FIRST(oldList); SLIST_REMOVE_HEAD(oldList, next); newBucket = HASH(hent->host.addr, newMask); SLIST_INSERT_HEAD(&newTab[newBucket], hent, next); } } /* Replace old table with new one */ if (priv->conf.debugLevel >= 3) { log(LOG_INFO, "ng_bridge: %s: table size %d -> %d\n", ng_bridge_nodename(priv->node), priv->numBuckets, newNumBuckets); } FREE(priv->tab, M_NETGRAPH); priv->numBuckets = newNumBuckets; priv->hashMask = newMask; priv->tab = newTab; return; } /****************************************************************** MISC FUNCTIONS ******************************************************************/ /* * Remove all hosts associated with a specific link from the hashtable. * If linkNum == -1, then remove all hosts in the table. */ static void ng_bridge_remove_hosts(priv_p priv, int linkNum) { int bucket; for (bucket = 0; bucket < priv->numBuckets; bucket++) { struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]); while (*hptr != NULL) { struct ng_bridge_hent *const hent = *hptr; if (linkNum == -1 || hent->host.linkNum == linkNum) { *hptr = SLIST_NEXT(hent, next); FREE(hent, M_NETGRAPH); priv->numHosts--; } else hptr = &SLIST_NEXT(hent, next); } } } /* * Handle our once-per-second timeout event. We do two things: * we decrement link->loopCount for those links being muted due to * a detected loopback condition, and we remove any hosts from * the hashtable whom we haven't heard from in a long while. */ static void ng_bridge_timeout(void *arg) { const node_p node = arg; const priv_p priv = node->private; int s, bucket; int counter = 0; int linkNum; /* Avoid race condition with ng_bridge_shutdown() */ s = splnet(); if ((node->flags & NG_INVALID) != 0 || priv == NULL) { ng_unref(node); splx(s); return; } /* Register a new timeout, keeping the existing node reference */ callout_reset(&priv->timer, hz, ng_bridge_timeout, node); /* Update host time counters and remove stale entries */ for (bucket = 0; bucket < priv->numBuckets; bucket++) { struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]); while (*hptr != NULL) { struct ng_bridge_hent *const hent = *hptr; /* Make sure host's link really exists */ KASSERT(priv->links[hent->host.linkNum] != NULL, ("%s: host %6D on nonexistent link %d\n", __FUNCTION__, hent->host.addr, ":", hent->host.linkNum)); /* Remove hosts we haven't heard from in a while */ if (++hent->host.staleness >= priv->conf.maxStaleness) { *hptr = SLIST_NEXT(hent, next); FREE(hent, M_NETGRAPH); priv->numHosts--; } else { if (hent->host.age < 0xffff) hent->host.age++; hptr = &SLIST_NEXT(hent, next); counter++; } } } KASSERT(priv->numHosts == counter, ("%s: hosts: %d != %d", __FUNCTION__, priv->numHosts, counter)); /* Decrease table size if necessary */ ng_bridge_rehash(priv); /* Decrease loop counter on muted looped back links */ for (counter = linkNum = 0; linkNum < NG_BRIDGE_MAX_LINKS; linkNum++) { struct ng_bridge_link *const link = priv->links[linkNum]; if (link != NULL) { if (link->loopCount != 0) { link->loopCount--; if (link->loopCount == 0 && priv->conf.debugLevel >= 2) { log(LOG_INFO, "ng_bridge: %s:" " restoring looped back link%d\n", ng_bridge_nodename(node), linkNum); } } counter++; } } KASSERT(priv->numLinks == counter, ("%s: links: %d != %d", __FUNCTION__, priv->numLinks, counter)); /* Done */ splx(s); } /* * Return node's "name", even if it doesn't have one. */ static const char * ng_bridge_nodename(node_p node) { static char name[NG_NODELEN+1]; if (node->name != NULL) snprintf(name, sizeof(name), "%s", node->name); else snprintf(name, sizeof(name), "[%x]", ng_node2ID(node)); return name; } Index: head/sys/netgraph/ng_cisco.c =================================================================== --- head/sys/netgraph/ng_cisco.c (revision 69921) +++ head/sys/netgraph/ng_cisco.c (revision 69922) @@ -1,602 +1,601 @@ /* * ng_cisco.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_cisco.c,v 1.25 1999/11/01 09:24:51 julian Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CISCO_MULTICAST 0x8f /* Cisco multicast address */ #define CISCO_UNICAST 0x0f /* Cisco unicast address */ #define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ #define CISCO_ADDR_REQ 0 /* Cisco address request */ #define CISCO_ADDR_REPLY 1 /* Cisco address reply */ #define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ #define KEEPALIVE_SECS 10 struct cisco_header { u_char address; u_char control; u_short protocol; }; #define CISCO_HEADER_LEN sizeof (struct cisco_header) struct cisco_packet { u_long type; u_long par1; u_long par2; u_short rel; u_short time0; u_short time1; }; #define CISCO_PACKET_LEN (sizeof(struct cisco_packet)) struct protoent { hook_p hook; /* the hook for this proto */ u_short af; /* address family, -1 = downstream */ }; struct cisco_priv { u_long local_seq; u_long remote_seq; u_long seqRetries; /* how many times we've been here throwing out * the same sequence number without ack */ node_p node; struct callout_handle handle; struct protoent downstream; struct protoent inet; /* IP information */ struct in_addr localip; struct in_addr localmask; struct protoent inet6; /* IPv6 information */ struct protoent atalk; /* AppleTalk information */ struct protoent ipx; /* IPX information */ }; typedef struct cisco_priv *sc_p; /* Netgraph methods */ static ng_constructor_t cisco_constructor; static ng_rcvmsg_t cisco_rcvmsg; static ng_shutdown_t cisco_rmnode; static ng_newhook_t cisco_newhook; static ng_rcvdata_t cisco_rcvdata; static ng_disconnect_t cisco_disconnect; /* Other functions */ static int cisco_input(sc_p sc, struct mbuf *m, meta_p meta); static void cisco_keepalive(void *arg); static int cisco_send(sc_p sc, int type, long par1, long par2); /* Parse type for struct ng_cisco_ipaddr */ static const struct ng_parse_struct_info ng_cisco_ipaddr_type_info = NG_CISCO_IPADDR_TYPE_INFO; static const struct ng_parse_type ng_cisco_ipaddr_type = { &ng_parse_struct_type, &ng_cisco_ipaddr_type_info }; /* Parse type for struct ng_async_stat */ static const struct ng_parse_struct_info ng_cisco_stats_type_info = NG_CISCO_STATS_TYPE_INFO; static const struct ng_parse_type ng_cisco_stats_type = { &ng_parse_struct_type, &ng_cisco_stats_type_info, }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_cisco_cmdlist[] = { { NGM_CISCO_COOKIE, NGM_CISCO_SET_IPADDR, "setipaddr", &ng_cisco_ipaddr_type, NULL }, { NGM_CISCO_COOKIE, NGM_CISCO_GET_IPADDR, "getipaddr", NULL, &ng_cisco_ipaddr_type }, { NGM_CISCO_COOKIE, NGM_CISCO_GET_STATUS, "getstats", NULL, &ng_cisco_stats_type }, { 0 } }; /* Node type */ static struct ng_type typestruct = { NG_VERSION, NG_CISCO_NODE_TYPE, NULL, cisco_constructor, cisco_rcvmsg, cisco_rmnode, cisco_newhook, NULL, NULL, cisco_rcvdata, - cisco_rcvdata, cisco_disconnect, ng_cisco_cmdlist }; NETGRAPH_INIT(cisco, &typestruct); /* * Node constructor */ static int cisco_constructor(node_p *nodep) { sc_p sc; int error = 0; MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); if (sc == NULL) return (ENOMEM); callout_handle_init(&sc->handle); if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(sc, M_NETGRAPH); return (error); } (*nodep)->private = sc; sc->node = *nodep; /* Initialise the varous protocol hook holders */ sc->downstream.af = 0xffff; sc->inet.af = AF_INET; sc->inet6.af = AF_INET6; sc->atalk.af = AF_APPLETALK; sc->ipx.af = AF_IPX; return (0); } /* * Check new hook */ static int cisco_newhook(node_p node, hook_p hook, const char *name) { const sc_p sc = node->private; if (strcmp(name, NG_CISCO_HOOK_DOWNSTREAM) == 0) { sc->downstream.hook = hook; hook->private = &sc->downstream; /* Start keepalives */ sc->handle = timeout(cisco_keepalive, sc, hz * KEEPALIVE_SECS); } else if (strcmp(name, NG_CISCO_HOOK_INET) == 0) { sc->inet.hook = hook; hook->private = &sc->inet; } else if (strcmp(name, NG_CISCO_HOOK_APPLETALK) == 0) { sc->atalk.hook = hook; hook->private = &sc->atalk; } else if (strcmp(name, NG_CISCO_HOOK_IPX) == 0) { sc->ipx.hook = hook; hook->private = &sc->ipx; } else if (strcmp(name, NG_CISCO_HOOK_DEBUG) == 0) { hook->private = NULL; /* unimplemented */ } else return (EINVAL); return 0; } /* * Receive control message. */ static int cisco_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { const sc_p sc = node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_GENERIC_COOKIE: switch (msg->header.cmd) { case NGM_TEXT_STATUS: { char *arg; int pos; NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg) + NG_TEXTRESPONSE, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } arg = (char *) resp->data; pos = sprintf(arg, "keepalive period: %d sec; ", KEEPALIVE_SECS); pos += sprintf(arg + pos, "unacknowledged keepalives: %ld", sc->seqRetries); resp->header.arglen = pos + 1; break; } default: error = EINVAL; break; } break; case NGM_CISCO_COOKIE: switch (msg->header.cmd) { case NGM_CISCO_GET_IPADDR: /* could be a late reply! */ if ((msg->header.flags & NGF_RESP) == 0) { struct in_addr *ips; NG_MKRESPONSE(resp, msg, 2 * sizeof(*ips), M_NOWAIT); if (!resp) { error = ENOMEM; break; } ips = (struct in_addr *) resp->data; ips[0] = sc->localip; ips[1] = sc->localmask; break; } /* FALLTHROUGH */ /* ...if it's a reply */ case NGM_CISCO_SET_IPADDR: { struct in_addr *const ips = (struct in_addr *)msg->data; if (msg->header.arglen < 2 * sizeof(*ips)) { error = EINVAL; break; } sc->localip = ips[0]; sc->localmask = ips[1]; break; } case NGM_CISCO_GET_STATUS: { struct ng_cisco_stats *stat; NG_MKRESPONSE(resp, msg, sizeof(*stat), M_NOWAIT); if (!resp) { error = ENOMEM; break; } stat = (struct ng_cisco_stats *)resp->data; stat->seqRetries = sc->seqRetries; stat->keepAlivePeriod = KEEPALIVE_SECS; break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); FREE(msg, M_NETGRAPH); return (error); } /* * Receive data */ static int cisco_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const sc_p sc = hook->node->private; struct protoent *pep; struct cisco_header *h; int error = 0; if ((pep = hook->private) == NULL) goto out; /* If it came from our downlink, deal with it separately */ if (pep->af == 0xffff) return (cisco_input(sc, m, meta)); /* OK so it came from a protocol, heading out. Prepend general data packet header. For now, IP,IPX only */ M_PREPEND(m, CISCO_HEADER_LEN, M_DONTWAIT); if (!m) { error = ENOBUFS; goto out; } h = mtod(m, struct cisco_header *); h->address = CISCO_UNICAST; h->control = 0; switch (pep->af) { case AF_INET: /* Internet Protocol */ h->protocol = htons(ETHERTYPE_IP); break; case AF_INET6: h->protocol = htons(ETHERTYPE_IPV6); break; case AF_APPLETALK: /* AppleTalk Protocol */ h->protocol = htons(ETHERTYPE_AT); break; case AF_IPX: /* Novell IPX Protocol */ h->protocol = htons(ETHERTYPE_IPX); break; default: error = EAFNOSUPPORT; goto out; } /* Send it */ NG_SEND_DATA(error, sc->downstream.hook, m, meta); return (error); out: NG_FREE_DATA(m, meta); return (error); } /* * Shutdown node */ static int cisco_rmnode(node_p node) { const sc_p sc = node->private; node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); node->private = NULL; ng_unref(sc->node); FREE(sc, M_NETGRAPH); return (0); } /* * Disconnection of a hook * * For this type, removal of the last link destroys the node */ static int cisco_disconnect(hook_p hook) { const sc_p sc = hook->node->private; struct protoent *pep; /* Check it's not the debug hook */ if ((pep = hook->private)) { pep->hook = NULL; if (pep->af == 0xffff) { /* If it is the downstream hook, stop the timers */ untimeout(cisco_keepalive, sc, sc->handle); } } /* If no more hooks, remove the node */ if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } /* * Receive data */ static int cisco_input(sc_p sc, struct mbuf *m, meta_p meta) { struct cisco_header *h; struct cisco_packet *p; struct protoent *pep; int error = 0; if (m->m_pkthdr.len <= CISCO_HEADER_LEN) goto drop; /* Strip off cisco header */ h = mtod(m, struct cisco_header *); m_adj(m, CISCO_HEADER_LEN); switch (h->address) { default: /* Invalid Cisco packet. */ goto drop; case CISCO_UNICAST: case CISCO_MULTICAST: /* Don't check the control field here (RFC 1547). */ switch (ntohs(h->protocol)) { default: goto drop; case CISCO_KEEPALIVE: p = mtod(m, struct cisco_packet *); switch (ntohl(p->type)) { default: log(LOG_WARNING, "cisco: unknown cisco packet type: 0x%lx\n", ntohl(p->type)); break; case CISCO_ADDR_REPLY: /* Reply on address request, ignore */ break; case CISCO_KEEPALIVE_REQ: sc->remote_seq = ntohl(p->par1); if (sc->local_seq == ntohl(p->par2)) { sc->local_seq++; sc->seqRetries = 0; } break; case CISCO_ADDR_REQ: { struct ng_mesg *msg, *resp; /* Ask inet peer for IP address information */ if (sc->inet.hook == NULL) goto nomsg; NG_MKMESSAGE(msg, NGM_CISCO_COOKIE, NGM_CISCO_GET_IPADDR, 0, M_NOWAIT); if (msg == NULL) goto nomsg; - ng_send_msg(sc->node, msg, - NG_CISCO_HOOK_INET, &resp); + ng_send_msg(sc->node, msg, NULL, + sc->inet.hook, NULL, &resp); if (resp != NULL) cisco_rcvmsg(sc->node, resp, ".", NULL, NULL); nomsg: /* Send reply to peer device */ error = cisco_send(sc, CISCO_ADDR_REPLY, ntohl(sc->localip.s_addr), ntohl(sc->localmask.s_addr)); break; } } goto drop; case ETHERTYPE_IP: pep = &sc->inet; break; case ETHERTYPE_IPV6: pep = &sc->inet6; break; case ETHERTYPE_AT: pep = &sc->atalk; break; case ETHERTYPE_IPX: pep = &sc->ipx; break; } break; } /* Send it on */ if (pep->hook == NULL) goto drop; NG_SEND_DATA(error, pep->hook, m, meta); return (error); drop: NG_FREE_DATA(m, meta); return (error); } /* * Send keepalive packets, every 10 seconds. */ static void cisco_keepalive(void *arg) { const sc_p sc = arg; int s = splimp(); cisco_send(sc, CISCO_KEEPALIVE_REQ, sc->local_seq, sc->remote_seq); sc->seqRetries++; splx(s); sc->handle = timeout(cisco_keepalive, sc, hz * KEEPALIVE_SECS); } /* * Send Cisco keepalive packet. */ static int cisco_send(sc_p sc, int type, long par1, long par2) { struct cisco_header *h; struct cisco_packet *ch; struct mbuf *m; u_long t; int error = 0; meta_p meta = NULL; struct timeval time; getmicrotime(&time); MGETHDR(m, M_DONTWAIT, MT_DATA); if (!m) return (ENOBUFS); t = (time.tv_sec - boottime.tv_sec) * 1000; m->m_pkthdr.len = m->m_len = CISCO_HEADER_LEN + CISCO_PACKET_LEN; m->m_pkthdr.rcvif = 0; h = mtod(m, struct cisco_header *); h->address = CISCO_MULTICAST; h->control = 0; h->protocol = htons(CISCO_KEEPALIVE); ch = (struct cisco_packet *) (h + 1); ch->type = htonl(type); ch->par1 = htonl(par1); ch->par2 = htonl(par2); ch->rel = -1; ch->time0 = htons((u_short) (t >> 16)); ch->time1 = htons((u_short) t); NG_SEND_DATA(error, sc->downstream.hook, m, meta); return (error); } Index: head/sys/netgraph/ng_echo.c =================================================================== --- head/sys/netgraph/ng_echo.c (revision 69921) +++ head/sys/netgraph/ng_echo.c (revision 69922) @@ -1,119 +1,118 @@ /* * ng_echo.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 Elisher * * $FreeBSD$ * $Whistle: ng_echo.c,v 1.13 1999/11/01 09:24:51 julian Exp $ */ /* * Netgraph "echo" node * * This node simply bounces data and messages back to whence they came. */ #include #include #include #include #include #include #include /* Netgraph methods */ static ng_rcvmsg_t nge_rcvmsg; static ng_rcvdata_t nge_rcvdata; static ng_disconnect_t nge_disconnect; /* Netgraph type */ static struct ng_type typestruct = { NG_VERSION, NG_ECHO_NODE_TYPE, NULL, NULL, nge_rcvmsg, NULL, NULL, NULL, NULL, nge_rcvdata, - nge_rcvdata, nge_disconnect, NULL }; NETGRAPH_INIT(echo, &typestruct); /* * Receive control message. We just bounce it back as a reply. */ static int nge_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { if (rptr) { msg->header.flags |= NGF_RESP; *rptr = msg; } else { FREE(msg, M_NETGRAPH); } return (0); } /* * Receive data */ static int nge_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { int error = 0; NG_SEND_DATA(error, hook, m, meta); return (error); } /* * Removal of the last link destroys the nodeo */ static int nge_disconnect(hook_p hook) { if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } Index: head/sys/netgraph/ng_ether.c =================================================================== --- head/sys/netgraph/ng_ether.c (revision 69921) +++ head/sys/netgraph/ng_ether.c (revision 69922) @@ -1,808 +1,819 @@ /* * ng_ether.c * * Copyright (c) 1996-2000 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. * * Authors: Archie Cobbs * Julian Elischer * * $FreeBSD$ */ /* * ng_ether(4) netgraph node type */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IFP2AC(IFP) ((struct arpcom *)IFP) #define IFP2NG(ifp) ((struct ng_node *)((struct arpcom *)(ifp))->ac_netgraph) /* Per-node private data */ struct private { struct ifnet *ifp; /* associated interface */ hook_p upper; /* upper hook connection */ hook_p lower; /* lower OR orphan hook connection */ u_char lowerOrphan; /* whether lower is lower or orphan */ u_char autoSrcAddr; /* always overwrite source address */ u_char promisc; /* promiscuous mode enabled */ }; typedef struct private *priv_p; /* Functional hooks called from if_ethersubr.c */ static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp, struct ether_header *eh); static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m, struct ether_header *eh); static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp); static void ng_ether_attach(struct ifnet *ifp); static void ng_ether_detach(struct ifnet *ifp); /* Other functions */ static void ng_ether_input2(node_p node, struct mbuf **mp, struct ether_header *eh); static int ng_ether_glueback_header(struct mbuf **mp, struct ether_header *eh); static int ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta); static int ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta); /* Netgraph node methods */ static ng_constructor_t ng_ether_constructor; static ng_rcvmsg_t ng_ether_rcvmsg; static ng_shutdown_t ng_ether_rmnode; static ng_newhook_t ng_ether_newhook; +static ng_connect_t ng_ether_connect; static ng_rcvdata_t ng_ether_rcvdata; static ng_disconnect_t ng_ether_disconnect; static int ng_ether_mod_event(module_t mod, int event, void *data); /* Parse type for an Ethernet address */ static ng_parse_t ng_enaddr_parse; static ng_unparse_t ng_enaddr_unparse; const struct ng_parse_type ng_ether_enaddr_type = { NULL, NULL, NULL, ng_enaddr_parse, ng_enaddr_unparse, NULL, /* no such thing as a "default" EN address */ 0 }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_ether_cmdlist[] = { { NGM_ETHER_COOKIE, NGM_ETHER_GET_IFNAME, "getifname", NULL, &ng_parse_string_type }, { NGM_ETHER_COOKIE, NGM_ETHER_GET_IFINDEX, "getifindex", NULL, &ng_parse_int32_type }, { NGM_ETHER_COOKIE, NGM_ETHER_GET_ENADDR, "getenaddr", NULL, &ng_ether_enaddr_type }, { NGM_ETHER_COOKIE, NGM_ETHER_SET_ENADDR, "setenaddr", &ng_ether_enaddr_type, NULL }, { NGM_ETHER_COOKIE, NGM_ETHER_GET_PROMISC, "getpromisc", NULL, &ng_parse_int32_type }, { NGM_ETHER_COOKIE, NGM_ETHER_SET_PROMISC, "setpromisc", &ng_parse_int32_type, NULL }, { NGM_ETHER_COOKIE, NGM_ETHER_GET_AUTOSRC, "getautosrc", NULL, &ng_parse_int32_type }, { NGM_ETHER_COOKIE, NGM_ETHER_SET_AUTOSRC, "setautosrc", &ng_parse_int32_type, NULL }, { 0 } }; static struct ng_type ng_ether_typestruct = { NG_VERSION, NG_ETHER_NODE_TYPE, ng_ether_mod_event, ng_ether_constructor, ng_ether_rcvmsg, ng_ether_rmnode, ng_ether_newhook, NULL, - NULL, + ng_ether_connect, ng_ether_rcvdata, - ng_ether_rcvdata, ng_ether_disconnect, ng_ether_cmdlist, }; NETGRAPH_INIT(ether, &ng_ether_typestruct); /****************************************************************** ETHERNET FUNCTION HOOKS ******************************************************************/ /* * Handle a packet that has come in on an interface. We get to * look at it here before any upper layer protocols do. * * NOTE: this function will get called at splimp() */ static void ng_ether_input(struct ifnet *ifp, struct mbuf **mp, struct ether_header *eh) { const node_p node = IFP2NG(ifp); const priv_p priv = node->private; /* If "lower" hook not connected, let packet continue */ if (priv->lower == NULL || priv->lowerOrphan) return; ng_ether_input2(node, mp, eh); } /* * Handle a packet that has come in on an interface, and which * does not match any of our known protocols (an ``orphan''). * * NOTE: this function will get called at splimp() */ static void ng_ether_input_orphan(struct ifnet *ifp, struct mbuf *m, struct ether_header *eh) { const node_p node = IFP2NG(ifp); const priv_p priv = node->private; /* If "orphan" hook not connected, let packet continue */ if (priv->lower == NULL || !priv->lowerOrphan) { m_freem(m); return; } ng_ether_input2(node, &m, eh); if (m != NULL) m_freem(m); } /* - * Handle a packet that has come in on an interface. + * Handle a packet that has come in on an ethernet interface. * The Ethernet header has already been detached from the mbuf, * so we have to put it back. * * NOTE: this function will get called at splimp() */ static void ng_ether_input2(node_p node, struct mbuf **mp, struct ether_header *eh) { const priv_p priv = node->private; - meta_p meta = NULL; int error; /* Glue Ethernet header back on */ if ((error = ng_ether_glueback_header(mp, eh)) != 0) return; /* Send out lower/orphan hook */ - (void)ng_queue_data(priv->lower, *mp, meta); + NG_SEND_DATA_ONLY(error, priv->lower, *mp); *mp = NULL; } /* * Handle a packet that is going out on an interface. * The Ethernet header is already attached to the mbuf. */ static int ng_ether_output(struct ifnet *ifp, struct mbuf **mp) { const node_p node = IFP2NG(ifp); const priv_p priv = node->private; meta_p meta = NULL; int error = 0; /* If "upper" hook not connected, let packet continue */ if (priv->upper == NULL) return (0); /* Send it out "upper" hook */ - NG_SEND_DATA_RET(error, priv->upper, *mp, meta); + NG_SEND_DATA_RET(error, priv->upper, *mp, meta, NULL); /* If we got a reflected packet back, handle it */ if (error == 0 && *mp != NULL) { error = ng_ether_rcv_upper(node, *mp, meta); *mp = NULL; } return (error); } /* * A new Ethernet interface has been attached. * Create a new node for it, etc. */ static void ng_ether_attach(struct ifnet *ifp) { char name[IFNAMSIZ + 1]; priv_p priv; node_p node; /* Create node */ KASSERT(!IFP2NG(ifp), ("%s: node already exists?", __FUNCTION__)); snprintf(name, sizeof(name), "%s%d", ifp->if_name, ifp->if_unit); if (ng_make_node_common(&ng_ether_typestruct, &node) != 0) { log(LOG_ERR, "%s: can't %s for %s\n", __FUNCTION__, "create node", name); return; } /* Allocate private data */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv == NULL) { log(LOG_ERR, "%s: can't %s for %s\n", __FUNCTION__, "allocate memory", name); ng_unref(node); return; } node->private = priv; priv->ifp = ifp; IFP2NG(ifp) = node; priv->autoSrcAddr = 1; /* Try to give the node the same name as the interface */ if (ng_name_node(node, name) != 0) { log(LOG_WARNING, "%s: can't name node %s\n", __FUNCTION__, name); } } /* * An Ethernet interface is being detached. * Destroy its node. */ static void ng_ether_detach(struct ifnet *ifp) { const node_p node = IFP2NG(ifp); priv_p priv; if (node == NULL) /* no node (why not?), ignore */ return; ng_rmnode(node); /* break all links to other nodes */ node->flags |= NG_INVALID; ng_unname(node); /* free name (and its reference) */ IFP2NG(ifp) = NULL; /* detach node from interface */ priv = node->private; /* free node private info */ bzero(priv, sizeof(*priv)); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); /* free node itself */ } /* * Optimization for gluing the Ethernet header back onto * the front of an incoming packet. */ static int ng_ether_glueback_header(struct mbuf **mp, struct ether_header *eh) { struct mbuf *m = *mp; uintfptr_t room; int error = 0; /* * Possibly the header is already on the front. * If this is the case so just move the markers back * to re-include it. We lucked out. * This allows us to avoid a yucky m_pullup * in later nodes if it works. */ if (eh == mtod(m, struct ether_header *) - 1) { m->m_len += sizeof(*eh); m->m_data -= sizeof(*eh); m->m_pkthdr.len += sizeof(*eh); goto done; } /* * Alternatively there may be room even though * it is stored somewhere else. If so, copy it in. * This only safe because we KNOW that this packet has * just been generated by an ethernet card, so there are * no aliases to the buffer (not so for outgoing packets). * Nearly all ethernet cards will end up producing mbufs * that fall into these cases. So we are not optimizing * contorted cases. */ if ((m->m_flags & M_EXT) != 0) { room = mtod(m, caddr_t) - m->m_ext.ext_buf; if (room > m->m_ext.ext_size) /* garbage, fail immediately */ room = 0; } else room = mtod(m, caddr_t) - m->m_pktdat; /* * If we have room, just copy it and adjust */ if (room >= sizeof(*eh)) { m->m_len += sizeof(*eh); m->m_data -= sizeof(*eh); m->m_pkthdr.len += sizeof(*eh); goto copy; } /* * Doing anything more is likely to get more * expensive than it's worth.. * it's probable that everything else is in one * big lump. The next node will do an m_pullup() * for exactly the amount of data it needs and * hopefully everything after that will not * need one. So let's just use M_PREPEND. */ M_PREPEND(m, sizeof (*eh), M_DONTWAIT); if (m == NULL) { error = ENOBUFS; goto done; } copy: /* Copy header and return (possibly new) mbuf */ bcopy((caddr_t)eh, mtod(m, struct ether_header *), sizeof(*eh)); done: *mp = m; return error; } /****************************************************************** NETGRAPH NODE METHODS ******************************************************************/ /* * It is not possible or allowable to create a node of this type. * Nodes get created when the interface is attached (or, when * this node type's KLD is loaded). */ static int ng_ether_constructor(node_p *nodep) { return (EINVAL); } /* * Check for attaching a new hook. */ static int ng_ether_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; u_char orphan = priv->lowerOrphan; hook_p *hookptr; /* Divert hook is an alias for lower */ if (strcmp(name, NG_ETHER_HOOK_DIVERT) == 0) name = NG_ETHER_HOOK_LOWER; /* Which hook? */ if (strcmp(name, NG_ETHER_HOOK_UPPER) == 0) hookptr = &priv->upper; else if (strcmp(name, NG_ETHER_HOOK_LOWER) == 0) { hookptr = &priv->lower; orphan = 0; } else if (strcmp(name, NG_ETHER_HOOK_ORPHAN) == 0) { hookptr = &priv->lower; orphan = 1; } else return (EINVAL); /* Check if already connected (shouldn't be, but doesn't hurt) */ if (*hookptr != NULL) return (EISCONN); /* OK */ *hookptr = hook; priv->lowerOrphan = orphan; return (0); } /* + * Hooks are attached, adjust to force queueing. + * We don't really care which hook it is. + * they should all be queuing for outgoing data. + */ +static int +ng_ether_connect(hook_p hook) +{ + hook->peer->flags |= HK_QUEUE; + return (0); +} + +/* * Receive an incoming control message. */ static int ng_ether_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { const priv_p priv = node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_ETHER_COOKIE: switch (msg->header.cmd) { case NGM_ETHER_GET_IFNAME: NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } snprintf(resp->data, IFNAMSIZ + 1, "%s%d", priv->ifp->if_name, priv->ifp->if_unit); break; case NGM_ETHER_GET_IFINDEX: NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } *((u_int32_t *)resp->data) = priv->ifp->if_index; break; case NGM_ETHER_GET_ENADDR: NG_MKRESPONSE(resp, msg, ETHER_ADDR_LEN, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } bcopy((IFP2AC(priv->ifp))->ac_enaddr, resp->data, ETHER_ADDR_LEN); break; case NGM_ETHER_SET_ENADDR: { if (msg->header.arglen != ETHER_ADDR_LEN) { error = EINVAL; break; } error = if_setlladdr(priv->ifp, (u_char *)msg->data, ETHER_ADDR_LEN); break; } case NGM_ETHER_GET_PROMISC: NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } *((u_int32_t *)resp->data) = priv->promisc; break; case NGM_ETHER_SET_PROMISC: { u_char want; if (msg->header.arglen != sizeof(u_int32_t)) { error = EINVAL; break; } want = !!*((u_int32_t *)msg->data); if (want ^ priv->promisc) { if ((error = ifpromisc(priv->ifp, want)) != 0) break; priv->promisc = want; } break; } case NGM_ETHER_GET_AUTOSRC: NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } *((u_int32_t *)resp->data) = priv->autoSrcAddr; break; case NGM_ETHER_SET_AUTOSRC: if (msg->header.arglen != sizeof(u_int32_t)) { error = EINVAL; break; } priv->autoSrcAddr = !!*((u_int32_t *)msg->data); break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp != NULL) FREE(resp, M_NETGRAPH); FREE(msg, M_NETGRAPH); return (error); } /* * Receive data on a hook. */ static int ng_ether_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const node_p node = hook->node; const priv_p priv = node->private; if (hook == priv->lower) return ng_ether_rcv_lower(node, m, meta); if (hook == priv->upper) return ng_ether_rcv_upper(node, m, meta); panic("%s: weird hook", __FUNCTION__); } /* * Handle an mbuf received on the "lower" hook. */ static int ng_ether_rcv_lower(node_p node, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; /* Make sure header is fully pulled up */ if (m->m_pkthdr.len < sizeof(struct ether_header)) { NG_FREE_DATA(m, meta); return (EINVAL); } if (m->m_len < sizeof(struct ether_header) && (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } /* Drop in the MAC address if desired */ if (priv->autoSrcAddr) { bcopy((IFP2AC(priv->ifp))->ac_enaddr, mtod(m, struct ether_header *)->ether_shost, ETHER_ADDR_LEN); } /* Send it on its way */ NG_FREE_META(meta); return ether_output_frame(priv->ifp, m); } /* * Handle an mbuf received on the "upper" hook. */ static int ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; struct ether_header *eh; /* Check length and pull off header */ if (m->m_pkthdr.len < sizeof(*eh)) { NG_FREE_DATA(m, meta); return (EINVAL); } if (m->m_len < sizeof(*eh) && (m = m_pullup(m, sizeof(*eh))) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } eh = mtod(m, struct ether_header *); m->m_data += sizeof(*eh); m->m_len -= sizeof(*eh); m->m_pkthdr.len -= sizeof(*eh); m->m_pkthdr.rcvif = priv->ifp; /* Route packet back in */ NG_FREE_META(meta); ether_demux(priv->ifp, eh, m); return (0); } /* * Shutdown node. This resets the node but does not remove it. */ static int ng_ether_rmnode(node_p node) { const priv_p priv = node->private; ng_cutlinks(node); node->flags &= ~NG_INVALID; /* bounce back to life */ if (priv->promisc) { /* disable promiscuous mode */ (void)ifpromisc(priv->ifp, 0); priv->promisc = 0; } priv->autoSrcAddr = 1; /* reset auto-src-addr flag */ return (0); } /* * Hook disconnection. */ static int ng_ether_disconnect(hook_p hook) { const priv_p priv = hook->node->private; if (hook == priv->upper) priv->upper = NULL; else if (hook == priv->lower) { priv->lower = NULL; priv->lowerOrphan = 0; } else panic("%s: weird hook", __FUNCTION__); if (hook->node->numhooks == 0) ng_rmnode(hook->node); /* reset node */ return (0); } 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); } /****************************************************************** INITIALIZATION ******************************************************************/ /* * Handle loading and unloading for this node type. */ static int ng_ether_mod_event(module_t mod, int event, void *data) { struct ifnet *ifp; int error = 0; int s; s = splnet(); switch (event) { case MOD_LOAD: /* Register function hooks */ if (ng_ether_attach_p != NULL) { error = EEXIST; break; } ng_ether_attach_p = ng_ether_attach; ng_ether_detach_p = ng_ether_detach; ng_ether_output_p = ng_ether_output; ng_ether_input_p = ng_ether_input; ng_ether_input_orphan_p = ng_ether_input_orphan; /* Create nodes for any already-existing Ethernet interfaces */ TAILQ_FOREACH(ifp, &ifnet, if_link) { if (ifp->if_type == IFT_ETHER) ng_ether_attach(ifp); } break; case MOD_UNLOAD: /* * Note that the base code won't try to unload us until * all nodes have been removed, and that can't happen * until all Ethernet interfaces are removed. In any * case, we know there are no nodes left if the action * is MOD_UNLOAD, so there's no need to detach any nodes. */ /* Unregister function hooks */ ng_ether_attach_p = NULL; ng_ether_detach_p = NULL; ng_ether_output_p = NULL; ng_ether_input_p = NULL; ng_ether_input_orphan_p = NULL; break; default: error = EOPNOTSUPP; break; } splx(s); return (error); } Index: head/sys/netgraph/ng_frame_relay.c =================================================================== --- head/sys/netgraph/ng_frame_relay.c (revision 69921) +++ head/sys/netgraph/ng_frame_relay.c (revision 69922) @@ -1,522 +1,521 @@ /* * ng_frame_relay.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 Elisher * * $FreeBSD$ * $Whistle: ng_frame_relay.c,v 1.20 1999/11/01 09:24:51 julian Exp $ */ /* * This node implements the frame relay protocol, not including * the LMI line management. This means basically keeping track * of which DLCI's are active, doing frame (de)multiplexing, etc. * * It has a 'downstream' hook that goes to the line, and a * hook for each DLCI (eg, 'dlci16'). */ #include #include #include #include #include #include #include #include #include #include #include /* * Line info, and status per channel. */ struct ctxinfo { /* one per active hook */ u_int flags; #define CHAN_VALID 0x01 /* assigned to a channel */ #define CHAN_ACTIVE 0x02 /* bottom level active */ int dlci; /* the dlci assigned to this context */ hook_p hook; /* if there's a hook assigned.. */ }; #define MAX_CT 16 /* # of dlci's active at a time (POWER OF 2!) */ struct frmrel_softc { int unit; /* which card are we? */ int datahooks; /* number of data hooks attached */ node_p node; /* netgraph node */ int addrlen; /* address header length */ int flags; /* state */ int mtu; /* guess */ u_char remote_seq; /* sequence number the remote sent */ u_char local_seq; /* sequence number the remote rcvd */ u_short ALT[1024]; /* map DLCIs to CTX */ #define CTX_VALID 0x8000 /* this bit means it's a valid CTX */ #define CTX_VALUE (MAX_CT - 1) /* mask for context part */ struct ctxinfo channel[MAX_CT]; struct ctxinfo downstream; }; typedef struct frmrel_softc *sc_p; #define BYTEX_EA 0x01 /* End Address. Always 0 on byte1 */ #define BYTE1_C_R 0x02 #define BYTE2_FECN 0x08 /* forwards congestion notification */ #define BYTE2_BECN 0x04 /* Backward congestion notification */ #define BYTE2_DE 0x02 /* Discard elligability */ #define LASTBYTE_D_C 0x02 /* last byte is dl_core or dlci info */ /* Used to do headers */ static struct segment { u_char mask; u_char shift; u_char width; } makeup[] = { { 0xfc, 2, 6 }, { 0xf0, 4, 4 }, { 0xfe, 1, 7 }, { 0xfc, 2, 6 } }; #define SHIFTIN(segment, byte, dlci) \ { \ (dlci) <<= (segment)->width; \ (dlci) |= \ (((byte) & (segment)->mask) >> (segment)->shift); \ } #define SHIFTOUT(segment, byte, dlci) \ { \ (byte) |= (((dlci) << (segment)->shift) & (segment)->mask); \ (dlci) >>= (segment)->width; \ } /* Netgraph methods */ static ng_constructor_t ngfrm_constructor; static ng_shutdown_t ngfrm_rmnode; static ng_newhook_t ngfrm_newhook; static ng_rcvdata_t ngfrm_rcvdata; static ng_disconnect_t ngfrm_disconnect; /* Other internal functions */ static int ngfrm_decode(node_p node, struct mbuf * m, meta_p meta); static int ngfrm_addrlen(char *hdr); static int ngfrm_allocate_CTX(sc_p sc, int dlci); /* Netgraph type */ static struct ng_type typestruct = { NG_VERSION, NG_FRAMERELAY_NODE_TYPE, NULL, ngfrm_constructor, NULL, ngfrm_rmnode, ngfrm_newhook, NULL, NULL, ngfrm_rcvdata, - ngfrm_rcvdata, ngfrm_disconnect, NULL }; NETGRAPH_INIT(framerelay, &typestruct); /* * Given a DLCI, return the index of the context table entry for it, * Allocating a new one if needs be, or -1 if none available. */ static int ngfrm_allocate_CTX(sc_p sc, int dlci) { u_int ctxnum = -1; /* what ctx number we are using */ volatile struct ctxinfo *CTXp = NULL; /* Sanity check the dlci value */ if (dlci > 1023) return (-1); /* Check to see if we already have an entry for this DLCI */ if (sc->ALT[dlci]) { if ((ctxnum = sc->ALT[dlci] & CTX_VALUE) < MAX_CT) { CTXp = sc->channel + ctxnum; } else { ctxnum = -1; sc->ALT[dlci] = 0; /* paranoid but... */ } } /* * If the index has no valid entry yet, then we need to allocate a * CTX number to it */ if (CTXp == NULL) { for (ctxnum = 0; ctxnum < MAX_CT; ctxnum++) { /* * If the VALID flag is empty it is unused */ if ((sc->channel[ctxnum].flags & CHAN_VALID) == 0) { bzero(sc->channel + ctxnum, sizeof(struct ctxinfo)); CTXp = sc->channel + ctxnum; sc->ALT[dlci] = ctxnum | CTX_VALID; sc->channel[ctxnum].dlci = dlci; sc->channel[ctxnum].flags = CHAN_VALID; break; } } } /* * If we still don't have a CTX pointer, then we never found a free * spot so give up now.. */ if (!CTXp) { log(LOG_ERR, "No CTX available for dlci %d\n", dlci); return (-1); } return (ctxnum); } /* * Node constructor */ static int ngfrm_constructor(node_p *nodep) { sc_p sc; int error = 0; MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); if (!sc) return (ENOMEM); if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(sc, M_NETGRAPH); return (error); } sc->addrlen = 2; /* default */ /* Link the node and our private info */ (*nodep)->private = sc; sc->node = *nodep; return (0); } /* * Add a new hook * * We allow hooks called "debug", "downstream" and dlci[0-1023] * The hook's private info points to our stash of info about that * channel. A NULL pointer is debug and a DLCI of -1 means downstream. */ static int ngfrm_newhook(node_p node, hook_p hook, const char *name) { const sc_p sc = node->private; const char *cp; char *eptr; int dlci = 0; int ctxnum; /* Check if it's our friend the control hook */ if (strcmp(name, NG_FRAMERELAY_HOOK_DEBUG) == 0) { hook->private = NULL; /* paranoid */ return (0); } /* * All other hooks either start with 'dlci' and have a decimal * trailing channel number up to 4 digits, or are the downstream * hook. */ if (strncmp(name, NG_FRAMERELAY_HOOK_DLCI, strlen(NG_FRAMERELAY_HOOK_DLCI)) != 0) { /* It must be the downstream connection */ if (strcmp(name, NG_FRAMERELAY_HOOK_DOWNSTREAM) != 0) return EINVAL; /* Make sure we haven't already got one (paranoid) */ if (sc->downstream.hook) return (EADDRINUSE); /* OK add it */ hook->private = &sc->downstream; sc->downstream.hook = hook; sc->downstream.dlci = -1; sc->downstream.flags |= CHAN_ACTIVE; sc->datahooks++; return (0); } /* Must be a dlci hook at this point */ cp = name + strlen(NG_FRAMERELAY_HOOK_DLCI); if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) return (EINVAL); dlci = (int)strtoul(cp, &eptr, 10); if (*eptr != '\0' || dlci < 0 || dlci > 1023) return (EINVAL); /* * We have a dlci, now either find it, or allocate it. It's possible * that we might have seen packets for it already and made an entry * for it. */ ctxnum = ngfrm_allocate_CTX(sc, dlci); if (ctxnum == -1) return (ENOBUFS); /* * Be paranoid: if it's got a hook already, that dlci is in use . * Generic code can not catch all the synonyms (e.g. dlci016 vs * dlci16) */ if (sc->channel[ctxnum].hook != NULL) return (EADDRINUSE); /* * Put our hooks into it (pun not intended) */ sc->channel[ctxnum].flags |= CHAN_ACTIVE; hook->private = sc->channel + ctxnum; sc->channel[ctxnum].hook = hook; sc->datahooks++; return (0); } /* * Count up the size of the address header if we don't already know */ int ngfrm_addrlen(char *hdr) { if (hdr[0] & BYTEX_EA) return 0; if (hdr[1] & BYTEX_EA) return 2; if (hdr[2] & BYTEX_EA) return 3; if (hdr[3] & BYTEX_EA) return 4; return 0; } /* * Receive data packet */ static int ngfrm_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { struct ctxinfo *const ctxp = hook->private; int error = 0; int dlci; sc_p sc; int alen; char *data; /* Data doesn't come in from just anywhere (e.g debug hook) */ if (ctxp == NULL) { error = ENETDOWN; goto bad; } /* If coming from downstream, decode it to a channel */ dlci = ctxp->dlci; if (dlci == -1) return (ngfrm_decode(hook->node, m, meta)); /* Derive the softc we will need */ sc = hook->node->private; /* If there is no live channel, throw it away */ if ((sc->downstream.hook == NULL) || ((ctxp->flags & CHAN_ACTIVE) == 0)) { error = ENETDOWN; goto bad; } /* Store the DLCI on the front of the packet */ alen = sc->addrlen; if (alen == 0) alen = 2; /* default value for transmit */ M_PREPEND(m, alen, M_DONTWAIT); if (m == NULL) { error = ENOBUFS; goto bad; } data = mtod(m, char *); /* * Shift the lowest bits into the address field untill we are done. * First byte is MSBits of addr so work backwards. */ switch (alen) { case 2: data[0] = data[1] = '\0'; SHIFTOUT(makeup + 1, data[1], dlci); SHIFTOUT(makeup + 0, data[0], dlci); data[1] |= BYTEX_EA; break; case 3: data[0] = data[1] = data[2] = '\0'; SHIFTOUT(makeup + 3, data[2], dlci); /* 3 and 2 is correct */ SHIFTOUT(makeup + 1, data[1], dlci); SHIFTOUT(makeup + 0, data[0], dlci); data[2] |= BYTEX_EA; break; case 4: data[0] = data[1] = data[2] = data[3] = '\0'; SHIFTOUT(makeup + 3, data[3], dlci); SHIFTOUT(makeup + 2, data[2], dlci); SHIFTOUT(makeup + 1, data[1], dlci); SHIFTOUT(makeup + 0, data[0], dlci); data[3] |= BYTEX_EA; break; default: panic(__FUNCTION__); } /* Send it */ NG_SEND_DATA(error, sc->downstream.hook, m, meta); return (error); bad: NG_FREE_DATA(m, meta); return (error); } /* * Decode an incoming frame coming from the switch */ static int ngfrm_decode(node_p node, struct mbuf *m, meta_p meta) { const sc_p sc = node->private; char *data; int alen; u_int dlci = 0; int error = 0; int ctxnum; if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { error = ENOBUFS; goto out; } data = mtod(m, char *); if ((alen = sc->addrlen) == 0) { sc->addrlen = alen = ngfrm_addrlen(data); } switch (alen) { case 2: SHIFTIN(makeup + 0, data[0], dlci); SHIFTIN(makeup + 1, data[1], dlci); break; case 3: SHIFTIN(makeup + 0, data[0], dlci); SHIFTIN(makeup + 1, data[1], dlci); SHIFTIN(makeup + 3, data[2], dlci); /* 3 and 2 is correct */ break; case 4: SHIFTIN(makeup + 0, data[0], dlci); SHIFTIN(makeup + 1, data[1], dlci); SHIFTIN(makeup + 2, data[2], dlci); SHIFTIN(makeup + 3, data[3], dlci); break; default: error = EINVAL; goto out; } if (dlci > 1023) { error = EINVAL; goto out; } ctxnum = sc->ALT[dlci]; if ((ctxnum & CTX_VALID) && sc->channel[ctxnum &= CTX_VALUE].hook) { /* Send it */ m_adj(m, alen); NG_SEND_DATA(error, sc->channel[ctxnum].hook, m, meta); return (error); } else { error = ENETDOWN; } out: NG_FREE_DATA(m, meta); return (error); } /* * Shutdown node */ static int ngfrm_rmnode(node_p node) { const sc_p sc = node->private; node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); node->private = NULL; FREE(sc, M_NETGRAPH); ng_unref(node); return (0); } /* * Hook disconnection * * Invalidate the private data associated with this dlci. * For this type, removal of the last link resets tries to destroy the node. */ static int ngfrm_disconnect(hook_p hook) { const sc_p sc = hook->node->private; struct ctxinfo *const cp = hook->private; int dlci; /* If it's a regular dlci hook, then free resources etc.. */ if (cp != NULL) { cp->hook = NULL; dlci = cp->dlci; if (dlci != -1) sc->ALT[dlci] = 0; cp->flags = 0; sc->datahooks--; } if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } Index: head/sys/netgraph/ng_hole.c =================================================================== --- head/sys/netgraph/ng_hole.c (revision 69921) +++ head/sys/netgraph/ng_hole.c (revision 69922) @@ -1,96 +1,95 @@ /* * ng_hole.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 Elisher * * $FreeBSD$ * $Whistle: ng_hole.c,v 1.10 1999/11/01 09:24:51 julian Exp $ */ /* * This node is a 'black hole' that simply discards everything it receives */ #include #include #include #include #include #include #include /* Netgraph methods */ static ng_rcvdata_t ngh_rcvdata; static ng_disconnect_t ngh_disconnect; static struct ng_type typestruct = { NG_VERSION, NG_HOLE_NODE_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ngh_rcvdata, - ngh_rcvdata, ngh_disconnect, NULL }; NETGRAPH_INIT(hole, &typestruct); /* * Receive data */ static int ngh_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { NG_FREE_DATA(m, meta); return 0; } /* * Hook disconnection */ static int ngh_disconnect(hook_p hook) { if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } Index: head/sys/netgraph/ng_iface.c =================================================================== --- head/sys/netgraph/ng_iface.c (revision 69921) +++ head/sys/netgraph/ng_iface.c (revision 69922) @@ -1,792 +1,791 @@ /* * ng_iface.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_iface.c,v 1.33 1999/11/01 09:24:51 julian Exp $ */ /* * This node is also a system networking interface. It has * a hook for each protocol (IP, AppleTalk, IPX, etc). Packets * are simply relayed between the interface and the hooks. * * Interfaces are named ng0, ng1, etc. New nodes take the * first available interface name. * * This node also includes Berkeley packet filter support. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* This struct describes one address family */ struct iffam { sa_family_t family; /* Address family */ const char *hookname; /* Name for hook */ }; typedef const struct iffam *iffam_p; /* List of address families supported by our interface */ const static struct iffam gFamilies[] = { { AF_INET, NG_IFACE_HOOK_INET }, { AF_INET6, NG_IFACE_HOOK_INET6 }, { AF_APPLETALK, NG_IFACE_HOOK_ATALK }, { AF_IPX, NG_IFACE_HOOK_IPX }, { AF_ATM, NG_IFACE_HOOK_ATM }, { AF_NATM, NG_IFACE_HOOK_NATM }, { AF_NS, NG_IFACE_HOOK_NS }, }; #define NUM_FAMILIES (sizeof(gFamilies) / sizeof(*gFamilies)) /* Node private data */ struct ng_iface_private { struct ifnet *ifp; /* Our interface */ int unit; /* Interface unit number */ node_p node; /* Our netgraph node */ hook_p hooks[NUM_FAMILIES]; /* Hook for each address family */ }; typedef struct ng_iface_private *priv_p; /* Interface methods */ static void ng_iface_start(struct ifnet *ifp); static int ng_iface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static int ng_iface_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst, struct rtentry *rt0); static void ng_iface_bpftap(struct ifnet *ifp, struct mbuf *m, sa_family_t family); #ifdef DEBUG static void ng_iface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data); #endif /* Netgraph methods */ static ng_constructor_t ng_iface_constructor; static ng_rcvmsg_t ng_iface_rcvmsg; static ng_shutdown_t ng_iface_rmnode; static ng_newhook_t ng_iface_newhook; static ng_rcvdata_t ng_iface_rcvdata; static ng_disconnect_t ng_iface_disconnect; /* Helper stuff */ static iffam_p get_iffam_from_af(sa_family_t family); static iffam_p get_iffam_from_hook(priv_p priv, hook_p hook); static iffam_p get_iffam_from_name(const char *name); static hook_p *get_hook_from_iffam(priv_p priv, iffam_p iffam); /* Parse type for struct ng_iface_ifname */ static const struct ng_parse_fixedstring_info ng_iface_ifname_info = { NG_IFACE_IFACE_NAME_MAX + 1 }; static const struct ng_parse_type ng_iface_ifname_type = { &ng_parse_fixedstring_type, &ng_iface_ifname_info }; /* Parse type for struct ng_cisco_ipaddr */ static const struct ng_parse_struct_info ng_cisco_ipaddr_type_info = NG_CISCO_IPADDR_TYPE_INFO; static const struct ng_parse_type ng_cisco_ipaddr_type = { &ng_parse_struct_type, &ng_cisco_ipaddr_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_iface_cmds[] = { { NGM_IFACE_COOKIE, NGM_IFACE_GET_IFNAME, "getifname", NULL, &ng_iface_ifname_type }, { NGM_IFACE_COOKIE, NGM_IFACE_POINT2POINT, "point2point", NULL, NULL }, { NGM_IFACE_COOKIE, NGM_IFACE_BROADCAST, "broadcast", NULL, NULL }, { NGM_CISCO_COOKIE, NGM_CISCO_GET_IPADDR, "getipaddr", NULL, &ng_cisco_ipaddr_type }, { 0 } }; /* Node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_IFACE_NODE_TYPE, NULL, ng_iface_constructor, ng_iface_rcvmsg, ng_iface_rmnode, ng_iface_newhook, NULL, NULL, ng_iface_rcvdata, - ng_iface_rcvdata, ng_iface_disconnect, ng_iface_cmds }; NETGRAPH_INIT(iface, &typestruct); /* We keep a bitmap indicating which unit numbers are free. One means the unit number is free, zero means it's taken. */ static int *ng_iface_units = NULL; static int ng_iface_units_len = 0; #define UNITS_BITSPERWORD (sizeof(*ng_iface_units) * NBBY) /************************************************************************ HELPER STUFF ************************************************************************/ /* * Get the family descriptor from the family ID */ static __inline__ iffam_p get_iffam_from_af(sa_family_t family) { iffam_p iffam; int k; for (k = 0; k < NUM_FAMILIES; k++) { iffam = &gFamilies[k]; if (iffam->family == family) return (iffam); } return (NULL); } /* * Get the family descriptor from the hook */ static __inline__ iffam_p get_iffam_from_hook(priv_p priv, hook_p hook) { int k; for (k = 0; k < NUM_FAMILIES; k++) if (priv->hooks[k] == hook) return (&gFamilies[k]); return (NULL); } /* * Get the hook from the iffam descriptor */ static __inline__ hook_p * get_hook_from_iffam(priv_p priv, iffam_p iffam) { return (&priv->hooks[iffam - gFamilies]); } /* * Get the iffam descriptor from the name */ static __inline__ iffam_p get_iffam_from_name(const char *name) { iffam_p iffam; int k; for (k = 0; k < NUM_FAMILIES; k++) { iffam = &gFamilies[k]; if (!strcmp(iffam->hookname, name)) return (iffam); } return (NULL); } /* * Find the first free unit number for a new interface. * Increase the size of the unit bitmap as necessary. */ static __inline__ int ng_iface_get_unit(int *unit) { int index, bit; for (index = 0; index < ng_iface_units_len && ng_iface_units[index] == 0; index++); if (index == ng_iface_units_len) { /* extend array */ int i, *newarray, newlen; newlen = (2 * ng_iface_units_len) + 4; MALLOC(newarray, int *, newlen * sizeof(*ng_iface_units), M_NETGRAPH, M_NOWAIT); if (newarray == NULL) return (ENOMEM); bcopy(ng_iface_units, newarray, ng_iface_units_len * sizeof(*ng_iface_units)); for (i = ng_iface_units_len; i < newlen; i++) newarray[i] = ~0; if (ng_iface_units != NULL) FREE(ng_iface_units, M_NETGRAPH); ng_iface_units = newarray; ng_iface_units_len = newlen; } bit = ffs(ng_iface_units[index]) - 1; KASSERT(bit >= 0 && bit <= UNITS_BITSPERWORD - 1, ("%s: word=%d bit=%d", __FUNCTION__, ng_iface_units[index], bit)); ng_iface_units[index] &= ~(1 << bit); *unit = (index * UNITS_BITSPERWORD) + bit; return (0); } /* * Free a no longer needed unit number. */ static __inline__ void ng_iface_free_unit(int unit) { int index, bit; index = unit / UNITS_BITSPERWORD; bit = unit % UNITS_BITSPERWORD; KASSERT(index < ng_iface_units_len, ("%s: unit=%d len=%d", __FUNCTION__, unit, ng_iface_units_len)); KASSERT((ng_iface_units[index] & (1 << bit)) == 0, ("%s: unit=%d is free", __FUNCTION__, unit)); ng_iface_units[index] |= (1 << bit); /* * XXX We could think about reducing the size of ng_iface_units[] * XXX here if the last portion is all ones */ } /************************************************************************ INTERFACE STUFF ************************************************************************/ /* * Process an ioctl for the virtual interface */ static int ng_iface_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct ifreq *const ifr = (struct ifreq *) data; int s, error = 0; #ifdef DEBUG ng_iface_print_ioctl(ifp, command, data); #endif s = splimp(); switch (command) { /* These two are mostly handled at a higher layer */ case SIOCSIFADDR: ifp->if_flags |= (IFF_UP | IFF_RUNNING); ifp->if_flags &= ~(IFF_OACTIVE); break; case SIOCGIFADDR: break; /* Set flags */ case SIOCSIFFLAGS: /* * If the interface is marked up and stopped, then start it. * If it is marked down and running, then stop it. */ if (ifr->ifr_flags & IFF_UP) { if (!(ifp->if_flags & IFF_RUNNING)) { ifp->if_flags &= ~(IFF_OACTIVE); ifp->if_flags |= IFF_RUNNING; } } else { if (ifp->if_flags & IFF_RUNNING) ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); } break; /* Set the interface MTU */ case SIOCSIFMTU: if (ifr->ifr_mtu > NG_IFACE_MTU_MAX || ifr->ifr_mtu < NG_IFACE_MTU_MIN) error = EINVAL; else ifp->if_mtu = ifr->ifr_mtu; break; /* Stuff that's not supported */ case SIOCADDMULTI: case SIOCDELMULTI: error = 0; break; case SIOCSIFPHYS: error = EOPNOTSUPP; break; default: error = EINVAL; break; } (void) splx(s); return (error); } /* * This routine is called to deliver a packet out the interface. * We simply look at the address family and relay the packet to * the corresponding hook, if it exists and is connected. */ static int ng_iface_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt0) { const priv_p priv = (priv_p) ifp->if_softc; const iffam_p iffam = get_iffam_from_af(dst->sa_family); meta_p meta = NULL; int len, error = 0; /* Check interface flags */ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { m_freem(m); return (ENETDOWN); } /* BPF writes need to be handled specially */ if (dst->sa_family == AF_UNSPEC) { if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) return (ENOBUFS); dst->sa_family = (sa_family_t)*mtod(m, int32_t *); m->m_data += 4; m->m_len -= 4; m->m_pkthdr.len -= 4; } /* Berkeley packet filter */ ng_iface_bpftap(ifp, m, dst->sa_family); /* Check address family to determine hook (if known) */ if (iffam == NULL) { m_freem(m); log(LOG_WARNING, "%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, (int)dst->sa_family); return (EAFNOSUPPORT); } /* Copy length before the mbuf gets invalidated */ len = m->m_pkthdr.len; /* Send packet; if hook is not connected, mbuf will get freed. */ NG_SEND_DATA(error, *get_hook_from_iffam(priv, iffam), m, meta); /* Update stats */ if (error == 0) { ifp->if_obytes += len; ifp->if_opackets++; } return (error); } /* * This routine should never be called */ static void ng_iface_start(struct ifnet *ifp) { printf("%s%d: %s called?", ifp->if_name, ifp->if_unit, __FUNCTION__); } /* * Flash a packet by the BPF (requires prepending 4 byte AF header) * Note the phoney mbuf; this is OK because BPF treats it read-only. */ static void ng_iface_bpftap(struct ifnet *ifp, struct mbuf *m, sa_family_t family) { int32_t family4 = (int32_t)family; struct mbuf m0; KASSERT(family != AF_UNSPEC, ("%s: family=AF_UNSPEC", __FUNCTION__)); if (ifp->if_bpf != NULL) { bzero(&m0, sizeof(m0)); m0.m_next = m; m0.m_len = sizeof(family4); m0.m_data = (char *)&family4; bpf_mtap(ifp, &m0); } } #ifdef DEBUG /* * Display an ioctl to the virtual interface */ static void ng_iface_print_ioctl(struct ifnet *ifp, int command, caddr_t data) { char *str; switch (command & IOC_DIRMASK) { case IOC_VOID: str = "IO"; break; case IOC_OUT: str = "IOR"; break; case IOC_IN: str = "IOW"; break; case IOC_INOUT: str = "IORW"; break; default: str = "IO??"; } log(LOG_DEBUG, "%s%d: %s('%c', %d, char[%d])\n", ifp->if_name, ifp->if_unit, str, IOCGROUP(command), command & 0xff, IOCPARM_LEN(command)); } #endif /* DEBUG */ /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Constructor for a node */ static int ng_iface_constructor(node_p *nodep) { char ifname[NG_IFACE_IFACE_NAME_MAX + 1]; struct ifnet *ifp; node_p node; priv_p priv; int error = 0; /* Allocate node and interface private structures */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT|M_ZERO); if (priv == NULL) return (ENOMEM); MALLOC(ifp, struct ifnet *, sizeof(*ifp), M_NETGRAPH, M_NOWAIT|M_ZERO); if (ifp == NULL) { FREE(priv, M_NETGRAPH); return (ENOMEM); } /* Link them together */ ifp->if_softc = priv; priv->ifp = ifp; /* Get an interface unit number */ if ((error = ng_iface_get_unit(&priv->unit)) != 0) { FREE(ifp, M_NETGRAPH); FREE(priv, M_NETGRAPH); return (error); } /* Call generic node constructor */ if ((error = ng_make_node_common(&typestruct, nodep)) != 0) { ng_iface_free_unit(priv->unit); FREE(ifp, M_NETGRAPH); FREE(priv, M_NETGRAPH); return (error); } node = *nodep; /* Link together node and private info */ node->private = priv; priv->node = node; /* Initialize interface structure */ ifp->if_name = NG_IFACE_IFACE_NAME; ifp->if_unit = priv->unit; ifp->if_output = ng_iface_output; ifp->if_start = ng_iface_start; ifp->if_ioctl = ng_iface_ioctl; ifp->if_watchdog = NULL; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; ifp->if_mtu = NG_IFACE_MTU_DEFAULT; ifp->if_flags = (IFF_SIMPLEX|IFF_POINTOPOINT|IFF_NOARP|IFF_MULTICAST); ifp->if_type = IFT_PROPVIRTUAL; /* XXX */ ifp->if_addrlen = 0; /* XXX */ ifp->if_hdrlen = 0; /* XXX */ ifp->if_baudrate = 64000; /* XXX */ TAILQ_INIT(&ifp->if_addrhead); /* Give this node the same name as the interface (if possible) */ bzero(ifname, sizeof(ifname)); snprintf(ifname, sizeof(ifname), "%s%d", ifp->if_name, ifp->if_unit); if (ng_name_node(node, ifname) != 0) log(LOG_WARNING, "%s: can't acquire netgraph name\n", ifname); /* Attach the interface */ if_attach(ifp); bpfattach(ifp, DLT_NULL, sizeof(u_int)); /* Done */ return (0); } /* * Give our ok for a hook to be added */ static int ng_iface_newhook(node_p node, hook_p hook, const char *name) { const iffam_p iffam = get_iffam_from_name(name); hook_p *hookptr; if (iffam == NULL) return (EPFNOSUPPORT); hookptr = get_hook_from_iffam((priv_p) node->private, iffam); if (*hookptr != NULL) return (EISCONN); *hookptr = hook; return (0); } /* * Receive a control message */ static int ng_iface_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { const priv_p priv = node->private; struct ifnet *const ifp = priv->ifp; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_IFACE_COOKIE: switch (msg->header.cmd) { case NGM_IFACE_GET_IFNAME: { struct ng_iface_ifname *arg; NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } arg = (struct ng_iface_ifname *)resp->data; snprintf(arg->ngif_name, sizeof(arg->ngif_name), "%s%d", ifp->if_name, ifp->if_unit); break; } case NGM_IFACE_POINT2POINT: case NGM_IFACE_BROADCAST: { /* Deny request if interface is UP */ if ((ifp->if_flags & IFF_UP) != 0) return (EBUSY); /* Change flags */ switch (msg->header.cmd) { case NGM_IFACE_POINT2POINT: ifp->if_flags |= IFF_POINTOPOINT; ifp->if_flags &= ~IFF_BROADCAST; break; case NGM_IFACE_BROADCAST: ifp->if_flags &= ~IFF_POINTOPOINT; ifp->if_flags |= IFF_BROADCAST; break; } break; } default: error = EINVAL; break; } break; case NGM_CISCO_COOKIE: switch (msg->header.cmd) { case NGM_CISCO_GET_IPADDR: /* we understand this too */ { struct ifaddr *ifa; /* Return the first configured IP address */ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct ng_cisco_ipaddr *ips; if (ifa->ifa_addr->sa_family != AF_INET) continue; NG_MKRESPONSE(resp, msg, sizeof(ips), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } ips = (struct ng_cisco_ipaddr *)resp->data; ips->ipaddr = ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr; ips->netmask = ((struct sockaddr_in *) ifa->ifa_netmask)->sin_addr; break; } /* No IP addresses on this interface? */ if (ifa == NULL) error = EADDRNOTAVAIL; break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); FREE(msg, M_NETGRAPH); return (error); } /* * Recive data from a hook. Pass the packet to the correct input routine. */ static int ng_iface_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const priv_p priv = hook->node->private; const iffam_p iffam = get_iffam_from_hook(priv, hook); struct ifnet *const ifp = priv->ifp; /* Sanity checks */ KASSERT(iffam != NULL, ("%s: iffam", __FUNCTION__)); KASSERT(m->m_flags & M_PKTHDR, ("%s: not pkthdr", __FUNCTION__)); if (m == NULL) return (EINVAL); if ((ifp->if_flags & IFF_UP) == 0) { NG_FREE_DATA(m, meta); return (ENETDOWN); } /* Update interface stats */ ifp->if_ipackets++; ifp->if_ibytes += m->m_pkthdr.len; /* Note receiving interface */ m->m_pkthdr.rcvif = ifp; /* Berkeley packet filter */ ng_iface_bpftap(ifp, m, iffam->family); /* Ignore any meta-data */ NG_FREE_META(meta); /* Send packet */ return family_enqueue(iffam->family, m); } /* * Shutdown and remove the node and its associated interface. */ static int ng_iface_rmnode(node_p node) { const priv_p priv = node->private; ng_cutlinks(node); ng_unname(node); bpfdetach(priv->ifp); if_detach(priv->ifp); priv->ifp = NULL; ng_iface_free_unit(priv->unit); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); return (0); } /* * Hook disconnection. Note that we do *not* shutdown when all * hooks have been disconnected. */ static int ng_iface_disconnect(hook_p hook) { const priv_p priv = hook->node->private; const iffam_p iffam = get_iffam_from_hook(priv, hook); if (iffam == NULL) panic(__FUNCTION__); *get_hook_from_iffam(priv, iffam) = NULL; return (0); } Index: head/sys/netgraph/ng_ksocket.c =================================================================== --- head/sys/netgraph/ng_ksocket.c (revision 69921) +++ head/sys/netgraph/ng_ksocket.c (revision 69922) @@ -1,909 +1,908 @@ /* * 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 #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 { hook_p hook; struct socket *so; }; typedef struct ng_ksocket_private *priv_p; /* Netgraph node methods */ static ng_constructor_t ng_ksocket_constructor; static ng_rcvmsg_t ng_ksocket_rcvmsg; static ng_shutdown_t ng_ksocket_rmnode; static ng_newhook_t ng_ksocket_newhook; static ng_rcvdata_t ng_ksocket_rcvdata; 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_IP, 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 }, { "ddp", ATPROTO_DDP, PF_APPLETALK }, { "aarp", ATPROTO_AARP, PF_APPLETALK }, { NULL, -1 }, }; /* Helper functions */ 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); /************************************************************************ 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_info ng_parse_generic_sockaddr_type_info = { { { "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_info }; /* 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); return (E2BIG); } if (*buflen < pathoff + pathlen) { FREE(path, M_NETGRAPH); return (ERANGE); } *off += toklen; bcopy(path, sun->sun_path, pathlen); sun->sun_len = pathoff + pathlen; FREE(path, M_NETGRAPH); 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); 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_info ng_ksocket_sockopt_type_info = 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_info, }; /* 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_sockaddr_type }, { NGM_KSOCKET_COOKIE, NGM_KSOCKET_CONNECT, "connect", &ng_ksocket_sockaddr_type, NULL }, { 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 = { NG_VERSION, NG_KSOCKET_NODE_TYPE, NULL, ng_ksocket_constructor, ng_ksocket_rcvmsg, ng_ksocket_rmnode, ng_ksocket_newhook, NULL, NULL, ng_ksocket_rcvdata, - ng_ksocket_rcvdata, ng_ksocket_disconnect, 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 */ static int ng_ksocket_constructor(node_p *nodep) { priv_p priv; int error; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv == NULL) return (ENOMEM); /* Call generic node constructor */ if ((error = ng_make_node_common(&ng_ksocket_typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = 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 proc *p = curproc ? curproc : &proc0; /* XXX broken */ const priv_p priv = node->private; char *s1, *s2, name[NG_HOOKLEN+1]; int family, type, protocol, error; /* Check if we're already connected */ if (priv->hook != NULL) return (EISCONN); /* 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'; if ((family = ng_ksocket_parse(ng_ksocket_families, s1, 0)) == -1) return (EINVAL); s1 = s2; if ((s2 = index(s1, '/')) == NULL) return (EINVAL); *s2++ = '\0'; if ((type = ng_ksocket_parse(ng_ksocket_types, s1, 0)) == -1) return (EINVAL); s1 = s2; if ((protocol = ng_ksocket_parse(ng_ksocket_protos, s1, family)) == -1) return (EINVAL); /* Create the socket */ if ((error = socreate(family, &priv->so, type, protocol, p)) != 0) return (error); /* XXX call soreserve() ? */ /* Add our hook for incoming data */ priv->so->so_upcallarg = (caddr_t)node; priv->so->so_upcall = ng_ksocket_incoming; priv->so->so_rcv.sb_flags |= SB_UPCALL; /* OK */ priv->hook = hook; return (0); } /* * Receive a control message */ static int ng_ksocket_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rptr, hook_p lasthook) { struct proc *p = curproc ? curproc : &proc0; /* XXX broken */ const priv_p priv = node->private; struct socket *const so = priv->so; struct ng_mesg *resp = NULL; int error = 0; 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, p); break; } case NGM_KSOCKET_LISTEN: { /* Sanity check */ if (msg->header.arglen != sizeof(int)) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Listen */ if ((error = solisten(so, *((int *)msg->data), p)) != 0) break; /* Notify sender when we get a connection attempt */ /* XXX implement me */ error = ENODEV; break; } case NGM_KSOCKET_ACCEPT: { /* Sanity check */ if (msg->header.arglen != 0) ERROUT(EINVAL); if (so == NULL) ERROUT(ENXIO); /* Accept on the socket in a non-blocking way */ /* Create a new ksocket node for the new connection */ /* Return a response with the peer's sockaddr and the absolute name of the newly created node */ /* XXX implement me */ error = ENODEV; 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, p)) != 0) { so->so_state &= ~SS_ISCONNECTING; ERROUT(error); } if ((so->so_state & SS_ISCONNECTING) != 0) /* Notify sender when we connect */ /* XXX implement me */ 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_p = p; 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) { FREE(resp, M_NETGRAPH); 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_p = p; error = sosetopt(so, &sopt); break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /* * Receive incoming data on our hook. Send it out the socket. */ static int ng_ksocket_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { struct proc *p = curproc ? curproc : &proc0; /* XXX broken */ const node_p node = hook->node; const priv_p priv = node->private; struct socket *const so = priv->so; int error; NG_FREE_META(meta); error = (*so->so_proto->pr_usrreqs->pru_sosend)(so, 0, 0, m, 0, 0, p); return (error); } /* * Destroy node */ static int ng_ksocket_rmnode(node_p node) { const priv_p priv = node->private; /* Close our socket (if any) */ if (priv->so != NULL) { priv->so->so_upcall = NULL; priv->so->so_rcv.sb_flags &= ~SB_UPCALL; soclose(priv->so); priv->so = NULL; } /* Take down netgraph node */ node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); bzero(priv, sizeof(*priv)); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); /* let the node escape */ return (0); } /* * Hook disconnection */ static int ng_ksocket_disconnect(hook_p hook) { KASSERT(hook->node->numhooks == 0, ("%s: numhooks=%d?", __FUNCTION__, hook->node->numhooks)); ng_rmnode(hook->node); return (0); } /************************************************************************ HELPER STUFF ************************************************************************/ /* * When incoming data is appended to the socket, we get notified here. */ static void ng_ksocket_incoming(struct socket *so, void *arg, int waitflag) { const node_p node = arg; const priv_p priv = node->private; meta_p meta = NULL; struct mbuf *m; struct uio auio; int s, flags, error; s = splnet(); /* Sanity check */ if ((node->flags & NG_INVALID) != 0) { splx(s); return; } KASSERT(so == priv->so, ("%s: wrong socket", __FUNCTION__)); KASSERT(priv->hook != NULL, ("%s: no hook", __FUNCTION__)); /* Read and forward available mbuf's */ auio.uio_procp = NULL; auio.uio_resid = 1000000000; flags = MSG_DONTWAIT; do { if ((error = (*so->so_proto->pr_usrreqs->pru_soreceive) (so, (struct sockaddr **)0, &auio, &m, (struct mbuf **)0, &flags)) == 0 && m != NULL) { struct mbuf *n; /* 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; n = n->m_next) m->m_pkthdr.len += n->m_len; NG_SEND_DATA(error, priv->hook, m, meta); } } while (error == 0 && m != NULL); splx(s); } /* * 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_lmi.c =================================================================== --- head/sys/netgraph/ng_lmi.c (revision 69921) +++ head/sys/netgraph/ng_lmi.c (revision 69922) @@ -1,1092 +1,1091 @@ /* * 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_rmnode; 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, meta_p meta); static struct ng_type typestruct = { NG_VERSION, NG_LMI_NODE_TYPE, NULL, nglmi_constructor, nglmi_rcvmsg, nglmi_rmnode, nglmi_newhook, NULL, NULL, nglmi_rcvdata, - nglmi_rcvdata, nglmi_disconnect, NULL }; 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 *nodep) { sc_p sc; int error = 0; MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); if (sc == NULL) return (ENOMEM); callout_handle_init(&sc->handle); if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(sc, M_NETGRAPH); return (error); } (*nodep)->private = sc; sc->protoname = NAME_NONE; sc->node = *nodep; 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 = node->private; if (strcmp(name, NG_LMI_HOOK_DEBUG) == 0) { hook->private = 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; hook->private = node->private; 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; hook->private = node->private; 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; hook->private = node->private; 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; hook->private = node->private; 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; hook->private = node->private; 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; 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( struct ng_meta) + META_PAD, * M_NETGRAPH, M_NOWAIT); */ MALLOC(meta, meta_p, sizeof(*meta) + META_PAD, M_NETGRAPH, 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; } 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); /* 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, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook) { int error = 0; sc_p sc = node->private; 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; } FREE(msg, M_NETGRAPH); 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, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { sc_p sc = hook->node->private; u_char *data; unsigned short dlci; u_short packetlen; int resptype_seen = 0; int seq_seen = 0; if (hook->private == 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); NG_FREE_META(meta); return (0); } if (nglmi_checkdata(hook, m, meta) == 0) return (0); /* pass the first 4 bytes (already checked in the nglmi_checkdata()) */ data = mtod(m, 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_DATA(m, meta); return (0); drop: NG_FREE_DATA(m, meta); 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, meta_p meta) { sc_p sc = hook->node->private; 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, 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; u_char *bp = mtod(m, 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; u_char *bp = mtod(m, 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_DATA(m, meta); return (0); } /* * Do local shutdown processing.. * Cut any remaining links and free our local resources. */ static int nglmi_rmnode(node_p node) { const sc_p sc = node->private; node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); node->private = NULL; ng_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 = hook->node->private; /* OK to remove debug hook(s) */ if (hook->private == NULL) return (0); /* Stop timer if it's currently active */ if (sc->flags & SCF_CONNECTED) untimeout(LMI_ticker, sc, sc->handle); /* Self-destruct */ ng_rmnode(hook->node); return (0); } Index: head/sys/netgraph/ng_message.h =================================================================== --- head/sys/netgraph/ng_message.h (revision 69921) +++ head/sys/netgraph/ng_message.h (revision 69922) @@ -1,328 +1,419 @@ /* * ng_message.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: ng_message.h,v 1.12 1999/01/25 01:17:44 archie Exp $ */ #ifndef _NETGRAPH_NG_MESSAGE_H_ #define _NETGRAPH_NG_MESSAGE_H_ 1 /* ASCII string size limits */ #define NG_TYPELEN 15 /* max type name len (16 with null) */ #define NG_HOOKLEN 15 /* max hook name len (16 with null) */ #define NG_NODELEN 15 /* max node name len (16 with null) */ #define NG_PATHLEN 511 /* max path len (512 with null) */ #define NG_CMDSTRLEN 15 /* max command string (16 with null) */ #define NG_TEXTRESPONSE 1024 /* allow this length for a text response */ /* A netgraph message */ struct ng_mesg { struct ng_msghdr { u_char version; /* must == NG_VERSION */ u_char spare; /* pad to 2 bytes */ u_int16_t arglen; /* length of data */ u_int32_t flags; /* message status */ u_int32_t token; /* match with reply */ u_int32_t typecookie; /* node's type cookie */ u_int32_t cmd; /* command identifier */ u_char cmdstr[NG_CMDSTRLEN+1]; /* cmd string + \0 */ } header; char data[0]; /* placeholder for actual data */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_NG_MESG_INFO(dtype) { \ { \ { "version", &ng_parse_uint8_type }, \ { "spare", &ng_parse_uint8_type }, \ { "arglen", &ng_parse_uint16_type }, \ { "flags", &ng_parse_hint32_type }, \ { "token", &ng_parse_uint32_type }, \ { "typecookie", &ng_parse_uint32_type }, \ { "cmd", &ng_parse_uint32_type }, \ { "cmdstr", &ng_parse_cmdbuf_type }, \ { "data", (dtype) }, \ { NULL }, \ } \ } /* Negraph type binary compatibility field */ -#define NG_VERSION 3 +#define NG_VERSION 4 /* Flags field flags */ #define NGF_ORIG 0x0000 /* the msg is the original request */ #define NGF_RESP 0x0001 /* the message is a response */ /* Type of a unique node ID */ #define ng_ID_t unsigned int /* * Here we describe the "generic" messages that all nodes inherently * understand. With the exception of NGM_TEXT_STATUS, these are handled * automatically by the base netgraph code. */ /* Generic message type cookie */ #define NGM_GENERIC_COOKIE 851672668 /* Generic messages defined for this type cookie */ #define NGM_SHUTDOWN 1 /* shut down node */ #define NGM_MKPEER 2 /* create and attach a peer node */ #define NGM_CONNECT 3 /* connect two nodes */ #define NGM_NAME 4 /* give a node a name */ #define NGM_RMHOOK 5 /* break a connection btw. two nodes */ #define NGM_NODEINFO 6 /* get nodeinfo for the target */ #define NGM_LISTHOOKS 7 /* get list of hooks on node */ #define NGM_LISTNAMES 8 /* list all globally named nodes */ #define NGM_LISTNODES 9 /* list all nodes, named and unnamed */ #define NGM_LISTTYPES 10 /* list all installed node types */ #define NGM_TEXT_STATUS 11 /* (optional) get text status report */ #define NGM_BINARY2ASCII 12 /* convert struct ng_mesg to ascii */ #define NGM_ASCII2BINARY 13 /* convert ascii to struct ng_mesg */ #define NGM_TEXT_CONFIG 14 /* (optional) get/set text config */ +/* + * Flow control and intra node control messages. + * These are routed between nodes to allow flow control and to allow + * events to be passed around the graph. + * There will be some form of default handling for these but I + * do not yet know what it is.. + */ + +/* Generic message type cookie */ +#define NGM_FLOW_COOKIE 851672669 /* temp for debugging */ + +/* Upstream messages */ +#define NGM_LINK_IS_UP 32 /* e.g. carrier found - no data */ +#define NGM_LINK_IS_DOWN 33 /* carrier lost, includes queue state */ +#define NGM_HIGH_WATER_PASSED 34 /* includes queue state */ +#define NGM_LOW_WATER_PASSED 35 /* includes queue state */ +#define NGM_SYNC_QUEUE_STATE 36 /* sync response from sending packet */ + +/* Downstream messages */ +#define NGM_DROP_LINK 41 /* drop DTR, etc. - stay in the graph */ +#define NGM_RAISE LINK 42 /* if you previously dropped it */ +#define NGM_FLUSH_QUEUE 43 /* no data */ +#define NGM_GET_BANDWIDTH 44 /* either real or measured */ +#define NGM_SET_XMIT_Q_LIMITS 45 /* includes queue state */ +#define NGM_GET_XMIT_Q_LIMITS 46 /* returns queue state */ +#define NGM_MICROMANAGE 47 /* We want sync. queue state reply + for each packet sent down */ +#define NGM_SET_FLOW_MANAGER 48 /* send flow control here */ /* Structure used for NGM_MKPEER */ struct ngm_mkpeer { char type[NG_TYPELEN + 1]; /* peer type */ char ourhook[NG_HOOKLEN + 1]; /* hook name */ char peerhook[NG_HOOKLEN + 1]; /* peer hook name */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_MKPEER_INFO() { \ { \ { "type", &ng_parse_typebuf_type }, \ { "ourhook", &ng_parse_hookbuf_type }, \ { "peerhook", &ng_parse_hookbuf_type }, \ { NULL }, \ } \ } /* Structure used for NGM_CONNECT */ struct ngm_connect { char path[NG_PATHLEN + 1]; /* peer path */ char ourhook[NG_HOOKLEN + 1]; /* hook name */ char peerhook[NG_HOOKLEN + 1]; /* peer hook name */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_CONNECT_INFO() { \ { \ { "path", &ng_parse_pathbuf_type }, \ { "ourhook", &ng_parse_hookbuf_type }, \ { "peerhook", &ng_parse_hookbuf_type }, \ { NULL }, \ } \ } /* Structure used for NGM_NAME */ struct ngm_name { char name[NG_NODELEN + 1]; /* node name */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_NAME_INFO() { \ { \ { "name", &ng_parse_nodebuf_type }, \ { NULL }, \ } \ } /* Structure used for NGM_RMHOOK */ struct ngm_rmhook { char ourhook[NG_HOOKLEN + 1]; /* hook name */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_RMHOOK_INFO() { \ { \ { "hook", &ng_parse_hookbuf_type }, \ { NULL }, \ } \ } /* Structure used for NGM_NODEINFO */ struct nodeinfo { char name[NG_NODELEN + 1]; /* node name (if any) */ char type[NG_TYPELEN + 1]; /* peer type */ ng_ID_t id; /* unique identifier */ u_int32_t hooks; /* number of active hooks */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_NODEINFO_INFO() { \ { \ { "name", &ng_parse_nodebuf_type }, \ { "type", &ng_parse_typebuf_type }, \ { "id", &ng_parse_hint32_type }, \ { "hooks", &ng_parse_uint32_type }, \ { NULL }, \ } \ } /* Structure used for NGM_LISTHOOKS */ struct linkinfo { char ourhook[NG_HOOKLEN + 1]; /* hook name */ char peerhook[NG_HOOKLEN + 1]; /* peer hook */ struct nodeinfo nodeinfo; }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_LINKINFO_INFO(nitype) { \ { \ { "ourhook", &ng_parse_hookbuf_type }, \ { "peerhook", &ng_parse_hookbuf_type }, \ { "nodeinfo", (nitype) }, \ { NULL }, \ } \ } struct hooklist { struct nodeinfo nodeinfo; /* node information */ struct linkinfo link[0]; /* info about each hook */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_HOOKLIST_INFO(nitype,litype) { \ { \ { "nodeinfo", (nitype) }, \ { "linkinfo", (litype) }, \ { NULL }, \ } \ } /* Structure used for NGM_LISTNAMES/NGM_LISTNODES */ struct namelist { u_int32_t numnames; struct nodeinfo nodeinfo[0]; }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_LISTNODES_INFO(niarraytype) { \ { \ { "numnames", &ng_parse_uint32_type }, \ { "nodeinfo", (niarraytype) }, \ { NULL }, \ } \ } /* Structure used for NGM_LISTTYPES */ struct typeinfo { char type_name[NG_TYPELEN + 1]; /* name of type */ u_int32_t numnodes; /* number alive */ }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_TYPEINFO_INFO() { \ { \ { "typename", &ng_parse_typebuf_type }, \ { "numnodes", &ng_parse_uint32_type }, \ { NULL }, \ } \ } struct typelist { u_int32_t numtypes; struct typeinfo typeinfo[0]; }; /* Keep this in sync with the above structure definition */ #define NG_GENERIC_TYPELIST_INFO(tiarraytype) { \ { \ { "numtypes", &ng_parse_uint32_type }, \ { "typeinfo", (tiarraytype) }, \ { NULL }, \ } \ } + +struct ngm_bandwidth { + u_int64_t nominal_in; + u_int64_t seen_in; + u_int64_t nominal_out; + u_int64_t seen_out; +}; + +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_BANDWIDTH_INFO() { \ + { \ + { "nominal_in", &ng_parse_uint64_type }, \ + { "seen_in", &ng_parse_uint64_type }, \ + { "nominal_out", &ng_parse_uint64_type }, \ + { "seen_out", &ng_parse_uint64_type }, \ + { NULL }, \ + } \ +} + +/* + * Information about a node's 'output' queue. + * This is NOT the netgraph input queueing mechanism, + * but rather any queue the node may implement internally + * This has to consider ALTQ if we are to work with it. + * As far as I can see, ALTQ counts PACKETS, not bytes. + * If ALTQ has several queues and one has passed a watermark + * we should have the priority of that queue be real (and not -1) + * XXX ALTQ stuff is just an idea..... + */ +struct ngm_queue_state { + u_int queue_priority; /* maybe only low-pri is full. -1 = all*/ + u_int max_queuelen_bytes; + u_int max_queuelen_packets; + u_int low_watermark; + u_int high_watermark; + u_int current; +}; + +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_QUEUE_INFO() { \ + { \ + { "max_queuelen_bytes", &ng_parse_uint_type }, \ + { "max_queuelen_packets", &ng_parse_uint_type }, \ + { "high_watermark", &ng_parse_uint_type }, \ + { "low_watermark", &ng_parse_uint_type }, \ + { "current", &ng_parse_uint_type }, \ + { NULL }, \ + } \ +} + +/* Tell a node who to send async flow control info to. */ +struct flow_manager { + ng_ID_t id; /* unique identifier */ +}; + +/* Keep this in sync with the above structure definition */ +#define NG_GENERIC_FLOW_MANAGER_INFO() { \ + { \ + { "id", &ng_parse_hint32_type }, \ + { NULL }, \ + } \ +} + /* * For netgraph nodes that are somehow associated with file descriptors * (e.g., a device that has a /dev entry and is also a netgraph node), * we define a generic ioctl for requesting the corresponding nodeinfo * structure and for assigning a name (if there isn't one already). * * For these to you need to also #include . */ #define NGIOCGINFO _IOR('N', 40, struct nodeinfo) /* get node info */ #define NGIOCSETNAME _IOW('N', 41, struct ngm_name) /* set node name */ #ifdef _KERNEL /* * Allocate and initialize a netgraph message "msg" with "len" * extra bytes of argument. Sets "msg" to NULL if fails. * Does not initialize token. */ #define NG_MKMESSAGE(msg, cookie, cmdid, len, how) \ do { \ MALLOC((msg), struct ng_mesg *, sizeof(struct ng_mesg) \ + (len), M_NETGRAPH, (how) | M_ZERO); \ if ((msg) == NULL) \ break; \ (msg)->header.version = NG_VERSION; \ (msg)->header.typecookie = (cookie); \ (msg)->header.cmd = (cmdid); \ (msg)->header.arglen = (len); \ strncpy((msg)->header.cmdstr, #cmdid, \ sizeof((msg)->header.cmdstr) - 1); \ } while (0) /* * Allocate and initialize a response "rsp" to a message "msg" * with "len" extra bytes of argument. Sets "rsp" to NULL if fails. */ #define NG_MKRESPONSE(rsp, msg, len, how) \ do { \ MALLOC((rsp), struct ng_mesg *, sizeof(struct ng_mesg) \ + (len), M_NETGRAPH, (how) | M_ZERO); \ if ((rsp) == NULL) \ break; \ (rsp)->header.version = NG_VERSION; \ (rsp)->header.arglen = (len); \ (rsp)->header.token = (msg)->header.token; \ (rsp)->header.typecookie = (msg)->header.typecookie; \ (rsp)->header.cmd = (msg)->header.cmd; \ bcopy((msg)->header.cmdstr, (rsp)->header.cmdstr, \ sizeof((rsp)->header.cmdstr)); \ (rsp)->header.flags |= NGF_RESP; \ } while (0) #endif /* _KERNEL */ #endif /* _NETGRAPH_NG_MESSAGE_H_ */ Index: head/sys/netgraph/ng_mppc.c =================================================================== --- head/sys/netgraph/ng_mppc.c (revision 69921) +++ head/sys/netgraph/ng_mppc.c (revision 69922) @@ -1,795 +1,796 @@ /* * ng_mppc.c * * Copyright (c) 1996-2000 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_mppc.c,v 1.4 1999/11/25 00:10:12 archie Exp $ * $FreeBSD$ */ /* * Microsoft PPP compression (MPPC) and encryption (MPPE) netgraph node type. * * You must define one or both of the NETGRAPH_MPPC_COMPRESSION and/or * NETGRAPH_MPPC_ENCRYPTION options for this node type to be useful. */ #include #include #include #include #include #include #include #include #include #include #include "opt_netgraph.h" #if !defined(NETGRAPH_MPPC_COMPRESSION) && !defined(NETGRAPH_MPPC_ENCRYPTION) #error Need either NETGRAPH_MPPC_COMPRESSION or NETGRAPH_MPPC_ENCRYPTION #endif #ifdef NETGRAPH_MPPC_COMPRESSION /* XXX this file doesn't exist yet, but hopefully someday it will... */ #include #endif #ifdef NETGRAPH_MPPC_ENCRYPTION #include #endif #include /* Decompression blowup */ #define MPPC_DECOMP_BUFSIZE 8092 /* allocate buffer this big */ #define MPPC_DECOMP_SAFETY 100 /* plus this much margin */ /* MPPC/MPPE header length */ #define MPPC_HDRLEN 2 /* Key length */ #define KEYLEN(b) (((b) & MPPE_128) ? 16 : 8) /* What sequence number jump is too far */ #define MPPC_INSANE_JUMP 256 /* MPPC packet header bits */ #define MPPC_FLAG_FLUSHED 0x8000 /* xmitter reset state */ #define MPPC_FLAG_RESTART 0x4000 /* compress history restart */ #define MPPC_FLAG_COMPRESSED 0x2000 /* packet is compresed */ #define MPPC_FLAG_ENCRYPTED 0x1000 /* packet is encrypted */ #define MPPC_CCOUNT_MASK 0x0fff /* sequence number mask */ #define MPPE_UPDATE_MASK 0xff /* coherency count when we're */ #define MPPE_UPDATE_FLAG 0xff /* supposed to update key */ #define MPPC_COMP_OK 0x05 #define MPPC_DECOMP_OK 0x05 /* Per direction info */ struct ng_mppc_dir { struct ng_mppc_config cfg; /* configuration */ hook_p hook; /* netgraph hook */ u_int16_t cc:12; /* coherency count */ u_char flushed; /* clean history (xmit only) */ #ifdef NETGRAPH_MPPC_COMPRESSION u_char *history; /* compression history */ #endif #ifdef NETGRAPH_MPPC_ENCRYPTION u_char key[MPPE_KEY_LEN]; /* session key */ struct rc4_state rc4; /* rc4 state */ #endif }; /* Node private data */ struct ng_mppc_private { struct ng_mppc_dir xmit; /* compress/encrypt config */ struct ng_mppc_dir recv; /* decompress/decrypt config */ char *ctrlpath; /* path to controlling node */ }; typedef struct ng_mppc_private *priv_p; /* Netgraph node methods */ static ng_constructor_t ng_mppc_constructor; static ng_rcvmsg_t ng_mppc_rcvmsg; static ng_shutdown_t ng_mppc_rmnode; static ng_newhook_t ng_mppc_newhook; static ng_rcvdata_t ng_mppc_rcvdata; static ng_disconnect_t ng_mppc_disconnect; /* Helper functions */ static int ng_mppc_compress(node_p node, struct mbuf *m, struct mbuf **resultp); static int ng_mppc_decompress(node_p node, struct mbuf *m, struct mbuf **resultp); static void ng_mppc_getkey(const u_char *h, u_char *h2, int len); static void ng_mppc_updatekey(u_int32_t bits, u_char *key0, u_char *key, struct rc4_state *rc4); static void ng_mppc_reset_req(node_p node); /* Node type descriptor */ static struct ng_type ng_mppc_typestruct = { NG_VERSION, NG_MPPC_NODE_TYPE, NULL, ng_mppc_constructor, ng_mppc_rcvmsg, ng_mppc_rmnode, ng_mppc_newhook, NULL, NULL, ng_mppc_rcvdata, - ng_mppc_rcvdata, ng_mppc_disconnect, NULL }; NETGRAPH_INIT(mppc, &ng_mppc_typestruct); /* Fixed bit pattern to weaken keysize down to 40 bits */ static const u_char ng_mppe_weakenkey[3] = { 0xd1, 0x26, 0x9e }; #define ERROUT(x) do { error = (x); goto done; } while (0) /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Node type constructor */ static int ng_mppc_constructor(node_p *nodep) { priv_p priv; int error; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv == NULL) return (ENOMEM); /* Call generic node constructor */ if ((error = ng_make_node_common(&ng_mppc_typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Done */ return (0); } /* * Give our OK for a hook to be added */ static int ng_mppc_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; hook_p *hookPtr; /* Check hook name */ if (strcmp(name, NG_MPPC_HOOK_COMP) == 0) hookPtr = &priv->xmit.hook; else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0) hookPtr = &priv->recv.hook; else return (EINVAL); /* See if already connected */ if (*hookPtr != NULL) return (EISCONN); /* OK */ *hookPtr = hook; return (0); } /* * Receive a control message */ static int ng_mppc_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rptr, hook_p lasthook) { const priv_p priv = node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_MPPC_COOKIE: switch (msg->header.cmd) { case NGM_MPPC_CONFIG_COMP: case NGM_MPPC_CONFIG_DECOMP: { struct ng_mppc_config *const cfg = (struct ng_mppc_config *)msg->data; const int isComp = msg->header.cmd == NGM_MPPC_CONFIG_COMP; struct ng_mppc_dir *const d = isComp ? &priv->xmit : &priv->recv; /* Check configuration */ if (msg->header.arglen != sizeof(*cfg)) ERROUT(EINVAL); if (cfg->enable) { if ((cfg->bits & ~MPPC_VALID_BITS) != 0) ERROUT(EINVAL); #ifndef NETGRAPH_MPPC_COMPRESSION if ((cfg->bits & MPPC_BIT) != 0) ERROUT(EPROTONOSUPPORT); #endif #ifndef NETGRAPH_MPPC_ENCRYPTION if ((cfg->bits & MPPE_BITS) != 0) ERROUT(EPROTONOSUPPORT); #endif } else cfg->bits = 0; /* Save return address so we can send reset-req's */ if (priv->ctrlpath != NULL) { FREE(priv->ctrlpath, M_NETGRAPH); priv->ctrlpath = NULL; } if (!isComp && raddr != NULL) { MALLOC(priv->ctrlpath, char *, strlen(raddr) + 1, M_NETGRAPH, M_NOWAIT); if (priv->ctrlpath == NULL) ERROUT(ENOMEM); strcpy(priv->ctrlpath, raddr); } /* Configuration is OK, reset to it */ d->cfg = *cfg; #ifdef NETGRAPH_MPPC_COMPRESSION /* Initialize state buffers for compression */ if (d->history != NULL) { FREE(d->history, M_NETGRAPH); d->history = NULL; } if ((cfg->bits & MPPC_BIT) != 0) { MALLOC(d->history, u_char *, isComp ? MPPC_SizeOfCompressionHistory() : MPPC_SizeOfDecompressionHistory(), M_NETGRAPH, M_NOWAIT); if (d->history == NULL) ERROUT(ENOMEM); if (isComp) MPPC_InitCompressionHistory(d->history); else { MPPC_InitDecompressionHistory( d->history); } } #endif #ifdef NETGRAPH_MPPC_ENCRYPTION /* Generate initial session keys for encryption */ if ((cfg->bits & MPPE_BITS) != 0) { const int keylen = KEYLEN(cfg->bits); bcopy(cfg->startkey, d->key, keylen); ng_mppc_getkey(cfg->startkey, d->key, keylen); if ((cfg->bits & MPPE_128) == 0) { bcopy(&ng_mppe_weakenkey, d->key, sizeof(ng_mppe_weakenkey)); } rc4_init(&d->rc4, d->key, keylen); } #endif /* Initialize other state */ d->cc = 0; d->flushed = 0; break; } case NGM_MPPC_RESETREQ: ng_mppc_reset_req(node); break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /* * Receive incoming data on our hook. */ static int ng_mppc_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const node_p node = hook->node; const priv_p priv = node->private; struct mbuf *out; int error; /* Compress and/or encrypt */ if (hook == priv->xmit.hook) { if (!priv->xmit.cfg.enable) { NG_FREE_DATA(m, meta); return (ENXIO); } if ((error = ng_mppc_compress(node, m, &out)) != 0) { NG_FREE_DATA(m, meta); return(error); } m_freem(m); NG_SEND_DATA(error, priv->xmit.hook, out, meta); return (error); } /* Decompress and/or decrypt */ if (hook == priv->recv.hook) { if (!priv->recv.cfg.enable) { NG_FREE_DATA(m, meta); return (ENXIO); } if ((error = ng_mppc_decompress(node, m, &out)) != 0) { NG_FREE_DATA(m, meta); if (error == EINVAL && priv->ctrlpath != NULL) { struct ng_mesg *msg; /* Need to send a reset-request */ NG_MKMESSAGE(msg, NGM_MPPC_COOKIE, NGM_MPPC_RESETREQ, 0, M_NOWAIT); if (msg == NULL) return (error); - ng_send_msg(node, msg, priv->ctrlpath, NULL); + /* XXX can we use a hook instead of ctrlpath? */ + ng_send_msg(node, msg, priv->ctrlpath, + NULL, NULL, NULL); } return (error); } m_freem(m); NG_SEND_DATA(error, priv->recv.hook, out, meta); return (error); } /* Oops */ panic("%s: unknown hook", __FUNCTION__); } /* * Destroy node */ static int ng_mppc_rmnode(node_p node) { const priv_p priv = node->private; /* Take down netgraph node */ node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); if (priv->ctrlpath != NULL) FREE(priv->ctrlpath, M_NETGRAPH); #ifdef NETGRAPH_MPPC_COMPRESSION if (priv->xmit.history != NULL) FREE(priv->xmit.history, M_NETGRAPH); if (priv->recv.history != NULL) FREE(priv->recv.history, M_NETGRAPH); #endif bzero(priv, sizeof(*priv)); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); /* let the node escape */ return (0); } /* * Hook disconnection */ static int ng_mppc_disconnect(hook_p hook) { const node_p node = hook->node; const priv_p priv = node->private; /* Zero out hook pointer */ if (hook == priv->xmit.hook) priv->xmit.hook = NULL; if (hook == priv->recv.hook) priv->recv.hook = NULL; /* Go away if no longer connected */ if (node->numhooks == 0) ng_rmnode(node); return (0); } /************************************************************************ HELPER STUFF ************************************************************************/ /* * Compress/encrypt a packet and put the result in a new mbuf at *resultp. * The original mbuf is not free'd. */ static int ng_mppc_compress(node_p node, struct mbuf *m, struct mbuf **resultp) { const priv_p priv = node->private; struct ng_mppc_dir *const d = &priv->xmit; u_char *inbuf, *outbuf; int outlen, inlen; u_int16_t header; /* Initialize */ *resultp = NULL; header = d->cc; if (d->flushed) { header |= MPPC_FLAG_FLUSHED; d->flushed = 0; } /* Work with contiguous regions of memory */ inlen = m->m_pkthdr.len; MALLOC(inbuf, u_char *, inlen, M_NETGRAPH, M_NOWAIT); if (inbuf == NULL) return (ENOMEM); m_copydata(m, 0, inlen, (caddr_t)inbuf); if ((d->cfg.bits & MPPC_BIT) != 0) outlen = MPPC_MAX_BLOWUP(inlen); else outlen = MPPC_HDRLEN + inlen; MALLOC(outbuf, u_char *, outlen, M_NETGRAPH, M_NOWAIT); if (outbuf == NULL) { FREE(inbuf, M_NETGRAPH); return (ENOMEM); } /* Compress "inbuf" into "outbuf" (if compression enabled) */ #ifdef NETGRAPH_MPPC_COMPRESSION if ((d->cfg.bits & MPPC_BIT) != 0) { u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS; u_char *source, *dest; u_long sourceCnt, destCnt; int rtn; /* Prepare to compress */ source = inbuf; sourceCnt = inlen; dest = outbuf + MPPC_HDRLEN; destCnt = outlen - MPPC_HDRLEN; if ((d->cfg.bits & MPPE_STATELESS) == 0) flags |= MPPC_SAVE_HISTORY; /* Compress */ rtn = MPPC_Compress(&source, &dest, &sourceCnt, &destCnt, d->history, flags, 0); /* Check return value */ KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __FUNCTION__)); if ((rtn & MPPC_EXPANDED) == 0 && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) { outlen -= destCnt; header |= MPPC_FLAG_COMPRESSED; if ((rtn & MPPC_RESTART_HISTORY) != 0) header |= MPPC_FLAG_RESTART; } d->flushed = (rtn & MPPC_EXPANDED) != 0 || (flags & MPPC_SAVE_HISTORY) == 0; } #endif /* If we did not compress this packet, copy it to output buffer */ if ((header & MPPC_FLAG_COMPRESSED) == 0) { bcopy(inbuf, outbuf + MPPC_HDRLEN, inlen); outlen = MPPC_HDRLEN + inlen; } FREE(inbuf, M_NETGRAPH); /* Always set the flushed bit in stateless mode */ if ((d->cfg.bits & MPPE_STATELESS) != 0) header |= MPPC_FLAG_FLUSHED; /* Now encrypt packet (if encryption enabled) */ #ifdef NETGRAPH_MPPC_ENCRYPTION if ((d->cfg.bits & MPPE_BITS) != 0) { /* Set header bits; need to reset key if we say we did */ header |= MPPC_FLAG_ENCRYPTED; if ((header & MPPC_FLAG_FLUSHED) != 0) rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); /* Update key if it's time */ if ((d->cfg.bits & MPPE_STATELESS) != 0 || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) { ng_mppc_updatekey(d->cfg.bits, d->cfg.startkey, d->key, &d->rc4); } /* Encrypt packet */ rc4_crypt(&d->rc4, outbuf + MPPC_HDRLEN, outbuf + MPPC_HDRLEN, outlen - MPPC_HDRLEN); } #endif /* Update sequence number */ d->cc++; /* Install header */ *((u_int16_t *)outbuf) = htons(header); /* Return packet in an mbuf */ *resultp = m_devget((caddr_t)outbuf, outlen, 0, NULL, NULL); FREE(outbuf, M_NETGRAPH); return (*resultp == NULL ? ENOBUFS : 0); } /* * Decompress/decrypt packet and put the result in a new mbuf at *resultp. * The original mbuf is not free'd. */ static int ng_mppc_decompress(node_p node, struct mbuf *m, struct mbuf **resultp) { const priv_p priv = node->private; struct ng_mppc_dir *const d = &priv->recv; u_int16_t header, cc, numLost; u_char *buf; int len; /* Pull off header */ if (m->m_pkthdr.len < MPPC_HDRLEN) return (EINVAL); m_copydata(m, 0, MPPC_HDRLEN, (caddr_t)&header); NTOHS(header); cc = (header & MPPC_CCOUNT_MASK); /* Copy payload into a contiguous region of memory */ len = m->m_pkthdr.len - MPPC_HDRLEN; MALLOC(buf, u_char *, len, M_NETGRAPH, M_NOWAIT); if (buf == NULL) return (ENOMEM); m_copydata(m, MPPC_HDRLEN, len, (caddr_t)buf); /* Check for insane jumps in sequence numbering (D.O.S. attack) */ numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK); if (numLost >= MPPC_INSANE_JUMP) { log(LOG_ERR, "%s: insane jump %d", __FUNCTION__, numLost); priv->recv.cfg.enable = 0; goto failed; } /* If flushed bit set, we can always handle packet */ if ((header & MPPC_FLAG_FLUSHED) != 0) { #ifdef NETGRAPH_MPPC_COMPRESSION if (d->history != NULL) MPPC_InitDecompressionHistory(d->history); #endif #ifdef NETGRAPH_MPPC_ENCRYPTION if ((d->cfg.bits & MPPE_BITS) != 0) { /* Resync as necessary, skipping lost packets */ while (d->cc != cc) { if ((d->cfg.bits & MPPE_STATELESS) || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) { ng_mppc_updatekey(d->cfg.bits, d->cfg.startkey, d->key, &d->rc4); } d->cc++; } /* Reset key (except in stateless mode, see below) */ if ((d->cfg.bits & MPPE_STATELESS) == 0) rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); } #endif d->cc = cc; /* skip over lost seq numbers */ numLost = 0; /* act like no packets were lost */ } /* Can't decode non-sequential packets without a flushed bit */ if (numLost != 0) goto failed; /* Decrypt packet */ if ((header & MPPC_FLAG_ENCRYPTED) != 0) { /* Are we not expecting encryption? */ if ((d->cfg.bits & MPPE_BITS) == 0) { log(LOG_ERR, "%s: rec'd unexpectedly %s packet", __FUNCTION__, "encrypted"); goto failed; } #ifdef NETGRAPH_MPPC_ENCRYPTION /* Update key if it's time (always in stateless mode) */ if ((d->cfg.bits & MPPE_STATELESS) != 0 || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) { ng_mppc_updatekey(d->cfg.bits, d->cfg.startkey, d->key, &d->rc4); } /* Decrypt packet */ rc4_crypt(&d->rc4, buf, buf, len); #endif } else { /* Are we expecting encryption? */ if ((d->cfg.bits & MPPE_BITS) != 0) { log(LOG_ERR, "%s: rec'd unexpectedly %s packet", __FUNCTION__, "unencrypted"); goto failed; } } /* Update coherency count for next time (12 bit arithmetic) */ d->cc++; /* Check for unexpected compressed packet */ if ((header & MPPC_FLAG_COMPRESSED) != 0 && (d->cfg.bits & MPPC_BIT) == 0) { log(LOG_ERR, "%s: rec'd unexpectedly %s packet", __FUNCTION__, "compressed"); failed: FREE(buf, M_NETGRAPH); return (EINVAL); } #ifdef NETGRAPH_MPPC_COMPRESSION /* Decompress packet */ if ((header & MPPC_FLAG_COMPRESSED) != 0) { int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS; u_char *decompbuf, *source, *dest; u_long sourceCnt, destCnt; int decomplen, rtn; /* Allocate a buffer for decompressed data */ MALLOC(decompbuf, u_char *, MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY, M_NETGRAPH, M_NOWAIT); if (decompbuf == NULL) { FREE(buf, M_NETGRAPH); return (ENOMEM); } decomplen = MPPC_DECOMP_BUFSIZE; /* Prepare to decompress */ source = buf; sourceCnt = len; dest = decompbuf; destCnt = decomplen; if ((header & MPPC_FLAG_RESTART) != 0) flags |= MPPC_RESTART_HISTORY; /* Decompress */ rtn = MPPC_Decompress(&source, &dest, &sourceCnt, &destCnt, d->history, flags); /* Check return value */ KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __FUNCTION__)); if ((rtn & MPPC_DEST_EXHAUSTED) != 0 || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) { log(LOG_ERR, "%s: decomp returned 0x%x", __FUNCTION__, rtn); FREE(decompbuf, M_NETGRAPH); goto failed; } /* Replace compressed data with decompressed data */ FREE(buf, M_NETGRAPH); buf = decompbuf; len = decomplen - destCnt; } #endif /* Return result in an mbuf */ *resultp = m_devget((caddr_t)buf, len, 0, NULL, NULL); FREE(buf, M_NETGRAPH); return (*resultp == NULL ? ENOBUFS : 0); } /* * The peer has sent us a CCP ResetRequest, so reset our transmit state. */ static void ng_mppc_reset_req(node_p node) { const priv_p priv = node->private; struct ng_mppc_dir *const d = &priv->xmit; #ifdef NETGRAPH_MPPC_COMPRESSION if (d->history != NULL) MPPC_InitCompressionHistory(d->history); #endif #ifdef NETGRAPH_MPPC_ENCRYPTION if ((d->cfg.bits & MPPE_STATELESS) == 0) rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); #endif d->flushed = 1; } /* * Generate a new encryption key */ static void ng_mppc_getkey(const u_char *h, u_char *h2, int len) { static const u_char pad1[10] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const u_char pad2[10] = { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, }; u_char hash[20]; SHA1_CTX c; int k; bzero(&hash, sizeof(hash)); SHA1Init(&c); SHA1Update(&c, h, len); for (k = 0; k < 4; k++) SHA1Update(&c, pad1, sizeof(pad2)); SHA1Update(&c, h2, len); for (k = 0; k < 4; k++) SHA1Update(&c, pad2, sizeof(pad2)); SHA1Final(hash, &c); bcopy(hash, h2, len); } /* * Update the encryption key */ static void ng_mppc_updatekey(u_int32_t bits, u_char *key0, u_char *key, struct rc4_state *rc4) { const int keylen = KEYLEN(bits); ng_mppc_getkey(key0, key, keylen); rc4_init(rc4, key, keylen); rc4_crypt(rc4, key, key, keylen); if ((bits & MPPE_128) == 0) bcopy(&ng_mppe_weakenkey, key, sizeof(ng_mppe_weakenkey)); rc4_init(rc4, key, keylen); } Index: head/sys/netgraph/ng_one2many.c =================================================================== --- head/sys/netgraph/ng_one2many.c (revision 69921) +++ head/sys/netgraph/ng_one2many.c (revision 69922) @@ -1,521 +1,520 @@ /* * ng_one2many.c * * Copyright (c) 2000 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$ */ /* * ng_one2many(4) netgraph node type * * Packets received on the "one" hook are sent out each of the * "many" hooks in round-robin fashion. Packets received on any * "many" hook are always delivered to the "one" hook. */ #include #include #include #include #include #include #include #include #include #include #include /* Per-link private data */ struct ng_one2many_link { hook_p hook; /* netgraph hook */ struct ng_one2many_link_stats stats; /* link stats */ }; /* Per-node private data */ struct ng_one2many_private { struct ng_one2many_config conf; /* node configuration */ struct ng_one2many_link one; /* "one" hook */ struct ng_one2many_link many[NG_ONE2MANY_MAX_LINKS]; u_int16_t nextMany; /* next round-robin */ u_int16_t numActiveMany; /* # active "many" */ u_int16_t activeMany[NG_ONE2MANY_MAX_LINKS]; }; typedef struct ng_one2many_private *priv_p; /* Netgraph node methods */ static ng_constructor_t ng_one2many_constructor; static ng_rcvmsg_t ng_one2many_rcvmsg; static ng_shutdown_t ng_one2many_rmnode; static ng_newhook_t ng_one2many_newhook; static ng_rcvdata_t ng_one2many_rcvdata; static ng_disconnect_t ng_one2many_disconnect; /* Other functions */ static void ng_one2many_update_many(priv_p priv); /* Store each hook's link number in the private field */ #define LINK_NUM(hook) (*(int16_t *)(&(hook)->private)) /****************************************************************** NETGRAPH PARSE TYPES ******************************************************************/ /* Parse type for struct ng_one2many_config */ static const struct ng_parse_fixedarray_info ng_one2many_enableLinks_array_type_info = { &ng_parse_uint8_type, NG_ONE2MANY_MAX_LINKS }; static const struct ng_parse_type ng_one2many_enableLinks_array_type = { &ng_parse_fixedarray_type, &ng_one2many_enableLinks_array_type_info, }; static const struct ng_parse_struct_info ng_one2many_config_type_info = NG_ONE2MANY_CONFIG_TYPE_INFO(&ng_one2many_enableLinks_array_type); static const struct ng_parse_type ng_one2many_config_type = { &ng_parse_struct_type, &ng_one2many_config_type_info, }; /* Parse type for struct ng_one2many_link_stats */ static const struct ng_parse_struct_info ng_one2many_link_stats_type_info = NG_ONE2MANY_LINK_STATS_TYPE_INFO; static const struct ng_parse_type ng_one2many_link_stats_type = { &ng_parse_struct_type, &ng_one2many_link_stats_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_one2many_cmdlist[] = { { NGM_ONE2MANY_COOKIE, NGM_ONE2MANY_SET_CONFIG, "setconfig", &ng_one2many_config_type, NULL }, { NGM_ONE2MANY_COOKIE, NGM_ONE2MANY_GET_CONFIG, "getconfig", NULL, &ng_one2many_config_type }, { NGM_ONE2MANY_COOKIE, NGM_ONE2MANY_GET_STATS, "getstats", &ng_parse_int32_type, &ng_one2many_link_stats_type }, { NGM_ONE2MANY_COOKIE, NGM_ONE2MANY_CLR_STATS, "clrstats", &ng_parse_int32_type, NULL, }, { NGM_ONE2MANY_COOKIE, NGM_ONE2MANY_GETCLR_STATS, "getclrstats", &ng_parse_int32_type, &ng_one2many_link_stats_type }, { 0 } }; /* Node type descriptor */ static struct ng_type ng_one2many_typestruct = { NG_VERSION, NG_ONE2MANY_NODE_TYPE, NULL, ng_one2many_constructor, ng_one2many_rcvmsg, ng_one2many_rmnode, ng_one2many_newhook, NULL, NULL, ng_one2many_rcvdata, - ng_one2many_rcvdata, ng_one2many_disconnect, ng_one2many_cmdlist, }; NETGRAPH_INIT(one2many, &ng_one2many_typestruct); /****************************************************************** NETGRAPH NODE METHODS ******************************************************************/ /* * Node constructor */ static int ng_one2many_constructor(node_p *nodep) { priv_p priv; int error; /* Allocate and initialize private info */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv == NULL) return (ENOMEM); priv->conf.xmitAlg = NG_ONE2MANY_XMIT_ROUNDROBIN; priv->conf.failAlg = NG_ONE2MANY_FAIL_MANUAL; /* Call superclass constructor */ if ((error = ng_make_node_common(&ng_one2many_typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Done */ return (0); } /* * Method for attaching a new hook */ static int ng_one2many_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; struct ng_one2many_link *link; int linkNum; u_long i; /* Which hook? */ if (strncmp(name, NG_ONE2MANY_HOOK_MANY_PREFIX, strlen(NG_ONE2MANY_HOOK_MANY_PREFIX)) == 0) { const char *cp; char *eptr; cp = name + strlen(NG_ONE2MANY_HOOK_MANY_PREFIX); if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) return (EINVAL); i = strtoul(cp, &eptr, 10); if (*eptr != '\0' || i < 0 || i >= NG_ONE2MANY_MAX_LINKS) return (EINVAL); linkNum = (int)i; link = &priv->many[linkNum]; } else if (strcmp(name, NG_ONE2MANY_HOOK_ONE) == 0) { linkNum = NG_ONE2MANY_ONE_LINKNUM; link = &priv->one; } else return (EINVAL); /* Is hook already connected? (should never happen) */ if (link->hook != NULL) return (EISCONN); /* Setup private info for this link */ LINK_NUM(hook) = linkNum; link->hook = hook; bzero(&link->stats, sizeof(link->stats)); if (linkNum != NG_ONE2MANY_ONE_LINKNUM) { priv->conf.enabledLinks[linkNum] = 1; /* auto-enable link */ ng_one2many_update_many(priv); } /* Done */ return (0); } /* * Receive a control message */ static int ng_one2many_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { const priv_p priv = node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_ONE2MANY_COOKIE: switch (msg->header.cmd) { case NGM_ONE2MANY_SET_CONFIG: { struct ng_one2many_config *conf; int i; /* Check that new configuration is valid */ if (msg->header.arglen != sizeof(*conf)) { error = EINVAL; break; } conf = (struct ng_one2many_config *)msg->data; switch (conf->xmitAlg) { case NG_ONE2MANY_XMIT_ROUNDROBIN: break; default: error = EINVAL; break; } switch (conf->failAlg) { case NG_ONE2MANY_FAIL_MANUAL: break; default: error = EINVAL; break; } if (error != 0) break; /* Normalized many link enabled bits */ for (i = 0; i < NG_ONE2MANY_MAX_LINKS; i++) conf->enabledLinks[i] = !!conf->enabledLinks[i]; /* Copy config and reset */ bcopy(conf, &priv->conf, sizeof(*conf)); ng_one2many_update_many(priv); break; } case NGM_ONE2MANY_GET_CONFIG: { struct ng_one2many_config *conf; NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } conf = (struct ng_one2many_config *)resp->data; bcopy(&priv->conf, conf, sizeof(priv->conf)); break; } case NGM_ONE2MANY_GET_STATS: case NGM_ONE2MANY_CLR_STATS: case NGM_ONE2MANY_GETCLR_STATS: { struct ng_one2many_link *link; int linkNum; /* Get link */ if (msg->header.arglen != sizeof(int32_t)) { error = EINVAL; break; } linkNum = *((int32_t *)msg->data); if (linkNum == NG_ONE2MANY_ONE_LINKNUM) link = &priv->one; else if (linkNum == 0 && linkNum < NG_ONE2MANY_MAX_LINKS) { link = &priv->many[linkNum]; } else { error = EINVAL; break; } /* Get/clear stats */ if (msg->header.cmd != NGM_ONE2MANY_CLR_STATS) { NG_MKRESPONSE(resp, msg, sizeof(link->stats), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } bcopy(&link->stats, resp->data, sizeof(link->stats)); } if (msg->header.cmd != NGM_ONE2MANY_GET_STATS) bzero(&link->stats, sizeof(link->stats)); break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } /* Done */ if (rptr) *rptr = resp; else if (resp != NULL) FREE(resp, M_NETGRAPH); FREE(msg, M_NETGRAPH); return (error); } /* * Receive data on a hook */ static int ng_one2many_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const node_p node = hook->node; const priv_p priv = node->private; struct ng_one2many_link *src; struct ng_one2many_link *dst; int error = 0; int linkNum; /* Get link number */ linkNum = LINK_NUM(hook); KASSERT(linkNum == NG_ONE2MANY_ONE_LINKNUM || (linkNum >= 0 && linkNum < NG_ONE2MANY_MAX_LINKS), ("%s: linkNum=%d", __FUNCTION__, linkNum)); /* Figure out source link */ src = (linkNum == NG_ONE2MANY_ONE_LINKNUM) ? &priv->one : &priv->many[linkNum]; KASSERT(src->hook != NULL, ("%s: no src%d", __FUNCTION__, linkNum)); /* Update receive stats */ src->stats.recvPackets++; src->stats.recvOctets += m->m_pkthdr.len; /* Figure out destination link */ if (linkNum == NG_ONE2MANY_ONE_LINKNUM) { if (priv->numActiveMany == 0) { NG_FREE_DATA(m, meta); return (ENOTCONN); } dst = &priv->many[priv->activeMany[priv->nextMany]]; priv->nextMany = (priv->nextMany + 1) % priv->numActiveMany; } else dst = &priv->one; /* Update transmit stats */ dst->stats.xmitPackets++; dst->stats.xmitOctets += m->m_pkthdr.len; /* Deliver packet */ NG_SEND_DATA(error, dst->hook, m, meta); return (error); } /* * Shutdown node */ static int ng_one2many_rmnode(node_p node) { const priv_p priv = node->private; ng_unname(node); ng_cutlinks(node); KASSERT(priv->numActiveMany == 0, ("%s: numActiveMany=%d", __FUNCTION__, priv->numActiveMany)); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); return (0); } /* * Hook disconnection. */ static int ng_one2many_disconnect(hook_p hook) { const priv_p priv = hook->node->private; int linkNum; /* Get link number */ linkNum = LINK_NUM(hook); KASSERT(linkNum == NG_ONE2MANY_ONE_LINKNUM || (linkNum >= 0 && linkNum < NG_ONE2MANY_MAX_LINKS), ("%s: linkNum=%d", __FUNCTION__, linkNum)); /* Nuke the link */ if (linkNum == NG_ONE2MANY_ONE_LINKNUM) priv->one.hook = NULL; else { priv->many[linkNum].hook = NULL; priv->conf.enabledLinks[linkNum] = 0; ng_one2many_update_many(priv); } /* If no hooks left, go away */ if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } /****************************************************************** OTHER FUNCTIONS ******************************************************************/ /* * Update internal state after the addition or removal of a "many" link */ static void ng_one2many_update_many(priv_p priv) { int linkNum; /* Update list of which "many" links are up */ priv->numActiveMany = 0; for (linkNum = 0; linkNum < NG_ONE2MANY_MAX_LINKS; linkNum++) { switch (priv->conf.failAlg) { case NG_ONE2MANY_FAIL_MANUAL: if (priv->many[linkNum].hook != NULL && priv->conf.enabledLinks[linkNum]) { priv->activeMany[priv->numActiveMany] = linkNum; priv->numActiveMany++; } break; #ifdef INVARIANTS default: panic("%s: invalid failAlg", __FUNCTION__); #endif } } /* Update transmit algorithm state */ switch (priv->conf.xmitAlg) { case NG_ONE2MANY_XMIT_ROUNDROBIN: if (priv->numActiveMany > 0) priv->nextMany %= priv->numActiveMany; break; #ifdef INVARIANTS default: panic("%s: invalid xmitAlg", __FUNCTION__); #endif } } Index: head/sys/netgraph/ng_ppp.c =================================================================== --- head/sys/netgraph/ng_ppp.c (revision 69921) +++ head/sys/netgraph/ng_ppp.c (revision 69922) @@ -1,2035 +1,2037 @@ /* * ng_ppp.c * * Copyright (c) 1996-2000 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_ppp.c,v 1.24 1999/11/01 09:24:52 julian Exp $ */ /* * PPP node type. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PROT_VALID(p) (((p) & 0x0101) == 0x0001) #define PROT_COMPRESSABLE(p) (((p) & 0xff00) == 0x0000) /* Some PPP protocol numbers we're interested in */ #define PROT_APPLETALK 0x0029 #define PROT_COMPD 0x00fd #define PROT_CRYPTD 0x0053 #define PROT_IP 0x0021 #define PROT_IPV6 0x0057 #define PROT_IPX 0x002b #define PROT_LCP 0xc021 #define PROT_MP 0x003d #define PROT_VJCOMP 0x002d #define PROT_VJUNCOMP 0x002f /* Multilink PPP definitions */ #define MP_MIN_MRRU 1500 /* per RFC 1990 */ #define MP_INITIAL_SEQ 0 /* per RFC 1990 */ #define MP_MIN_LINK_MRU 32 #define MP_SHORT_SEQ_MASK 0x00000fff /* short seq # mask */ #define MP_SHORT_SEQ_HIBIT 0x00000800 /* short seq # high bit */ #define MP_SHORT_FIRST_FLAG 0x00008000 /* first fragment in frame */ #define MP_SHORT_LAST_FLAG 0x00004000 /* last fragment in frame */ #define MP_LONG_SEQ_MASK 0x00ffffff /* long seq # mask */ #define MP_LONG_SEQ_HIBIT 0x00800000 /* long seq # high bit */ #define MP_LONG_FIRST_FLAG 0x80000000 /* first fragment in frame */ #define MP_LONG_LAST_FLAG 0x40000000 /* last fragment in frame */ #define MP_NOSEQ 0x7fffffff /* impossible sequence number */ /* Sign extension of MP sequence numbers */ #define MP_SHORT_EXTEND(s) (((s) & MP_SHORT_SEQ_HIBIT) ? \ ((s) | ~MP_SHORT_SEQ_MASK) \ : ((s) & MP_SHORT_SEQ_MASK)) #define MP_LONG_EXTEND(s) (((s) & MP_LONG_SEQ_HIBIT) ? \ ((s) | ~MP_LONG_SEQ_MASK) \ : ((s) & MP_LONG_SEQ_MASK)) /* Comparision of MP sequence numbers. Note: all sequence numbers except priv->xseq are stored with the sign bit extended. */ #define MP_SHORT_SEQ_DIFF(x,y) MP_SHORT_EXTEND((x) - (y)) #define MP_LONG_SEQ_DIFF(x,y) MP_LONG_EXTEND((x) - (y)) #define MP_RECV_SEQ_DIFF(priv,x,y) \ ((priv)->conf.recvShortSeq ? \ MP_SHORT_SEQ_DIFF((x), (y)) : \ MP_LONG_SEQ_DIFF((x), (y))) /* Increment receive sequence number */ #define MP_NEXT_RECV_SEQ(priv,seq) \ (((seq) + 1) & ((priv)->conf.recvShortSeq ? \ MP_SHORT_SEQ_MASK : MP_LONG_SEQ_MASK)) /* Don't fragment transmitted packets smaller than this */ #define MP_MIN_FRAG_LEN 6 /* Maximum fragment reasssembly queue length */ #define MP_MAX_QUEUE_LEN 128 /* Fragment queue scanner period */ #define MP_FRAGTIMER_INTERVAL (hz/2) /* We store incoming fragments this way */ struct ng_ppp_frag { int seq; /* fragment seq# */ u_char first; /* First in packet? */ u_char last; /* Last in packet? */ struct timeval timestamp; /* time of reception */ struct mbuf *data; /* Fragment data */ meta_p meta; /* Fragment meta */ TAILQ_ENTRY(ng_ppp_frag) f_qent; /* Fragment queue */ }; /* We use integer indicies to refer to the non-link hooks */ static const char *const ng_ppp_hook_names[] = { NG_PPP_HOOK_ATALK, #define HOOK_INDEX_ATALK 0 NG_PPP_HOOK_BYPASS, #define HOOK_INDEX_BYPASS 1 NG_PPP_HOOK_COMPRESS, #define HOOK_INDEX_COMPRESS 2 NG_PPP_HOOK_ENCRYPT, #define HOOK_INDEX_ENCRYPT 3 NG_PPP_HOOK_DECOMPRESS, #define HOOK_INDEX_DECOMPRESS 4 NG_PPP_HOOK_DECRYPT, #define HOOK_INDEX_DECRYPT 5 NG_PPP_HOOK_INET, #define HOOK_INDEX_INET 6 NG_PPP_HOOK_IPX, #define HOOK_INDEX_IPX 7 NG_PPP_HOOK_VJC_COMP, #define HOOK_INDEX_VJC_COMP 8 NG_PPP_HOOK_VJC_IP, #define HOOK_INDEX_VJC_IP 9 NG_PPP_HOOK_VJC_UNCOMP, #define HOOK_INDEX_VJC_UNCOMP 10 NG_PPP_HOOK_VJC_VJIP, #define HOOK_INDEX_VJC_VJIP 11 NG_PPP_HOOK_IPV6, #define HOOK_INDEX_IPV6 12 NULL #define HOOK_INDEX_MAX 13 }; /* We store index numbers in the hook private pointer. The HOOK_INDEX() for a hook is either the index (above) for normal hooks, or the ones complement of the link number for link hooks. */ #define HOOK_INDEX(hook) (*((int16_t *) &(hook)->private)) /* Per-link private information */ struct ng_ppp_link { struct ng_ppp_link_conf conf; /* link configuration */ hook_p hook; /* connection to link data */ int32_t seq; /* highest rec'd seq# - MSEQ */ struct timeval lastWrite; /* time of last write */ int bytesInQueue; /* bytes in the output queue */ struct ng_ppp_link_stat stats; /* Link stats */ }; /* Total per-node private information */ struct ng_ppp_private { struct ng_ppp_bund_conf conf; /* bundle config */ struct ng_ppp_link_stat bundleStats; /* bundle stats */ struct ng_ppp_link links[NG_PPP_MAX_LINKS];/* per-link info */ int32_t xseq; /* next out MP seq # */ int32_t mseq; /* min links[i].seq */ u_char vjCompHooked; /* VJ comp hooked up? */ u_char allLinksEqual; /* all xmit the same? */ u_char timerActive; /* frag timer active? */ u_int numActiveLinks; /* how many links up */ int activeLinks[NG_PPP_MAX_LINKS]; /* indicies */ u_int lastLink; /* for round robin */ hook_p hooks[HOOK_INDEX_MAX]; /* non-link hooks */ TAILQ_HEAD(ng_ppp_fraglist, ng_ppp_frag) /* fragment queue */ frags; int qlen; /* fraq queue length */ struct callout_handle fragTimer; /* fraq queue check */ }; typedef struct ng_ppp_private *priv_p; /* Netgraph node methods */ static ng_constructor_t ng_ppp_constructor; static ng_rcvmsg_t ng_ppp_rcvmsg; static ng_shutdown_t ng_ppp_rmnode; static ng_newhook_t ng_ppp_newhook; static ng_rcvdata_t ng_ppp_rcvdata; static ng_disconnect_t ng_ppp_disconnect; /* Helper functions */ static int ng_ppp_input(node_p node, int bypass, int linkNum, struct mbuf *m, meta_p meta); static int ng_ppp_output(node_p node, int bypass, int proto, int linkNum, struct mbuf *m, meta_p meta); static int ng_ppp_mp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta); static int ng_ppp_check_packet(node_p node); static void ng_ppp_get_packet(node_p node, struct mbuf **mp, meta_p *metap); static int ng_ppp_frag_process(node_p node); static int ng_ppp_frag_trim(node_p node); static void ng_ppp_frag_timeout(void *arg); static void ng_ppp_frag_checkstale(node_p node); static void ng_ppp_frag_reset(node_p node); static int ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta); static void ng_ppp_mp_strategy(node_p node, int len, int *distrib); static int ng_ppp_intcmp(const void *v1, const void *v2); static struct mbuf *ng_ppp_addproto(struct mbuf *m, int proto, int compOK); static struct mbuf *ng_ppp_prepend(struct mbuf *m, const void *buf, int len); static int ng_ppp_config_valid(node_p node, const struct ng_ppp_node_conf *newConf); static void ng_ppp_update(node_p node, int newConf); static void ng_ppp_start_frag_timer(node_p node); static void ng_ppp_stop_frag_timer(node_p node); /* Parse type for struct ng_ppp_mp_state_type */ static const struct ng_parse_fixedarray_info ng_ppp_rseq_array_info = { &ng_parse_hint32_type, NG_PPP_MAX_LINKS }; static const struct ng_parse_type ng_ppp_rseq_array_type = { &ng_parse_fixedarray_type, &ng_ppp_rseq_array_info, }; static const struct ng_parse_struct_info ng_ppp_mp_state_type_info = NG_PPP_MP_STATE_TYPE_INFO(&ng_ppp_rseq_array_type); static const struct ng_parse_type ng_ppp_mp_state_type = { &ng_parse_struct_type, &ng_ppp_mp_state_type_info, }; /* Parse type for struct ng_ppp_link_conf */ static const struct ng_parse_struct_info ng_ppp_link_type_info = NG_PPP_LINK_TYPE_INFO; static const struct ng_parse_type ng_ppp_link_type = { &ng_parse_struct_type, &ng_ppp_link_type_info, }; /* Parse type for struct ng_ppp_bund_conf */ static const struct ng_parse_struct_info ng_ppp_bund_type_info = NG_PPP_BUND_TYPE_INFO; static const struct ng_parse_type ng_ppp_bund_type = { &ng_parse_struct_type, &ng_ppp_bund_type_info, }; /* Parse type for struct ng_ppp_node_conf */ static const struct ng_parse_fixedarray_info ng_ppp_array_info = { &ng_ppp_link_type, NG_PPP_MAX_LINKS }; static const struct ng_parse_type ng_ppp_link_array_type = { &ng_parse_fixedarray_type, &ng_ppp_array_info, }; static const struct ng_parse_struct_info ng_ppp_conf_type_info = NG_PPP_CONFIG_TYPE_INFO(&ng_ppp_bund_type, &ng_ppp_link_array_type); static const struct ng_parse_type ng_ppp_conf_type = { &ng_parse_struct_type, &ng_ppp_conf_type_info }; /* Parse type for struct ng_ppp_link_stat */ static const struct ng_parse_struct_info ng_ppp_stats_type_info = NG_PPP_STATS_TYPE_INFO; static const struct ng_parse_type ng_ppp_stats_type = { &ng_parse_struct_type, &ng_ppp_stats_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_ppp_cmds[] = { { NGM_PPP_COOKIE, NGM_PPP_SET_CONFIG, "setconfig", &ng_ppp_conf_type, NULL }, { NGM_PPP_COOKIE, NGM_PPP_GET_CONFIG, "getconfig", NULL, &ng_ppp_conf_type }, { NGM_PPP_COOKIE, NGM_PPP_GET_MP_STATE, "getmpstate", NULL, &ng_ppp_mp_state_type }, { NGM_PPP_COOKIE, NGM_PPP_GET_LINK_STATS, "getstats", &ng_parse_int16_type, &ng_ppp_stats_type }, { NGM_PPP_COOKIE, NGM_PPP_CLR_LINK_STATS, "clrstats", &ng_parse_int16_type, NULL }, { NGM_PPP_COOKIE, NGM_PPP_GETCLR_LINK_STATS, "getclrstats", &ng_parse_int16_type, &ng_ppp_stats_type }, { 0 } }; /* Node type descriptor */ static struct ng_type ng_ppp_typestruct = { NG_VERSION, NG_PPP_NODE_TYPE, NULL, ng_ppp_constructor, ng_ppp_rcvmsg, ng_ppp_rmnode, ng_ppp_newhook, NULL, NULL, ng_ppp_rcvdata, - ng_ppp_rcvdata, ng_ppp_disconnect, ng_ppp_cmds }; NETGRAPH_INIT(ppp, &ng_ppp_typestruct); static int *compareLatencies; /* hack for ng_ppp_intcmp() */ /* Address and control field header */ static const u_char ng_ppp_acf[2] = { 0xff, 0x03 }; /* Maximum time we'll let a complete incoming packet sit in the queue */ static const struct timeval ng_ppp_max_staleness = { 2, 0 }; /* 2 seconds */ #define ERROUT(x) do { error = (x); goto done; } while (0) /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Node type constructor */ static int ng_ppp_constructor(node_p *nodep) { priv_p priv; int i, error; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv == NULL) return (ENOMEM); /* Call generic node constructor */ if ((error = ng_make_node_common(&ng_ppp_typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Initialize state */ TAILQ_INIT(&priv->frags); for (i = 0; i < NG_PPP_MAX_LINKS; i++) priv->links[i].seq = MP_NOSEQ; callout_handle_init(&priv->fragTimer); /* Done */ return (0); } /* * Give our OK for a hook to be added */ static int ng_ppp_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; int linkNum = -1; hook_p *hookPtr = NULL; int hookIndex = -1; /* Figure out which hook it is */ if (strncmp(name, NG_PPP_HOOK_LINK_PREFIX, /* a link hook? */ strlen(NG_PPP_HOOK_LINK_PREFIX)) == 0) { const char *cp; char *eptr; cp = name + strlen(NG_PPP_HOOK_LINK_PREFIX); if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) return (EINVAL); linkNum = (int)strtoul(cp, &eptr, 10); if (*eptr != '\0' || linkNum < 0 || linkNum >= NG_PPP_MAX_LINKS) return (EINVAL); hookPtr = &priv->links[linkNum].hook; hookIndex = ~linkNum; } else { /* must be a non-link hook */ int i; for (i = 0; ng_ppp_hook_names[i] != NULL; i++) { if (strcmp(name, ng_ppp_hook_names[i]) == 0) { hookPtr = &priv->hooks[i]; hookIndex = i; break; } } if (ng_ppp_hook_names[i] == NULL) return (EINVAL); /* no such hook */ } /* See if hook is already connected */ if (*hookPtr != NULL) return (EISCONN); /* Disallow more than one link unless multilink is enabled */ if (linkNum != -1 && priv->links[linkNum].conf.enableLink && !priv->conf.enableMultilink && priv->numActiveLinks >= 1) return (ENODEV); /* OK */ *hookPtr = hook; HOOK_INDEX(hook) = hookIndex; ng_ppp_update(node, 0); return (0); } /* * Receive a control message */ static int ng_ppp_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rptr, hook_p lasthook) { const priv_p priv = node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_PPP_COOKIE: switch (msg->header.cmd) { case NGM_PPP_SET_CONFIG: { struct ng_ppp_node_conf *const conf = (struct ng_ppp_node_conf *)msg->data; int i; /* Check for invalid or illegal config */ if (msg->header.arglen != sizeof(*conf)) ERROUT(EINVAL); if (!ng_ppp_config_valid(node, conf)) ERROUT(EINVAL); /* Copy config */ priv->conf = conf->bund; for (i = 0; i < NG_PPP_MAX_LINKS; i++) priv->links[i].conf = conf->links[i]; ng_ppp_update(node, 1); break; } case NGM_PPP_GET_CONFIG: { struct ng_ppp_node_conf *conf; int i; NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); conf = (struct ng_ppp_node_conf *)resp->data; conf->bund = priv->conf; for (i = 0; i < NG_PPP_MAX_LINKS; i++) conf->links[i] = priv->links[i].conf; break; } case NGM_PPP_GET_MP_STATE: { struct ng_ppp_mp_state *info; int i; NG_MKRESPONSE(resp, msg, sizeof(*info), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); info = (struct ng_ppp_mp_state *)resp->data; bzero(info, sizeof(*info)); for (i = 0; i < NG_PPP_MAX_LINKS; i++) { if (priv->links[i].seq != MP_NOSEQ) info->rseq[i] = priv->links[i].seq; } info->mseq = priv->mseq; info->xseq = priv->xseq; break; } case NGM_PPP_GET_LINK_STATS: case NGM_PPP_CLR_LINK_STATS: case NGM_PPP_GETCLR_LINK_STATS: { struct ng_ppp_link_stat *stats; u_int16_t linkNum; if (msg->header.arglen != sizeof(u_int16_t)) ERROUT(EINVAL); linkNum = *((u_int16_t *) msg->data); if (linkNum >= NG_PPP_MAX_LINKS && linkNum != NG_PPP_BUNDLE_LINKNUM) ERROUT(EINVAL); stats = (linkNum == NG_PPP_BUNDLE_LINKNUM) ? &priv->bundleStats : &priv->links[linkNum].stats; if (msg->header.cmd != NGM_PPP_CLR_LINK_STATS) { NG_MKRESPONSE(resp, msg, sizeof(struct ng_ppp_link_stat), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); bcopy(stats, resp->data, sizeof(*stats)); } if (msg->header.cmd != NGM_PPP_GET_LINK_STATS) bzero(stats, sizeof(*stats)); break; } default: error = EINVAL; break; } break; case NGM_VJC_COOKIE: { char path[NG_PATHLEN + 1]; node_p origNode; - if ((error = ng_path2node(node, - raddr, &origNode, NULL, NULL)) != 0) + if ((error = ng_path2node(node, raddr, &origNode, NULL)) != 0) ERROUT(error); snprintf(path, sizeof(path), "[%lx]:%s", (long)node, NG_PPP_HOOK_VJC_IP); - return ng_send_msg(origNode, msg, path, rptr); + return ng_send_msg(origNode, msg, path, NULL, NULL, rptr); +/* XXX Archie, looks like you are using the wrong value for the ID here.. + you can't use a node address as a node-ID any more.. +But it may be that you can use the new 'hook' arg for ng_send_msg() +to achieve a more efficient resuld than an ID anyhow. */ } default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /* * Receive data on a hook */ static int ng_ppp_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const node_p node = hook->node; const priv_p priv = node->private; const int index = HOOK_INDEX(hook); u_int16_t linkNum = NG_PPP_BUNDLE_LINKNUM; hook_p outHook = NULL; int proto = 0, error; /* Did it come from a link hook? */ if (index < 0) { struct ng_ppp_link *link; /* Convert index into a link number */ linkNum = (u_int16_t)~index; KASSERT(linkNum < NG_PPP_MAX_LINKS, ("%s: bogus index 0x%x", __FUNCTION__, index)); link = &priv->links[linkNum]; /* Stats */ link->stats.recvFrames++; link->stats.recvOctets += m->m_pkthdr.len; /* Strip address and control fields, if present */ if (m->m_pkthdr.len >= 2) { if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { NG_FREE_DATA(m, meta); return (ENOBUFS); } if (bcmp(mtod(m, u_char *), &ng_ppp_acf, 2) == 0) m_adj(m, 2); } /* Dispatch incoming frame (if not enabled, to bypass) */ return ng_ppp_input(node, !link->conf.enableLink, linkNum, m, meta); } /* Get protocol & check if data allowed from this hook */ switch (index) { /* Outgoing data */ case HOOK_INDEX_ATALK: if (!priv->conf.enableAtalk) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_APPLETALK; break; case HOOK_INDEX_IPX: if (!priv->conf.enableIPX) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_IPX; break; case HOOK_INDEX_IPV6: if (!priv->conf.enableIPv6) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_IPV6; break; case HOOK_INDEX_INET: case HOOK_INDEX_VJC_VJIP: if (!priv->conf.enableIP) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_IP; break; case HOOK_INDEX_VJC_COMP: if (!priv->conf.enableVJCompression) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_VJCOMP; break; case HOOK_INDEX_VJC_UNCOMP: if (!priv->conf.enableVJCompression) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_VJUNCOMP; break; case HOOK_INDEX_COMPRESS: if (!priv->conf.enableCompression) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_COMPD; break; case HOOK_INDEX_ENCRYPT: if (!priv->conf.enableEncryption) { NG_FREE_DATA(m, meta); return (ENXIO); } proto = PROT_CRYPTD; break; case HOOK_INDEX_BYPASS: if (m->m_pkthdr.len < 4) { NG_FREE_DATA(m, meta); return (EINVAL); } if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } linkNum = ntohs(mtod(m, u_int16_t *)[0]); proto = ntohs(mtod(m, u_int16_t *)[1]); m_adj(m, 4); if (linkNum >= NG_PPP_MAX_LINKS && linkNum != NG_PPP_BUNDLE_LINKNUM) { NG_FREE_DATA(m, meta); return (EINVAL); } break; /* Incoming data */ case HOOK_INDEX_VJC_IP: if (!priv->conf.enableIP || !priv->conf.enableVJDecompression) { NG_FREE_DATA(m, meta); return (ENXIO); } break; case HOOK_INDEX_DECOMPRESS: if (!priv->conf.enableDecompression) { NG_FREE_DATA(m, meta); return (ENXIO); } break; case HOOK_INDEX_DECRYPT: if (!priv->conf.enableDecryption) { NG_FREE_DATA(m, meta); return (ENXIO); } break; default: panic("%s: bogus index 0x%x", __FUNCTION__, index); } /* Now figure out what to do with the frame */ switch (index) { /* Outgoing data */ case HOOK_INDEX_INET: if (priv->conf.enableVJCompression && priv->vjCompHooked) { outHook = priv->hooks[HOOK_INDEX_VJC_IP]; break; } /* FALLTHROUGH */ case HOOK_INDEX_ATALK: case HOOK_INDEX_IPV6: case HOOK_INDEX_IPX: case HOOK_INDEX_VJC_COMP: case HOOK_INDEX_VJC_UNCOMP: case HOOK_INDEX_VJC_VJIP: if (priv->conf.enableCompression && priv->hooks[HOOK_INDEX_COMPRESS] != NULL) { if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } outHook = priv->hooks[HOOK_INDEX_COMPRESS]; break; } /* FALLTHROUGH */ case HOOK_INDEX_COMPRESS: if (priv->conf.enableEncryption && priv->hooks[HOOK_INDEX_ENCRYPT] != NULL) { if ((m = ng_ppp_addproto(m, proto, 1)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } outHook = priv->hooks[HOOK_INDEX_ENCRYPT]; break; } /* FALLTHROUGH */ case HOOK_INDEX_ENCRYPT: return ng_ppp_output(node, 0, proto, NG_PPP_BUNDLE_LINKNUM, m, meta); case HOOK_INDEX_BYPASS: return ng_ppp_output(node, 1, proto, linkNum, m, meta); /* Incoming data */ case HOOK_INDEX_DECRYPT: case HOOK_INDEX_DECOMPRESS: return ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta); case HOOK_INDEX_VJC_IP: outHook = priv->hooks[HOOK_INDEX_INET]; break; } /* Send packet out hook */ - NG_SEND_DATA_RET(error, outHook, m, meta); + NG_SEND_DATA_RET(error, outHook, m, meta, resp); if (m != NULL || meta != NULL) - return ng_ppp_rcvdata(outHook, m, meta, NULL, NULL); + return ng_ppp_rcvdata(outHook, m, meta, NULL, NULL, resp); return (error); } /* * Destroy node */ static int ng_ppp_rmnode(node_p node) { const priv_p priv = node->private; /* Stop fragment queue timer */ ng_ppp_stop_frag_timer(node); /* Take down netgraph node */ node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); ng_ppp_frag_reset(node); bzero(priv, sizeof(*priv)); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); /* let the node escape */ return (0); } /* * Hook disconnection */ static int ng_ppp_disconnect(hook_p hook) { const node_p node = hook->node; const priv_p priv = node->private; const int index = HOOK_INDEX(hook); /* Zero out hook pointer */ if (index < 0) priv->links[~index].hook = NULL; else priv->hooks[index] = NULL; /* Update derived info (or go away if no hooks left) */ if (node->numhooks > 0) ng_ppp_update(node, 0); else ng_rmnode(node); return (0); } /************************************************************************ HELPER STUFF ************************************************************************/ /* * Handle an incoming frame. Extract the PPP protocol number * and dispatch accordingly. */ static int ng_ppp_input(node_p node, int bypass, int linkNum, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; hook_p outHook = NULL; int proto, error; /* Extract protocol number */ for (proto = 0; !PROT_VALID(proto) && m->m_pkthdr.len > 0; ) { if (m->m_len < 1 && (m = m_pullup(m, 1)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } proto = (proto << 8) + *mtod(m, u_char *); m_adj(m, 1); } if (!PROT_VALID(proto)) { if (linkNum == NG_PPP_BUNDLE_LINKNUM) priv->bundleStats.badProtos++; else priv->links[linkNum].stats.badProtos++; NG_FREE_DATA(m, meta); return (EINVAL); } /* Bypass frame? */ if (bypass) goto bypass; /* Check protocol */ switch (proto) { case PROT_COMPD: if (priv->conf.enableDecompression) outHook = priv->hooks[HOOK_INDEX_DECOMPRESS]; break; case PROT_CRYPTD: if (priv->conf.enableDecryption) outHook = priv->hooks[HOOK_INDEX_DECRYPT]; break; case PROT_VJCOMP: if (priv->conf.enableVJDecompression && priv->vjCompHooked) outHook = priv->hooks[HOOK_INDEX_VJC_COMP]; break; case PROT_VJUNCOMP: if (priv->conf.enableVJDecompression && priv->vjCompHooked) outHook = priv->hooks[HOOK_INDEX_VJC_UNCOMP]; break; case PROT_MP: if (priv->conf.enableMultilink && linkNum != NG_PPP_BUNDLE_LINKNUM) return ng_ppp_mp_input(node, linkNum, m, meta); break; case PROT_APPLETALK: if (priv->conf.enableAtalk) outHook = priv->hooks[HOOK_INDEX_ATALK]; break; case PROT_IPX: if (priv->conf.enableIPX) outHook = priv->hooks[HOOK_INDEX_IPX]; break; case PROT_IP: if (priv->conf.enableIP) outHook = priv->hooks[HOOK_INDEX_INET]; break; case PROT_IPV6: if (priv->conf.enableIPv6) outHook = priv->hooks[HOOK_INDEX_IPV6]; break; } bypass: /* For unknown/inactive protocols, forward out the bypass hook */ if (outHook == NULL) { u_int16_t hdr[2]; hdr[0] = htons(linkNum); hdr[1] = htons((u_int16_t)proto); if ((m = ng_ppp_prepend(m, &hdr, 4)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } outHook = priv->hooks[HOOK_INDEX_BYPASS]; } /* Forward frame */ NG_SEND_DATA(error, outHook, m, meta); return (error); } /* * Deliver a frame out a link, either a real one or NG_PPP_BUNDLE_LINKNUM * If the link is not enabled then ENXIO is returned, unless "bypass" is != 0. */ static int ng_ppp_output(node_p node, int bypass, int proto, int linkNum, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; struct ng_ppp_link *link; int len, error; /* If not doing MP, map bundle virtual link to (the only) link */ if (linkNum == NG_PPP_BUNDLE_LINKNUM && !priv->conf.enableMultilink) linkNum = priv->activeLinks[0]; /* Get link pointer (optimization) */ link = (linkNum != NG_PPP_BUNDLE_LINKNUM) ? &priv->links[linkNum] : NULL; /* Check link status (if real) */ if (linkNum != NG_PPP_BUNDLE_LINKNUM) { if (!bypass && !link->conf.enableLink) { NG_FREE_DATA(m, meta); return (ENXIO); } if (link->hook == NULL) { NG_FREE_DATA(m, meta); return (ENETDOWN); } } /* Prepend protocol number, possibly compressed */ if ((m = ng_ppp_addproto(m, proto, linkNum == NG_PPP_BUNDLE_LINKNUM || link->conf.enableProtoComp)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } /* Special handling for the MP virtual link */ if (linkNum == NG_PPP_BUNDLE_LINKNUM) return ng_ppp_mp_output(node, m, meta); /* Prepend address and control field (unless compressed) */ if (proto == PROT_LCP || !link->conf.enableACFComp) { if ((m = ng_ppp_prepend(m, &ng_ppp_acf, 2)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } } /* Deliver frame */ len = m->m_pkthdr.len; NG_SEND_DATA(error, link->hook, m, meta); /* Update stats and 'bytes in queue' counter */ if (error == 0) { link->stats.xmitFrames++; link->stats.xmitOctets += len; link->bytesInQueue += len; getmicrouptime(&link->lastWrite); } return error; } /* * Handle an incoming multi-link fragment * * The fragment reassembly algorithm is somewhat complex. This is mainly * because we are required not to reorder the reconstructed packets, yet * fragments are only guaranteed to arrive in order on a per-link basis. * In other words, when we have a complete packet ready, but the previous * packet is still incomplete, we have to decide between delivering the * complete packet and throwing away the incomplete one, or waiting to * see if the remainder of the incomplete one arrives, at which time we * can deliver both packets, in order. * * This problem is exacerbated by "sequence number slew", which is when * the sequence numbers coming in from different links are far apart from * each other. In particular, certain unnamed equipment (*cough* Ascend) * has been seen to generate sequence number slew of up to 10 on an ISDN * 2B-channel MP link. There is nothing invalid about sequence number slew * but it makes the reasssembly process have to work harder. * * However, the peer is required to transmit fragments in order on each * link. That means if we define MSEQ as the minimum over all links of * the highest sequence number received on that link, then we can always * give up any hope of receiving a fragment with sequence number < MSEQ in * the future (all of this using 'wraparound' sequence number space). * Therefore we can always immediately throw away incomplete packets * missing fragments with sequence numbers < MSEQ. * * Here is an overview of our algorithm: * * o Received fragments are inserted into a queue, for which we * maintain these invariants between calls to this function: * * - Fragments are ordered in the queue by sequence number * - If a complete packet is at the head of the queue, then * the first fragment in the packet has seq# > MSEQ + 1 * (otherwise, we could deliver it immediately) * - If any fragments have seq# < MSEQ, then they are necessarily * part of a packet whose missing seq#'s are all > MSEQ (otherwise, * we can throw them away because they'll never be completed) * - The queue contains at most MP_MAX_QUEUE_LEN fragments * * o We have a periodic timer that checks the queue for the first * complete packet that has been sitting in the queue "too long". * When one is detected, all previous (incomplete) fragments are * discarded, their missing fragments are declared lost and MSEQ * is increased. * * o If we recieve a fragment with seq# < MSEQ, we throw it away * because we've already delcared it lost. * * This assumes linkNum != NG_PPP_BUNDLE_LINKNUM. */ static int ng_ppp_mp_input(node_p node, int linkNum, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; struct ng_ppp_link *const link = &priv->links[linkNum]; struct ng_ppp_frag frag0, *frag = &frag0; struct ng_ppp_frag *qent; int i, diff, inserted; /* Stats */ priv->bundleStats.recvFrames++; priv->bundleStats.recvOctets += m->m_pkthdr.len; /* Extract fragment information from MP header */ if (priv->conf.recvShortSeq) { u_int16_t shdr; if (m->m_pkthdr.len < 2) { link->stats.runts++; NG_FREE_DATA(m, meta); return (EINVAL); } if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } shdr = ntohs(*mtod(m, u_int16_t *)); frag->seq = MP_SHORT_EXTEND(shdr); frag->first = (shdr & MP_SHORT_FIRST_FLAG) != 0; frag->last = (shdr & MP_SHORT_LAST_FLAG) != 0; diff = MP_SHORT_SEQ_DIFF(frag->seq, priv->mseq); m_adj(m, 2); } else { u_int32_t lhdr; if (m->m_pkthdr.len < 4) { link->stats.runts++; NG_FREE_DATA(m, meta); return (EINVAL); } if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } lhdr = ntohl(*mtod(m, u_int32_t *)); frag->seq = MP_LONG_EXTEND(lhdr); frag->first = (lhdr & MP_LONG_FIRST_FLAG) != 0; frag->last = (lhdr & MP_LONG_LAST_FLAG) != 0; diff = MP_LONG_SEQ_DIFF(frag->seq, priv->mseq); m_adj(m, 4); } frag->data = m; frag->meta = meta; getmicrouptime(&frag->timestamp); /* If sequence number is < MSEQ, we've already declared this fragment as lost, so we have no choice now but to drop it */ if (diff < 0) { link->stats.dropFragments++; NG_FREE_DATA(m, meta); return (0); } /* Update highest received sequence number on this link and MSEQ */ priv->mseq = link->seq = frag->seq; for (i = 0; i < priv->numActiveLinks; i++) { struct ng_ppp_link *const alink = &priv->links[priv->activeLinks[i]]; if (MP_RECV_SEQ_DIFF(priv, alink->seq, priv->mseq) < 0) priv->mseq = alink->seq; } /* Allocate a new frag struct for the queue */ MALLOC(frag, struct ng_ppp_frag *, sizeof(*frag), M_NETGRAPH, M_NOWAIT); if (frag == NULL) { NG_FREE_DATA(m, meta); ng_ppp_frag_process(node); return (ENOMEM); } *frag = frag0; /* Add fragment to queue, which is sorted by sequence number */ inserted = 0; TAILQ_FOREACH_REVERSE(qent, &priv->frags, ng_ppp_fraglist, f_qent) { diff = MP_RECV_SEQ_DIFF(priv, frag->seq, qent->seq); if (diff > 0) { TAILQ_INSERT_AFTER(&priv->frags, qent, frag, f_qent); inserted = 1; break; } else if (diff == 0) { /* should never happen! */ link->stats.dupFragments++; NG_FREE_DATA(frag->data, frag->meta); FREE(frag, M_NETGRAPH); return (EINVAL); } } if (!inserted) TAILQ_INSERT_HEAD(&priv->frags, frag, f_qent); priv->qlen++; /* Process the queue */ return ng_ppp_frag_process(node); } /* * Examine our list of fragments, and determine if there is a * complete and deliverable packet at the head of the list. * Return 1 if so, zero otherwise. */ static int ng_ppp_check_packet(node_p node) { const priv_p priv = node->private; struct ng_ppp_frag *qent, *qnext; /* Check for empty queue */ if (TAILQ_EMPTY(&priv->frags)) return (0); /* Check first fragment is the start of a deliverable packet */ qent = TAILQ_FIRST(&priv->frags); if (!qent->first || MP_RECV_SEQ_DIFF(priv, qent->seq, priv->mseq) > 1) return (0); /* Check that all the fragments are there */ while (!qent->last) { qnext = TAILQ_NEXT(qent, f_qent); if (qnext == NULL) /* end of queue */ return (0); if (qnext->seq != MP_NEXT_RECV_SEQ(priv, qent->seq)) return (0); qent = qnext; } /* Got one */ return (1); } /* * Pull a completed packet off the head of the incoming fragment queue. * This assumes there is a completed packet there to pull off. */ static void ng_ppp_get_packet(node_p node, struct mbuf **mp, meta_p *metap) { const priv_p priv = node->private; struct ng_ppp_frag *qent, *qnext; struct mbuf *m = NULL, *tail; qent = TAILQ_FIRST(&priv->frags); KASSERT(!TAILQ_EMPTY(&priv->frags) && qent->first, ("%s: no packet", __FUNCTION__)); for (tail = NULL; qent != NULL; qent = qnext) { qnext = TAILQ_NEXT(qent, f_qent); KASSERT(!TAILQ_EMPTY(&priv->frags), ("%s: empty q", __FUNCTION__)); TAILQ_REMOVE(&priv->frags, qent, f_qent); if (tail == NULL) { tail = m = qent->data; *metap = qent->meta; /* inherit first frag's meta */ } else { m->m_pkthdr.len += qent->data->m_pkthdr.len; tail->m_next = qent->data; NG_FREE_META(qent->meta); /* drop other frags' metas */ } while (tail->m_next != NULL) tail = tail->m_next; if (qent->last) qnext = NULL; FREE(qent, M_NETGRAPH); priv->qlen--; } *mp = m; } /* * Trim fragments from the queue whose packets can never be completed. * This assumes a complete packet is NOT at the beginning of the queue. * Returns 1 if fragments were removed, zero otherwise. */ static int ng_ppp_frag_trim(node_p node) { const priv_p priv = node->private; struct ng_ppp_frag *qent, *qnext = NULL; int removed = 0; /* Scan for "dead" fragments and remove them */ while (1) { int dead = 0; /* If queue is empty, we're done */ if (TAILQ_EMPTY(&priv->frags)) break; /* Determine whether first fragment can ever be completed */ TAILQ_FOREACH(qent, &priv->frags, f_qent) { if (MP_RECV_SEQ_DIFF(priv, qent->seq, priv->mseq) >= 0) break; qnext = TAILQ_NEXT(qent, f_qent); KASSERT(qnext != NULL, ("%s: last frag < MSEQ?", __FUNCTION__)); if (qnext->seq != MP_NEXT_RECV_SEQ(priv, qent->seq) || qent->last || qnext->first) { dead = 1; break; } } if (!dead) break; /* Remove fragment and all others in the same packet */ while ((qent = TAILQ_FIRST(&priv->frags)) != qnext) { KASSERT(!TAILQ_EMPTY(&priv->frags), ("%s: empty q", __FUNCTION__)); priv->bundleStats.dropFragments++; TAILQ_REMOVE(&priv->frags, qent, f_qent); NG_FREE_DATA(qent->data, qent->meta); FREE(qent, M_NETGRAPH); priv->qlen--; removed = 1; } } return (removed); } /* * Run the queue, restoring the queue invariants */ static int ng_ppp_frag_process(node_p node) { const priv_p priv = node->private; struct mbuf *m; meta_p meta; /* Deliver any deliverable packets */ while (ng_ppp_check_packet(node)) { ng_ppp_get_packet(node, &m, &meta); ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta); } /* Delete dead fragments and try again */ if (ng_ppp_frag_trim(node)) { while (ng_ppp_check_packet(node)) { ng_ppp_get_packet(node, &m, &meta); ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta); } } /* Check for stale fragments while we're here */ ng_ppp_frag_checkstale(node); /* Check queue length */ if (priv->qlen > MP_MAX_QUEUE_LEN) { struct ng_ppp_frag *qent; int i; /* Get oldest fragment */ KASSERT(!TAILQ_EMPTY(&priv->frags), ("%s: empty q", __FUNCTION__)); qent = TAILQ_FIRST(&priv->frags); /* Bump MSEQ if necessary */ if (MP_RECV_SEQ_DIFF(priv, priv->mseq, qent->seq) < 0) { priv->mseq = qent->seq; for (i = 0; i < priv->numActiveLinks; i++) { struct ng_ppp_link *const alink = &priv->links[priv->activeLinks[i]]; if (MP_RECV_SEQ_DIFF(priv, alink->seq, priv->mseq) < 0) alink->seq = priv->mseq; } } /* Drop it */ priv->bundleStats.dropFragments++; TAILQ_REMOVE(&priv->frags, qent, f_qent); NG_FREE_DATA(qent->data, qent->meta); FREE(qent, M_NETGRAPH); priv->qlen--; /* Process queue again */ return ng_ppp_frag_process(node); } /* Done */ return (0); } /* * Check for 'stale' completed packets that need to be delivered * * If a link goes down or has a temporary failure, MSEQ can get * "stuck", because no new incoming fragments appear on that link. * This can cause completed packets to never get delivered if * their sequence numbers are all > MSEQ + 1. * * This routine checks how long all of the completed packets have * been sitting in the queue, and if too long, removes fragments * from the queue and increments MSEQ to allow them to be delivered. */ static void ng_ppp_frag_checkstale(node_p node) { const priv_p priv = node->private; struct ng_ppp_frag *qent, *beg, *end; struct timeval now, age; struct mbuf *m; meta_p meta; int i, seq; now.tv_sec = 0; /* uninitialized state */ while (1) { /* If queue is empty, we're done */ if (TAILQ_EMPTY(&priv->frags)) break; /* Find the first complete packet in the queue */ beg = end = NULL; seq = TAILQ_FIRST(&priv->frags)->seq; TAILQ_FOREACH(qent, &priv->frags, f_qent) { if (qent->first) beg = qent; else if (qent->seq != seq) beg = NULL; if (beg != NULL && qent->last) { end = qent; break; } seq = MP_NEXT_RECV_SEQ(priv, seq); } /* If none found, exit */ if (end == NULL) break; /* Get current time (we assume we've been up for >= 1 second) */ if (now.tv_sec == 0) getmicrouptime(&now); /* Check if packet has been queued too long */ age = now; timevalsub(&age, &beg->timestamp); if (timevalcmp(&age, &ng_ppp_max_staleness, < )) break; /* Throw away junk fragments in front of the completed packet */ while ((qent = TAILQ_FIRST(&priv->frags)) != beg) { KASSERT(!TAILQ_EMPTY(&priv->frags), ("%s: empty q", __FUNCTION__)); priv->bundleStats.dropFragments++; TAILQ_REMOVE(&priv->frags, qent, f_qent); NG_FREE_DATA(qent->data, qent->meta); FREE(qent, M_NETGRAPH); priv->qlen--; } /* Extract completed packet */ ng_ppp_get_packet(node, &m, &meta); /* Bump MSEQ if necessary */ if (MP_RECV_SEQ_DIFF(priv, priv->mseq, end->seq) < 0) { priv->mseq = end->seq; for (i = 0; i < priv->numActiveLinks; i++) { struct ng_ppp_link *const alink = &priv->links[priv->activeLinks[i]]; if (MP_RECV_SEQ_DIFF(priv, alink->seq, priv->mseq) < 0) alink->seq = priv->mseq; } } /* Deliver packet */ ng_ppp_input(node, 0, NG_PPP_BUNDLE_LINKNUM, m, meta); } } /* * Periodically call ng_ppp_frag_checkstale() */ static void ng_ppp_frag_timeout(void *arg) { const node_p node = arg; const priv_p priv = node->private; int s = splnet(); /* Handle the race where shutdown happens just before splnet() above */ if ((node->flags & NG_INVALID) != 0) { ng_unref(node); splx(s); return; } /* Reset timer state after timeout */ KASSERT(priv->timerActive, ("%s: !timerActive", __FUNCTION__)); priv->timerActive = 0; KASSERT(node->refs > 1, ("%s: refs=%d", __FUNCTION__, node->refs)); ng_unref(node); /* Start timer again */ ng_ppp_start_frag_timer(node); /* Scan the fragment queue */ ng_ppp_frag_checkstale(node); splx(s); } /* * Deliver a frame out on the bundle, i.e., figure out how to fragment * the frame across the individual PPP links and do so. */ static int ng_ppp_mp_output(node_p node, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; int distrib[NG_PPP_MAX_LINKS]; int firstFragment; int activeLinkNum; /* At least one link must be active */ if (priv->numActiveLinks == 0) { NG_FREE_DATA(m, meta); return (ENETDOWN); } /* Round-robin strategy */ if (priv->conf.enableRoundRobin || m->m_pkthdr.len < MP_MIN_FRAG_LEN) { activeLinkNum = priv->lastLink++ % priv->numActiveLinks; bzero(&distrib, priv->numActiveLinks * sizeof(distrib[0])); distrib[activeLinkNum] = m->m_pkthdr.len; goto deliver; } /* Strategy when all links are equivalent (optimize the common case) */ if (priv->allLinksEqual) { const int fraction = m->m_pkthdr.len / priv->numActiveLinks; int i, remain; for (i = 0; i < priv->numActiveLinks; i++) distrib[priv->lastLink++ % priv->numActiveLinks] = fraction; remain = m->m_pkthdr.len - (fraction * priv->numActiveLinks); while (remain > 0) { distrib[priv->lastLink++ % priv->numActiveLinks]++; remain--; } goto deliver; } /* Strategy when all links are not equivalent */ ng_ppp_mp_strategy(node, m->m_pkthdr.len, distrib); deliver: /* Update stats */ priv->bundleStats.xmitFrames++; priv->bundleStats.xmitOctets += m->m_pkthdr.len; /* Send alloted portions of frame out on the link(s) */ for (firstFragment = 1, activeLinkNum = priv->numActiveLinks - 1; activeLinkNum >= 0; activeLinkNum--) { const int linkNum = priv->activeLinks[activeLinkNum]; struct ng_ppp_link *const link = &priv->links[linkNum]; /* Deliver fragment(s) out the next link */ for ( ; distrib[activeLinkNum] > 0; firstFragment = 0) { int len, lastFragment, error; struct mbuf *m2; meta_p meta2; /* Calculate fragment length; don't exceed link MTU */ len = distrib[activeLinkNum]; if (len > link->conf.mru) len = link->conf.mru; distrib[activeLinkNum] -= len; lastFragment = (len == m->m_pkthdr.len); /* Split off next fragment as "m2" */ m2 = m; if (!lastFragment) { struct mbuf *n = m_split(m, len, M_NOWAIT); if (n == NULL) { NG_FREE_DATA(m, meta); return (ENOMEM); } m = n; } /* Prepend MP header */ if (priv->conf.xmitShortSeq) { u_int16_t shdr; shdr = priv->xseq; priv->xseq = (priv->xseq + 1) & MP_SHORT_SEQ_MASK; if (firstFragment) shdr |= MP_SHORT_FIRST_FLAG; if (lastFragment) shdr |= MP_SHORT_LAST_FLAG; shdr = htons(shdr); m2 = ng_ppp_prepend(m2, &shdr, 2); } else { u_int32_t lhdr; lhdr = priv->xseq; priv->xseq = (priv->xseq + 1) & MP_LONG_SEQ_MASK; if (firstFragment) lhdr |= MP_LONG_FIRST_FLAG; if (lastFragment) lhdr |= MP_LONG_LAST_FLAG; lhdr = htonl(lhdr); m2 = ng_ppp_prepend(m2, &lhdr, 4); } if (m2 == NULL) { if (!lastFragment) m_freem(m); NG_FREE_META(meta); return (ENOBUFS); } /* Copy the meta information, if any */ meta2 = lastFragment ? meta : ng_copy_meta(meta); /* Send fragment */ error = ng_ppp_output(node, 0, PROT_MP, linkNum, m2, meta2); if (error != 0) { if (!lastFragment) NG_FREE_DATA(m, meta); return (error); } } } /* Done */ return (0); } /* * Computing the optimal fragmentation * ----------------------------------- * * This routine tries to compute the optimal fragmentation pattern based * on each link's latency, bandwidth, and calculated additional latency. * The latter quantity is the additional latency caused by previously * written data that has not been transmitted yet. * * This algorithm is only useful when not all of the links have the * same latency and bandwidth values. * * The essential idea is to make the last bit of each fragment of the * frame arrive at the opposite end at the exact same time. This greedy * algorithm is optimal, in that no other scheduling could result in any * packet arriving any sooner unless packets are delivered out of order. * * Suppose link i has bandwidth b_i (in tens of bytes per milisecond) and * latency l_i (in miliseconds). Consider the function function f_i(t) * which is equal to the number of bytes that will have arrived at * the peer after t miliseconds if we start writing continuously at * time t = 0. Then f_i(t) = b_i * (t - l_i) = ((b_i * t) - (l_i * b_i). * That is, f_i(t) is a line with slope b_i and y-intersect -(l_i * b_i). * Note that the y-intersect is always <= zero because latency can't be * negative. Note also that really the function is f_i(t) except when * f_i(t) is negative, in which case the function is zero. To take * care of this, let Q_i(t) = { if (f_i(t) > 0) return 1; else return 0; }. * So the actual number of bytes that will have arrived at the peer after * t miliseconds is f_i(t) * Q_i(t). * * At any given time, each link has some additional latency a_i >= 0 * due to previously written fragment(s) which are still in the queue. * This value is easily computed from the time since last transmission, * the previous latency value, the number of bytes written, and the * link's bandwidth. * * Assume that l_i includes any a_i already, and that the links are * sorted by latency, so that l_i <= l_{i+1}. * * Let N be the total number of bytes in the current frame we are sending. * * Suppose we were to start writing bytes at time t = 0 on all links * simultaneously, which is the most we can possibly do. Then let * F(t) be equal to the total number of bytes received by the peer * after t miliseconds. Then F(t) = Sum_i (f_i(t) * Q_i(t)). * * Our goal is simply this: fragment the frame across the links such * that the peer is able to reconstruct the completed frame as soon as * possible, i.e., at the least possible value of t. Call this value t_0. * * Then it follows that F(t_0) = N. Our strategy is first to find the value * of t_0, and then deduce how many bytes to write to each link. * * Rewriting F(t_0): * * t_0 = ( N + Sum_i ( l_i * b_i * Q_i(t_0) ) ) / Sum_i ( b_i * Q_i(t_0) ) * * Now, we note that Q_i(t) is constant for l_i <= t <= l_{i+1}. t_0 will * lie in one of these ranges. To find it, we just need to find the i such * that F(l_i) <= N <= F(l_{i+1}). Then we compute all the constant values * for Q_i() in this range, plug in the remaining values, solving for t_0. * * Once t_0 is known, then the number of bytes to send on link i is * just f_i(t_0) * Q_i(t_0). * * In other words, we start allocating bytes to the links one at a time. * We keep adding links until the frame is completely sent. Some links * may not get any bytes because their latency is too high. * * Is all this work really worth the trouble? Depends on the situation. * The bigger the ratio of computer speed to link speed, and the more * important total bundle latency is (e.g., for interactive response time), * the more it's worth it. There is however the cost of calling this * function for every frame. The running time is O(n^2) where n is the * number of links that receive a non-zero number of bytes. * * Since latency is measured in miliseconds, the "resolution" of this * algorithm is one milisecond. * * To avoid this algorithm altogether, configure all links to have the * same latency and bandwidth. */ static void ng_ppp_mp_strategy(node_p node, int len, int *distrib) { const priv_p priv = node->private; int latency[NG_PPP_MAX_LINKS]; int sortByLatency[NG_PPP_MAX_LINKS]; int activeLinkNum; int t0, total, topSum, botSum; struct timeval now; int i, numFragments; /* If only one link, this gets real easy */ if (priv->numActiveLinks == 1) { distrib[0] = len; return; } /* Get current time */ getmicrouptime(&now); /* Compute latencies for each link at this point in time */ for (activeLinkNum = 0; activeLinkNum < priv->numActiveLinks; activeLinkNum++) { struct ng_ppp_link *alink; struct timeval diff; int xmitBytes; /* Start with base latency value */ alink = &priv->links[priv->activeLinks[activeLinkNum]]; latency[activeLinkNum] = alink->conf.latency; sortByLatency[activeLinkNum] = activeLinkNum; /* see below */ /* Any additional latency? */ if (alink->bytesInQueue == 0) continue; /* Compute time delta since last write */ diff = now; timevalsub(&diff, &alink->lastWrite); if (now.tv_sec < 0 || diff.tv_sec >= 10) { /* sanity */ alink->bytesInQueue = 0; continue; } /* How many bytes could have transmitted since last write? */ xmitBytes = (alink->conf.bandwidth * diff.tv_sec) + (alink->conf.bandwidth * (diff.tv_usec / 1000)) / 100; alink->bytesInQueue -= xmitBytes; if (alink->bytesInQueue < 0) alink->bytesInQueue = 0; else latency[activeLinkNum] += (100 * alink->bytesInQueue) / alink->conf.bandwidth; } /* Sort active links by latency */ compareLatencies = latency; qsort(sortByLatency, priv->numActiveLinks, sizeof(*sortByLatency), ng_ppp_intcmp); compareLatencies = NULL; /* Find the interval we need (add links in sortByLatency[] order) */ for (numFragments = 1; numFragments < priv->numActiveLinks; numFragments++) { for (total = i = 0; i < numFragments; i++) { int flowTime; flowTime = latency[sortByLatency[numFragments]] - latency[sortByLatency[i]]; total += ((flowTime * priv->links[ priv->activeLinks[sortByLatency[i]]].conf.bandwidth) + 99) / 100; } if (total >= len) break; } /* Solve for t_0 in that interval */ for (topSum = botSum = i = 0; i < numFragments; i++) { int bw = priv->links[ priv->activeLinks[sortByLatency[i]]].conf.bandwidth; topSum += latency[sortByLatency[i]] * bw; /* / 100 */ botSum += bw; /* / 100 */ } t0 = ((len * 100) + topSum + botSum / 2) / botSum; /* Compute f_i(t_0) all i */ bzero(distrib, priv->numActiveLinks * sizeof(*distrib)); for (total = i = 0; i < numFragments; i++) { int bw = priv->links[ priv->activeLinks[sortByLatency[i]]].conf.bandwidth; distrib[sortByLatency[i]] = (bw * (t0 - latency[sortByLatency[i]]) + 50) / 100; total += distrib[sortByLatency[i]]; } /* Deal with any rounding error */ if (total < len) { struct ng_ppp_link *fastLink = &priv->links[priv->activeLinks[sortByLatency[0]]]; int fast = 0; /* Find the fastest link */ for (i = 1; i < numFragments; i++) { struct ng_ppp_link *const link = &priv->links[priv->activeLinks[sortByLatency[i]]]; if (link->conf.bandwidth > fastLink->conf.bandwidth) { fast = i; fastLink = link; } } distrib[sortByLatency[fast]] += len - total; } else while (total > len) { struct ng_ppp_link *slowLink = &priv->links[priv->activeLinks[sortByLatency[0]]]; int delta, slow = 0; /* Find the slowest link that still has bytes to remove */ for (i = 1; i < numFragments; i++) { struct ng_ppp_link *const link = &priv->links[priv->activeLinks[sortByLatency[i]]]; if (distrib[sortByLatency[slow]] == 0 || (distrib[sortByLatency[i]] > 0 && link->conf.bandwidth < slowLink->conf.bandwidth)) { slow = i; slowLink = link; } } delta = total - len; if (delta > distrib[sortByLatency[slow]]) delta = distrib[sortByLatency[slow]]; distrib[sortByLatency[slow]] -= delta; total -= delta; } } /* * Compare two integers */ static int ng_ppp_intcmp(const void *v1, const void *v2) { const int index1 = *((const int *) v1); const int index2 = *((const int *) v2); return compareLatencies[index1] - compareLatencies[index2]; } /* * Prepend a possibly compressed PPP protocol number in front of a frame */ static struct mbuf * ng_ppp_addproto(struct mbuf *m, int proto, int compOK) { if (compOK && PROT_COMPRESSABLE(proto)) { u_char pbyte = (u_char)proto; return ng_ppp_prepend(m, &pbyte, 1); } else { u_int16_t pword = htons((u_int16_t)proto); return ng_ppp_prepend(m, &pword, 2); } } /* * Prepend some bytes to an mbuf */ static struct mbuf * ng_ppp_prepend(struct mbuf *m, const void *buf, int len) { M_PREPEND(m, len, M_NOWAIT); if (m == NULL || (m->m_len < len && (m = m_pullup(m, len)) == NULL)) return (NULL); bcopy(buf, mtod(m, u_char *), len); return (m); } /* * Update private information that is derived from other private information */ static void ng_ppp_update(node_p node, int newConf) { const priv_p priv = node->private; int i; /* Update active status for VJ Compression */ priv->vjCompHooked = priv->hooks[HOOK_INDEX_VJC_IP] != NULL && priv->hooks[HOOK_INDEX_VJC_COMP] != NULL && priv->hooks[HOOK_INDEX_VJC_UNCOMP] != NULL && priv->hooks[HOOK_INDEX_VJC_VJIP] != NULL; /* Increase latency for each link an amount equal to one MP header */ if (newConf) { for (i = 0; i < NG_PPP_MAX_LINKS; i++) { int hdrBytes; hdrBytes = (priv->links[i].conf.enableACFComp ? 0 : 2) + (priv->links[i].conf.enableProtoComp ? 1 : 2) + (priv->conf.xmitShortSeq ? 2 : 4); priv->links[i].conf.latency += ((hdrBytes * priv->links[i].conf.bandwidth) + 50) / 100; } } /* Update list of active links */ bzero(&priv->activeLinks, sizeof(priv->activeLinks)); priv->numActiveLinks = 0; priv->allLinksEqual = 1; for (i = 0; i < NG_PPP_MAX_LINKS; i++) { struct ng_ppp_link *const link = &priv->links[i]; /* Is link active? */ if (link->conf.enableLink && link->hook != NULL) { struct ng_ppp_link *link0; /* Add link to list of active links */ priv->activeLinks[priv->numActiveLinks++] = i; link0 = &priv->links[priv->activeLinks[0]]; /* Determine if all links are still equal */ if (link->conf.latency != link0->conf.latency || link->conf.bandwidth != link0->conf.bandwidth) priv->allLinksEqual = 0; /* Initialize rec'd sequence number */ if (link->seq == MP_NOSEQ) { link->seq = (link == link0) ? MP_INITIAL_SEQ : link0->seq; } } else link->seq = MP_NOSEQ; } /* Update MP state as multi-link is active or not */ if (priv->conf.enableMultilink && priv->numActiveLinks > 0) ng_ppp_start_frag_timer(node); else { ng_ppp_stop_frag_timer(node); ng_ppp_frag_reset(node); priv->xseq = MP_INITIAL_SEQ; priv->mseq = MP_INITIAL_SEQ; for (i = 0; i < NG_PPP_MAX_LINKS; i++) { struct ng_ppp_link *const link = &priv->links[i]; bzero(&link->lastWrite, sizeof(link->lastWrite)); link->bytesInQueue = 0; link->seq = MP_NOSEQ; } } } /* * Determine if a new configuration would represent a valid change * from the current configuration and link activity status. */ static int ng_ppp_config_valid(node_p node, const struct ng_ppp_node_conf *newConf) { const priv_p priv = node->private; int i, newNumLinksActive; /* Check per-link config and count how many links would be active */ for (newNumLinksActive = i = 0; i < NG_PPP_MAX_LINKS; i++) { if (newConf->links[i].enableLink && priv->links[i].hook != NULL) newNumLinksActive++; if (!newConf->links[i].enableLink) continue; if (newConf->links[i].mru < MP_MIN_LINK_MRU) return (0); if (newConf->links[i].bandwidth == 0) return (0); if (newConf->links[i].bandwidth > NG_PPP_MAX_BANDWIDTH) return (0); if (newConf->links[i].latency > NG_PPP_MAX_LATENCY) return (0); } /* Check bundle parameters */ if (newConf->bund.enableMultilink && newConf->bund.mrru < MP_MIN_MRRU) return (0); /* Disallow changes to multi-link configuration while MP is active */ if (priv->numActiveLinks > 0 && newNumLinksActive > 0) { if (!priv->conf.enableMultilink != !newConf->bund.enableMultilink || !priv->conf.xmitShortSeq != !newConf->bund.xmitShortSeq || !priv->conf.recvShortSeq != !newConf->bund.recvShortSeq) return (0); } /* At most one link can be active unless multi-link is enabled */ if (!newConf->bund.enableMultilink && newNumLinksActive > 1) return (0); /* Configuration change would be valid */ return (1); } /* * Free all entries in the fragment queue */ static void ng_ppp_frag_reset(node_p node) { const priv_p priv = node->private; struct ng_ppp_frag *qent, *qnext; for (qent = TAILQ_FIRST(&priv->frags); qent; qent = qnext) { qnext = TAILQ_NEXT(qent, f_qent); NG_FREE_DATA(qent->data, qent->meta); FREE(qent, M_NETGRAPH); } TAILQ_INIT(&priv->frags); priv->qlen = 0; } /* * Start fragment queue timer */ static void ng_ppp_start_frag_timer(node_p node) { const priv_p priv = node->private; if (!priv->timerActive) { priv->fragTimer = timeout(ng_ppp_frag_timeout, node, MP_FRAGTIMER_INTERVAL); priv->timerActive = 1; node->refs++; } } /* * Stop fragment queue timer */ static void ng_ppp_stop_frag_timer(node_p node) { const priv_p priv = node->private; if (priv->timerActive) { untimeout(ng_ppp_frag_timeout, node, priv->fragTimer); priv->timerActive = 0; KASSERT(node->refs > 1, ("%s: refs=%d", __FUNCTION__, node->refs)); ng_unref(node); } } Index: head/sys/netgraph/ng_pppoe.c =================================================================== --- head/sys/netgraph/ng_pppoe.c (revision 69921) +++ head/sys/netgraph/ng_pppoe.c (revision 69922) @@ -1,1590 +1,1632 @@ /* * ng_pppoe.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_pppoe.c,v 1.10 1999/11/01 09:24:52 julian Exp $ */ #if 0 #define AAA printf("pppoe: %s\n", __FUNCTION__ ); #define BBB printf("-%d-", __LINE__ ); #else #define AAA #define BBB #endif #include #include #include #include #include #include #include #include #include #include #include #define SIGNOFF "session closed" #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) /* * This section contains the netgraph method declarations for the * sample node. These methods define the netgraph 'type'. */ static ng_constructor_t ng_pppoe_constructor; static ng_rcvmsg_t ng_pppoe_rcvmsg; static ng_shutdown_t ng_pppoe_rmnode; static ng_newhook_t ng_pppoe_newhook; static ng_connect_t ng_pppoe_connect; static ng_rcvdata_t ng_pppoe_rcvdata; static ng_disconnect_t ng_pppoe_disconnect; /* Parse type for struct ngpppoe_init_data */ static const struct ng_parse_struct_info ngpppoe_init_data_type_info = NG_PPPOE_INIT_DATA_TYPE_INFO; static const struct ng_parse_type ngpppoe_init_data_state_type = { &ng_parse_struct_type, &ngpppoe_init_data_type_info }; /* Parse type for struct ngpppoe_sts */ static const struct ng_parse_struct_info ng_pppoe_sts_type_info = NG_PPPOE_STS_TYPE_INFO; static const struct ng_parse_type ng_pppoe_sts_state_type = { &ng_parse_struct_type, &ng_pppoe_sts_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_pppoe_cmds[] = { { NGM_PPPOE_COOKIE, NGM_PPPOE_CONNECT, "pppoe_connect", &ngpppoe_init_data_state_type, NULL }, { NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN, "pppoe_listen", &ngpppoe_init_data_state_type, NULL }, { NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER, "pppoe_offer", &ngpppoe_init_data_state_type, NULL }, { NGM_PPPOE_COOKIE, + NGM_PPPOE_SERVICE, + "pppoe_service", + &ngpppoe_init_data_state_type, + NULL + }, + { + NGM_PPPOE_COOKIE, NGM_PPPOE_SUCCESS, "pppoe_success", &ng_pppoe_sts_state_type, NULL }, { NGM_PPPOE_COOKIE, NGM_PPPOE_FAIL, "pppoe_fail", &ng_pppoe_sts_state_type, NULL }, { NGM_PPPOE_COOKIE, NGM_PPPOE_CLOSE, "pppoe_close", &ng_pppoe_sts_state_type, NULL }, { 0 } }; /* Netgraph node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_PPPOE_NODE_TYPE, NULL, ng_pppoe_constructor, ng_pppoe_rcvmsg, ng_pppoe_rmnode, ng_pppoe_newhook, NULL, ng_pppoe_connect, ng_pppoe_rcvdata, - ng_pppoe_rcvdata, ng_pppoe_disconnect, ng_pppoe_cmds }; NETGRAPH_INIT(pppoe, &typestruct); /* * States for the session state machine. * These have no meaning if there is no hook attached yet. */ enum state { PPPOE_SNONE=0, /* [both] Initial state */ PPPOE_LISTENING, /* [Daemon] Listening for discover initiation pkt */ PPPOE_SINIT, /* [Client] Sent discovery initiation */ PPPOE_PRIMED, /* [Server] Awaiting PADI from daemon */ PPPOE_SOFFER, /* [Server] Sent offer message (got PADI)*/ PPPOE_SREQ, /* [Client] Sent a Request */ PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */ PPPOE_CONNECTED, /* [Both] Connection established, Data received */ PPPOE_DEAD /* [Both] */ }; #define NUMTAGS 20 /* number of tags we are set up to work with */ /* * Information we store for each hook on each node for negotiating the * session. The mbuf and cluster are freed once negotiation has completed. * The whole negotiation block is then discarded. */ struct sess_neg { struct mbuf *m; /* holds cluster with last sent packet */ union packet *pkt; /* points within the above cluster */ struct callout_handle timeout_handle; /* see timeout(9) */ u_int timeout; /* 0,1,2,4,8,16 etc. seconds */ u_int numtags; struct pppoe_tag *tags[NUMTAGS]; u_int service_len; u_int ac_name_len; struct datatag service; struct datatag ac_name; }; typedef struct sess_neg *negp; /* * Session information that is needed after connection. */ struct sess_con { hook_p hook; u_int16_t Session_ID; enum state state; char creator[NG_NODELEN + 1]; /* who to notify */ struct pppoe_full_hdr pkt_hdr; /* used when connected */ negp neg; /* used when negotiating */ /*struct sess_con *hash_next;*/ /* not yet used */ }; typedef struct sess_con *sessp; /* * Information we store for each node */ struct PPPOE { node_p node; /* back pointer to node */ hook_p ethernet_hook; hook_p debug_hook; u_int packets_in; /* packets in from ethernet */ u_int packets_out; /* packets out towards ethernet */ u_int32_t flags; /*struct sess_con *buckets[HASH_SIZE];*/ /* not yet used */ }; typedef struct PPPOE *priv_p; const struct ether_header eh_prototype = {{0xff,0xff,0xff,0xff,0xff,0xff}, {0x00,0x00,0x00,0x00,0x00,0x00}, ETHERTYPE_PPPOE_DISC}; union uniq { char bytes[sizeof(void *)]; void * pointer; }; #define LEAVE(x) do { error = x; goto quit; } while(0) static void pppoe_start(sessp sp); static void sendpacket(sessp sp); static void pppoe_ticker(void *arg); static struct pppoe_tag* scan_tags(sessp sp, struct pppoe_hdr* ph); static int pppoe_send_event(sessp sp, enum cmd cmdid); /************************************************************************* * Some basic utilities from the Linux version with author's permission.* * Author: Michal Ostrowski * ************************************************************************/ /* * Generate a new session id * XXX find out the FreeBSD locking scheme. */ static u_int16_t get_new_sid(node_p node) { static int pppoe_sid = 10; sessp sp; hook_p hook; u_int16_t val; priv_p privp = node->private; AAA restart: val = pppoe_sid++; /* * Spec says 0xFFFF is reserved. * Also don't use 0x0000 */ if (val == 0xffff) { pppoe_sid = 20; goto restart; } /* Check it isn't already in use */ LIST_FOREACH(hook, &node->hooks, hooks) { /* don't check special hooks */ if ((hook->private == &privp->debug_hook) || (hook->private == &privp->ethernet_hook)) continue; sp = hook->private; if (sp->Session_ID == val) goto restart; } return val; } /* * Return the location where the next tag can be put */ static __inline struct pppoe_tag* next_tag(struct pppoe_hdr* ph) { return (struct pppoe_tag*)(((char*)&ph->tag[0]) + ntohs(ph->length)); } /* * Look for a tag of a specific type * Don't trust any length the other end says. * but assume we already sanity checked ph->length. */ static struct pppoe_tag* get_tag(struct pppoe_hdr* ph, u_int16_t idx) { char *end = (char *)next_tag(ph); char *ptn; struct pppoe_tag *pt = &ph->tag[0]; /* * Keep processing tags while a tag header will still fit. */ AAA while((char*)(pt + 1) <= end) { /* * If the tag data would go past the end of the packet, abort. */ ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); if(ptn > end) return NULL; if(pt->tag_type == idx) return pt; pt = (struct pppoe_tag*)ptn; } return NULL; } /************************************************************************** * inlines to initialise or add tags to a session's tag list, **************************************************************************/ /* * Initialise the session's tag list */ static void init_tags(sessp sp) { AAA if(sp->neg == NULL) { printf("pppoe: asked to init NULL neg pointer\n"); return; } sp->neg->numtags = 0; } static void insert_tag(sessp sp, struct pppoe_tag *tp) { int i; negp neg; AAA if((neg = sp->neg) == NULL) { printf("pppoe: asked to use NULL neg pointer\n"); return; } if ((i = neg->numtags++) < NUMTAGS) { neg->tags[i] = tp; } else { printf("pppoe: asked to add too many tags to packet\n"); neg->numtags--; } } /* * Make up a packet, using the tags filled out for the session. * * Assume that the actual pppoe header and ethernet header * are filled out externally to this routine. * Also assume that neg->wh points to the correct * location at the front of the buffer space. */ static void make_packet(sessp sp) { struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; struct pppoe_tag **tag; char *dp; int count; int tlen; u_int16_t length = 0; AAA if ((sp->neg == NULL) || (sp->neg->m == NULL)) { printf("pppoe: make_packet called from wrong state\n"); } dp = (char *)wh->ph.tag; for (count = 0, tag = sp->neg->tags; ((count < sp->neg->numtags) && (count < NUMTAGS)); tag++, count++) { tlen = ntohs((*tag)->tag_len) + sizeof(**tag); if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) { printf("pppoe: tags too long\n"); sp->neg->numtags = count; break; /* XXX chop off what's too long */ } bcopy((char *)*tag, (char *)dp, tlen); length += tlen; dp += tlen; } wh->ph.length = htons(length); sp->neg->m->m_len = length + sizeof(*wh); sp->neg->m->m_pkthdr.len = length + sizeof(*wh); } /************************************************************************** * Routine to match a service offered * **************************************************************************/ /* * Find a hook that has a service string that matches that * we are seeking. for now use a simple string. * In the future we may need something like regexp(). * for testing allow a null string to match 1st found and a null service * to match all requests. Also make '*' do the same. */ static hook_p pppoe_match_svc(node_p node, char *svc_name, int svc_len) { sessp sp = NULL; negp neg = NULL; priv_p privp = node->private; hook_p hook; AAA LIST_FOREACH(hook, &node->hooks, hooks) { /* skip any hook that is debug or ethernet */ if ((hook->private == &privp->debug_hook) || (hook->private == &privp->ethernet_hook)) continue; sp = hook->private; /* Skip any sessions which are not in LISTEN mode. */ if ( sp->state != PPPOE_LISTENING) continue; neg = sp->neg; /* XXX check validity of this */ /* special case, NULL request. match 1st found. */ if (svc_len == 0) break; /* XXX check validity of this */ /* Special case for a blank or "*" service name (wildcard) */ if ((neg->service_len == 0) || ((neg->service_len == 1) && (neg->service.data[0] == '*'))) { break; } /* If the lengths don't match, that aint it. */ if (neg->service_len != svc_len) continue; /* An exact match? */ if (strncmp(svc_name, neg->service.data, svc_len) == 0) break; } return (hook); } /************************************************************************** * Routine to find a particular session that matches an incoming packet * **************************************************************************/ static hook_p pppoe_findsession(node_p node, struct pppoe_full_hdr *wh) { sessp sp = NULL; hook_p hook = NULL; priv_p privp = node->private; u_int16_t session = ntohs(wh->ph.sid); /* * find matching peer/session combination. */ AAA LIST_FOREACH(hook, &node->hooks, hooks) { /* don't check special hooks */ if ((hook->private == &privp->debug_hook) || (hook->private == &privp->ethernet_hook)) { continue; } sp = hook->private; if ( ( (sp->state == PPPOE_CONNECTED) || (sp->state == PPPOE_NEWCONNECTED) ) && (sp->Session_ID == session) && (bcmp(sp->pkt_hdr.eh.ether_dhost, wh->eh.ether_shost, ETHER_ADDR_LEN)) == 0) { break; } } return (hook); } static hook_p pppoe_finduniq(node_p node, struct pppoe_tag *tag) { hook_p hook = NULL; priv_p privp = node->private; union uniq uniq; AAA bcopy(tag->tag_data, uniq.bytes, sizeof(void *)); /* cycle through all known hooks */ LIST_FOREACH(hook, &node->hooks, hooks) { /* don't check special hooks */ if ((hook->private == &privp->debug_hook) || (hook->private == &privp->ethernet_hook)) continue; if (uniq.pointer == hook->private) break; } return (hook); } /************************************************************************** * start of Netgraph entrypoints * **************************************************************************/ /* * Allocate the private data structure and the generic node * and link them together. * * ng_make_node_common() returns with a generic node struct * with a single reference for us.. we transfer it to the * private structure.. when we free the private struct we must * unref the node so it gets freed too. - * - * If this were a device node than this work would be done in the attach() - * routine and the constructor would return EINVAL as you should not be able - * to creatednodes that depend on hardware (unless you can add the hardware :) */ static int ng_pppoe_constructor(node_p *nodep) { priv_p privdata; int error; AAA /* Initialize private descriptor */ MALLOC(privdata, priv_p, sizeof(*privdata), M_NETGRAPH, M_NOWAIT | M_ZERO); if (privdata == NULL) return (ENOMEM); /* Call the 'generic' (ie, superclass) node constructor */ if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(privdata, M_NETGRAPH); return (error); } /* Link structs together; this counts as our one reference to *nodep */ (*nodep)->private = privdata; privdata->node = *nodep; return (0); } /* * Give our ok for a hook to be added... * point the hook's private info to the hook structure. * * The following hook names are special: * Ethernet: the hook that should be connected to a NIC. * debug: copies of data sent out here (when I write the code). + * All other hook names need only be unique. (the framework checks this). */ static int ng_pppoe_newhook(node_p node, hook_p hook, const char *name) { const priv_p privp = node->private; sessp sp; AAA if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) { privp->ethernet_hook = hook; hook->private = &privp->ethernet_hook; } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) { privp->debug_hook = hook; hook->private = &privp->debug_hook; } else { /* * Any other unique name is OK. * The infrastructure has already checked that it's unique, * so just allocate it and hook it in. */ MALLOC(sp, sessp, sizeof(*sp), M_NETGRAPH, M_NOWAIT | M_ZERO); if (sp == NULL) { return (ENOMEM); } hook->private = sp; sp->hook = hook; } return(0); } /* * Get a netgraph control message. * Check it is one we understand. If needed, send a response. * We sometimes save the address for an async action later. * Always free the message. */ static int ng_pppoe_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { priv_p privp = node->private; struct ngpppoe_init_data *ourmsg = NULL; struct ng_mesg *resp = NULL; int error = 0; hook_p hook = NULL; sessp sp = NULL; negp neg = NULL; AAA /* Deal with message according to cookie and command */ switch (msg->header.typecookie) { case NGM_PPPOE_COOKIE: switch (msg->header.cmd) { case NGM_PPPOE_CONNECT: case NGM_PPPOE_LISTEN: case NGM_PPPOE_OFFER: + case NGM_PPPOE_SERVICE: ourmsg = (struct ngpppoe_init_data *)msg->data; if (msg->header.arglen < sizeof(*ourmsg)) { printf("pppoe: init data too small\n"); LEAVE(EMSGSIZE); } if (msg->header.arglen - sizeof(*ourmsg) > PPPOE_SERVICE_NAME_SIZE) { printf("pppoe_rcvmsg: service name too big"); LEAVE(EMSGSIZE); } if (msg->header.arglen - sizeof(*ourmsg) < ourmsg->data_len) { printf("pppoe: init data has bad length," " %d should be %d\n", ourmsg->data_len, msg->header.arglen - sizeof (*ourmsg)); LEAVE(EMSGSIZE); } /* make sure strcmp will terminate safely */ ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0'; /* cycle through all known hooks */ LIST_FOREACH(hook, &node->hooks, hooks) { if (hook->name && strcmp(hook->name, ourmsg->hook) == 0) break; } if (hook == NULL) { LEAVE(ENOENT); } if ((hook->private == &privp->debug_hook) || (hook->private == &privp->ethernet_hook)) { LEAVE(EINVAL); } sp = hook->private; + + /* + * PPPOE_SERVICE advertisments are set up + * on sessions that are in PRIMED state. + */ + if (msg->header.cmd == NGM_PPPOE_SERVICE) { + break; + } if (sp->state |= PPPOE_SNONE) { printf("pppoe: Session already active\n"); LEAVE(EISCONN); } /* * set up prototype header */ MALLOC(neg, negp, sizeof(*neg), M_NETGRAPH, M_NOWAIT | M_ZERO); if (neg == NULL) { printf("pppoe: Session out of memory\n"); LEAVE(ENOMEM); } MGETHDR(neg->m, M_DONTWAIT, MT_DATA); if(neg->m == NULL) { printf("pppoe: Session out of mbufs\n"); FREE(neg, M_NETGRAPH); LEAVE(ENOBUFS); } neg->m->m_pkthdr.rcvif = NULL; MCLGET(neg->m, M_DONTWAIT); if ((neg->m->m_flags & M_EXT) == 0) { printf("pppoe: Session out of mcls\n"); m_freem(neg->m); FREE(neg, M_NETGRAPH); LEAVE(ENOBUFS); } sp->neg = neg; callout_handle_init( &neg->timeout_handle); neg->m->m_len = sizeof(struct pppoe_full_hdr); neg->pkt = mtod(neg->m, union packet*); neg->pkt->pkt_header.eh = eh_prototype; neg->pkt->pkt_header.ph.ver = 0x1; neg->pkt->pkt_header.ph.type = 0x1; neg->pkt->pkt_header.ph.sid = 0x0000; neg->timeout = 0; strncpy(sp->creator, retaddr, NG_NODELEN); sp->creator[NG_NODELEN] = '\0'; } switch (msg->header.cmd) { case NGM_PPPOE_GET_STATUS: { struct ngpppoestat *stats; NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); if (!resp) { LEAVE(ENOMEM); } stats = (struct ngpppoestat *) resp->data; stats->packets_in = privp->packets_in; stats->packets_out = privp->packets_out; break; } case NGM_PPPOE_CONNECT: /* * Check the hook exists and is Uninitialised. * Send a PADI request, and start the timeout logic. * Store the originator of this message so we can send * a success of fail message to them later. * Move the session to SINIT * Set up the session to the correct state and * start it. */ neg->service.hdr.tag_type = PTT_SRV_NAME; neg->service.hdr.tag_len = htons((u_int16_t)ourmsg->data_len); if (ourmsg->data_len) bcopy(ourmsg->data, neg->service.data, ourmsg->data_len); neg->service_len = ourmsg->data_len; pppoe_start(sp); break; case NGM_PPPOE_LISTEN: /* * Check the hook exists and is Uninitialised. * Install the service matching string. * Store the originator of this message so we can send * a success of fail message to them later. * Move the hook to 'LISTENING' - */ neg->service.hdr.tag_type = PTT_SRV_NAME; neg->service.hdr.tag_len = htons((u_int16_t)ourmsg->data_len); if (ourmsg->data_len) bcopy(ourmsg->data, neg->service.data, ourmsg->data_len); neg->service_len = ourmsg->data_len; neg->pkt->pkt_header.ph.code = PADT_CODE; /* * wait for PADI packet coming from ethernet */ sp->state = PPPOE_LISTENING; break; case NGM_PPPOE_OFFER: /* * Check the hook exists and is Uninitialised. * Store the originator of this message so we can send * a success of fail message to them later. * Store the AC-Name given and go to PRIMED. */ neg->ac_name.hdr.tag_type = PTT_AC_NAME; neg->ac_name.hdr.tag_len = htons((u_int16_t)ourmsg->data_len); if (ourmsg->data_len) bcopy(ourmsg->data, neg->ac_name.data, ourmsg->data_len); neg->ac_name_len = ourmsg->data_len; neg->pkt->pkt_header.ph.code = PADO_CODE; /* * Wait for PADI packet coming from hook */ sp->state = PPPOE_PRIMED; break; + case NGM_PPPOE_SERVICE: + /* + * Check the session is primed. + * for now just allow ONE service to be advertised. + * If you do it twice you just overwrite. + */ + if (sp->state |= PPPOE_PRIMED) { + printf("pppoe: Session not primed\n"); + LEAVE(EISCONN); + } + neg->service.hdr.tag_type = PTT_SRV_NAME; + neg->service.hdr.tag_len = + htons((u_int16_t)ourmsg->data_len); + + if (ourmsg->data_len) + bcopy(ourmsg->data, neg->service.data, + ourmsg->data_len); + neg->service_len = ourmsg->data_len; + break; default: LEAVE(EINVAL); } break; default: LEAVE(EINVAL); } /* Take care of synchronous response, if any */ if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); /* Free the message and return */ quit: FREE(msg, M_NETGRAPH); return(error); } /* * Start a client into the first state. A separate function because * it can be needed if the negotiation times out. */ static void pppoe_start(sessp sp) { struct { struct pppoe_tag hdr; union uniq data; } uniqtag; /* * kick the state machine into starting up */ AAA sp->state = PPPOE_SINIT; /* reset the packet header to broadcast */ sp->neg->pkt->pkt_header.eh = eh_prototype; sp->neg->pkt->pkt_header.ph.code = PADI_CODE; uniqtag.hdr.tag_type = PTT_HOST_UNIQ; uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); uniqtag.data.pointer = sp; init_tags(sp); insert_tag(sp, &uniqtag.hdr); insert_tag(sp, &sp->neg->service.hdr); make_packet(sp); sendpacket(sp); } /* * Receive data, and do something with it. * The caller will never free m or meta, so * if we use up this data or abort we must free BOTH of these. */ static int ng_pppoe_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { node_p node = hook->node; const priv_p privp = node->private; sessp sp = hook->private; struct pppoe_full_hdr *wh; struct pppoe_hdr *ph; int error = 0; u_int16_t session; u_int16_t length; u_int8_t code; struct pppoe_tag *utag = NULL, *tag = NULL; hook_p sendhook; struct { struct pppoe_tag hdr; union uniq data; } uniqtag; negp neg = NULL; AAA if (hook->private == &privp->debug_hook) { /* * Data from the debug hook gets sent without modification * straight to the ethernet. */ NG_SEND_DATA( error, privp->ethernet_hook, m, meta); privp->packets_out++; } else if (hook->private == &privp->ethernet_hook) { /* * Incoming data. * Dig out various fields from the packet. * use them to decide where to send it. */ privp->packets_in++; if( m->m_len < sizeof(*wh)) { m = m_pullup(m, sizeof(*wh)); /* Checks length */ if (m == NULL) { printf("couldn't m_pullup\n"); LEAVE(ENOBUFS); } } wh = mtod(m, struct pppoe_full_hdr *); ph = &wh->ph; session = ntohs(wh->ph.sid); length = ntohs(wh->ph.length); code = wh->ph.code; switch(wh->eh.ether_type) { case ETHERTYPE_PPPOE_DISC: /* * We need to try to make sure that the tag area * is contiguous, or we could wander off the end * of a buffer and make a mess. * (Linux wouldn't have this problem). */ /*XXX fix this mess */ if (m->m_pkthdr.len <= MHLEN) { if( m->m_len < m->m_pkthdr.len) { m = m_pullup(m, m->m_pkthdr.len); if (m == NULL) { printf("couldn't m_pullup\n"); LEAVE(ENOBUFS); } } } if (m->m_len != m->m_pkthdr.len) { /* * It's not all in one piece. * We need to do extra work. */ printf("packet fragmented\n"); LEAVE(EMSGSIZE); } switch(code) { case PADI_CODE: /* * We are a server: * Look for a hook with the required service * and send the ENTIRE packet up there. * It should come back to a new hook in * PRIMED state. Look there for further * processing. */ tag = get_tag(ph, PTT_SRV_NAME); if (tag == NULL) { printf("no service tag\n"); LEAVE(ENETUNREACH); } sendhook = pppoe_match_svc(hook->node, tag->tag_data, ntohs(tag->tag_len)); if (sendhook) { NG_SEND_DATA(error, sendhook, m, meta); } else { printf("no such service\n"); LEAVE(ENETUNREACH); } break; case PADO_CODE: /* * We are a client: * Use the host_uniq tag to find the * hook this is in response to. * Received #2, now send #3 * For now simply accept the first we receive. */ utag = get_tag(ph, PTT_HOST_UNIQ); if ((utag == NULL) || (ntohs(utag->tag_len) != sizeof(sp))) { printf("no host unique field\n"); LEAVE(ENETUNREACH); } sendhook = pppoe_finduniq(node, utag); if (sendhook == NULL) { printf("no matching session\n"); LEAVE(ENETUNREACH); } /* * Check the session is in the right state. * It needs to be in PPPOE_SINIT. */ sp = sendhook->private; if (sp->state != PPPOE_SINIT) { printf("session in wrong state\n"); LEAVE(ENETUNREACH); } neg = sp->neg; untimeout(pppoe_ticker, sendhook, neg->timeout_handle); /* * This is the first time we hear * from the server, so note it's * unicast address, replacing the * broadcast address . */ bcopy(wh->eh.ether_shost, neg->pkt->pkt_header.eh.ether_dhost, ETHER_ADDR_LEN); neg->timeout = 0; neg->pkt->pkt_header.ph.code = PADR_CODE; init_tags(sp); insert_tag(sp, utag); /* Host Unique */ if ((tag = get_tag(ph, PTT_AC_COOKIE))) insert_tag(sp, tag); /* return cookie */ if ((tag = get_tag(ph, PTT_AC_NAME))) insert_tag(sp, tag); /* return it */ insert_tag(sp, &neg->service.hdr); /* Service */ scan_tags(sp, ph); make_packet(sp); sp->state = PPPOE_SREQ; sendpacket(sp); break; case PADR_CODE: /* * We are a server: * Use the ac_cookie tag to find the * hook this is in response to. */ utag = get_tag(ph, PTT_AC_COOKIE); if ((utag == NULL) || (ntohs(utag->tag_len) != sizeof(sp))) { LEAVE(ENETUNREACH); } sendhook = pppoe_finduniq(node, utag); if (sendhook == NULL) { LEAVE(ENETUNREACH); } /* * Check the session is in the right state. * It needs to be in PPPOE_SOFFER * or PPPOE_NEWCONNECTED. If the latter, * then this is a retry by the client. * so be nice, and resend. */ sp = sendhook->private; if (sp->state == PPPOE_NEWCONNECTED) { /* * Whoa! drop back to resend that * PADS packet. * We should still have a copy of it. */ sp->state = PPPOE_SOFFER; } if (sp->state != PPPOE_SOFFER) { LEAVE (ENETUNREACH); break; } neg = sp->neg; untimeout(pppoe_ticker, sendhook, neg->timeout_handle); neg->pkt->pkt_header.ph.code = PADS_CODE; if (sp->Session_ID == 0) neg->pkt->pkt_header.ph.sid = htons(sp->Session_ID = get_new_sid(node)); neg->timeout = 0; /* * start working out the tags to respond with. */ init_tags(sp); insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ if ((tag = get_tag(ph, PTT_SRV_NAME))) insert_tag(sp, tag);/* return service */ if ((tag = get_tag(ph, PTT_HOST_UNIQ))) insert_tag(sp, tag); /* return it */ insert_tag(sp, utag); /* ac_cookie */ scan_tags(sp, ph); make_packet(sp); sp->state = PPPOE_NEWCONNECTED; sendpacket(sp); /* * Having sent the last Negotiation header, * Set up the stored packet header to * be correct for the actual session. * But keep the negotialtion stuff * around in case we need to resend this last * packet. We'll discard it when we move * from NEWCONNECTED to CONNECTED */ sp->pkt_hdr = neg->pkt->pkt_header; sp->pkt_hdr.eh.ether_type = ETHERTYPE_PPPOE_SESS; sp->pkt_hdr.ph.code = 0; pppoe_send_event(sp, NGM_PPPOE_SUCCESS); break; case PADS_CODE: /* * We are a client: * Use the host_uniq tag to find the * hook this is in response to. * take the session ID and store it away. * Also make sure the pre-made header is * correct and set us into Session mode. */ utag = get_tag(ph, PTT_HOST_UNIQ); if ((utag == NULL) || (ntohs(utag->tag_len) != sizeof(sp))) { LEAVE (ENETUNREACH); break; } sendhook = pppoe_finduniq(node, utag); if (sendhook == NULL) { LEAVE(ENETUNREACH); } /* * Check the session is in the right state. * It needs to be in PPPOE_SREQ. */ sp = sendhook->private; if (sp->state != PPPOE_SREQ) { LEAVE(ENETUNREACH); } neg = sp->neg; untimeout(pppoe_ticker, sendhook, neg->timeout_handle); neg->pkt->pkt_header.ph.sid = wh->ph.sid; sp->Session_ID = ntohs(wh->ph.sid); neg->timeout = 0; sp->state = PPPOE_CONNECTED; /* * Now we have gone to Connected mode, * Free all resources needed for * negotiation. * Keep a copy of the header we will be using. */ sp->pkt_hdr = neg->pkt->pkt_header; sp->pkt_hdr.eh.ether_type = ETHERTYPE_PPPOE_SESS; sp->pkt_hdr.ph.code = 0; m_freem(neg->m); FREE(sp->neg, M_NETGRAPH); sp->neg = NULL; pppoe_send_event(sp, NGM_PPPOE_SUCCESS); break; case PADT_CODE: /* * Send a 'close' message to the controlling * process (the one that set us up); * And then tear everything down. * * Find matching peer/session combination. */ sendhook = pppoe_findsession(node, wh); NG_FREE_DATA(m, meta); /* no longer needed */ if (sendhook == NULL) { LEAVE(ENETUNREACH); } /* send message to creator */ /* close hook */ if (sendhook) { ng_destroy_hook(sendhook); } break; default: LEAVE(EPFNOSUPPORT); } break; case ETHERTYPE_PPPOE_SESS: /* * find matching peer/session combination. */ sendhook = pppoe_findsession(node, wh); if (sendhook == NULL) { LEAVE (ENETUNREACH); break; } sp = sendhook->private; m_adj(m, sizeof(*wh)); if (m->m_pkthdr.len < length) { /* Packet too short, dump it */ LEAVE(EMSGSIZE); } /* Also need to trim excess at the end */ if (m->m_pkthdr.len > length) { m_adj(m, -((int)(m->m_pkthdr.len - length))); } if ( sp->state != PPPOE_CONNECTED) { if (sp->state == PPPOE_NEWCONNECTED) { sp->state = PPPOE_CONNECTED; /* * Now we have gone to Connected mode, * Free all resources needed for * negotiation. Be paranoid about * whether there may be a timeout. */ m_freem(sp->neg->m); untimeout(pppoe_ticker, sendhook, sp->neg->timeout_handle); FREE(sp->neg, M_NETGRAPH); sp->neg = NULL; } else { LEAVE (ENETUNREACH); break; } } NG_SEND_DATA( error, sendhook, m, meta); break; default: LEAVE(EPFNOSUPPORT); } } else { /* * Not ethernet or debug hook.. * * The packet has come in on a normal hook. * We need to find out what kind of hook, * So we can decide how to handle it. * Check the hook's state. */ sp = hook->private; switch (sp->state) { case PPPOE_NEWCONNECTED: case PPPOE_CONNECTED: { static const u_char addrctrl[] = { 0xff, 0x03 }; struct pppoe_full_hdr *wh; /* * Remove PPP address and control fields, if any. * For example, ng_ppp(4) always sends LCP packets * with address and control fields as required by * generic PPP. PPPoE is an exception to the rule. */ if (m->m_pkthdr.len >= 2) { if (m->m_len < 2 && !(m = m_pullup(m, 2))) LEAVE(ENOBUFS); if (bcmp(mtod(m, u_char *), addrctrl, 2) == 0) m_adj(m, 2); } /* * Bang in a pre-made header, and set the length up * to be correct. Then send it to the ethernet driver. * But first correct the length. */ sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len)); M_PREPEND(m, sizeof(*wh), M_DONTWAIT); if (m == NULL) { LEAVE(ENOBUFS); } wh = mtod(m, struct pppoe_full_hdr *); bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); NG_SEND_DATA( error, privp->ethernet_hook, m, meta); privp->packets_out++; break; } case PPPOE_PRIMED: /* * A PADI packet is being returned by the application * that has set up this hook. This indicates that it * wants us to offer service. */ neg = sp->neg; if (m->m_len < sizeof(*wh)) { m = m_pullup(m, sizeof(*wh)); if (m == NULL) { LEAVE(ENOBUFS); } } wh = mtod(m, struct pppoe_full_hdr *); ph = &wh->ph; session = ntohs(wh->ph.sid); length = ntohs(wh->ph.length); code = wh->ph.code; if ( code != PADI_CODE) { LEAVE(EINVAL); }; untimeout(pppoe_ticker, hook, neg->timeout_handle); /* * This is the first time we hear * from the client, so note it's * unicast address, replacing the * broadcast address. */ bcopy(wh->eh.ether_shost, neg->pkt->pkt_header.eh.ether_dhost, ETHER_ADDR_LEN); sp->state = PPPOE_SOFFER; neg->timeout = 0; neg->pkt->pkt_header.ph.code = PADO_CODE; /* * start working out the tags to respond with. */ uniqtag.hdr.tag_type = PTT_AC_COOKIE; uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); uniqtag.data.pointer = sp; init_tags(sp); insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ if ((tag = get_tag(ph, PTT_SRV_NAME))) insert_tag(sp, tag); /* return service */ + /* + * If we have a NULL service request + * and have an extra service defined in this hook, + * then also add a tag for the extra service. + * XXX this is a hack. eventually we should be able + * to support advertising many services, not just one + */ + if (((tag == NULL) || (tag->tag_len == 0)) + && (neg->service.hdr.tag_len != 0)) { + insert_tag(sp, &neg->service.hdr); /* SERVICE */ + } if ((tag = get_tag(ph, PTT_HOST_UNIQ))) insert_tag(sp, tag); /* returned hostunique */ insert_tag(sp, &uniqtag.hdr); - /* XXX maybe put the tag in the session store */ scan_tags(sp, ph); make_packet(sp); sendpacket(sp); break; /* * Packets coming from the hook make no sense * to sessions in these states. Throw them away. */ case PPPOE_SINIT: case PPPOE_SREQ: case PPPOE_SOFFER: case PPPOE_SNONE: case PPPOE_LISTENING: case PPPOE_DEAD: default: LEAVE(ENETUNREACH); } } quit: NG_FREE_DATA(m, meta); return error; } /* * Do local shutdown processing.. * If we are a persistant device, we might refuse to go away, and * we'd only remove our links and reset ourself. */ static int ng_pppoe_rmnode(node_p node) { const priv_p privdata = node->private; AAA node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); node->private = NULL; ng_unref(privdata->node); FREE(privdata, M_NETGRAPH); return (0); } /* * 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_pppoe_connect(hook_p hook) { /* be really amiable and just say "YUP that's OK by me! " */ return (0); } /* * Hook disconnection * * Clean up all dangling links and information about the session/hook. * For this type, removal of the last link destroys the node */ static int ng_pppoe_disconnect(hook_p hook) { node_p node = hook->node; priv_p privp = node->private; sessp sp; int hooks; AAA hooks = node->numhooks; /* this one already not counted */ if (hook->private == &privp->debug_hook) { privp->debug_hook = NULL; } else if (hook->private == &privp->ethernet_hook) { privp->ethernet_hook = NULL; ng_rmnode(node); } else { sp = hook->private; if (sp->state != PPPOE_SNONE ) { pppoe_send_event(sp, NGM_PPPOE_CLOSE); } /* * According to the spec, if we are connected, * we should send a DISC packet if we are shutting down * a session. */ if ((privp->ethernet_hook) && ((sp->state == PPPOE_CONNECTED) || (sp->state == PPPOE_NEWCONNECTED))) { struct mbuf *m; struct pppoe_full_hdr *wh; struct pppoe_tag *tag; int msglen = strlen(SIGNOFF); void *dummy = NULL; int error = 0; /* revert the stored header to DISC/PADT mode */ wh = &sp->pkt_hdr; wh->ph.code = PADT_CODE; wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; /* generate a packet of that type */ MGETHDR(m, M_DONTWAIT, MT_DATA); if(m == NULL) printf("pppoe: Session out of mbufs\n"); else { m->m_pkthdr.rcvif = NULL; m->m_pkthdr.len = m->m_len = sizeof(*wh); bcopy((caddr_t)wh, mtod(m, caddr_t), sizeof(*wh)); /* * Add a General error message and adjust * sizes */ wh = mtod(m, struct pppoe_full_hdr *); tag = wh->ph.tag; tag->tag_type = PTT_GEN_ERR; tag->tag_len = htons((u_int16_t)msglen); strncpy(tag->tag_data, SIGNOFF, msglen); m->m_pkthdr.len = (m->m_len += sizeof(*tag) + msglen); wh->ph.length = htons(sizeof(*tag) + msglen); NG_SEND_DATA(error, privp->ethernet_hook, m, dummy); } } /* * As long as we have somewhere to store the timeout handle, * we may have a timeout pending.. get rid of it. */ if (sp->neg) { untimeout(pppoe_ticker, hook, sp->neg->timeout_handle); if (sp->neg->m) m_freem(sp->neg->m); FREE(sp->neg, M_NETGRAPH); } FREE(sp, M_NETGRAPH); hook->private = NULL; /* work out how many session hooks there are */ /* Node goes away on last session hook removal */ if (privp->ethernet_hook) hooks -= 1; if (privp->debug_hook) hooks -= 1; } if (node->numhooks == 0) ng_rmnode(node); return (0); } /* * timeouts come here. */ static void pppoe_ticker(void *arg) { int s = splnet(); hook_p hook = arg; sessp sp = hook->private; negp neg = sp->neg; int error = 0; struct mbuf *m0 = NULL; priv_p privp = hook->node->private; meta_p dummy = NULL; AAA switch(sp->state) { /* * resend the last packet, using an exponential backoff. * After a period of time, stop growing the backoff, * and either leave it, or revert to the start. */ case PPPOE_SINIT: case PPPOE_SREQ: /* timeouts on these produce resends */ m0 = m_copypacket(sp->neg->m, M_DONTWAIT); NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy); neg->timeout_handle = timeout(pppoe_ticker, hook, neg->timeout * hz); if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { if (sp->state == PPPOE_SREQ) { /* revert to SINIT mode */ pppoe_start(sp); } else { neg->timeout = PPPOE_TIMEOUT_LIMIT; } } break; case PPPOE_PRIMED: case PPPOE_SOFFER: /* a timeout on these says "give up" */ ng_destroy_hook(hook); break; default: /* timeouts have no meaning in other states */ printf("pppoe: unexpected timeout\n"); } splx(s); } static void sendpacket(sessp sp) { int error = 0; struct mbuf *m0 = NULL; hook_p hook = sp->hook; negp neg = sp->neg; priv_p privp = hook->node->private; meta_p dummy = NULL; AAA switch(sp->state) { case PPPOE_LISTENING: case PPPOE_DEAD: case PPPOE_SNONE: case PPPOE_CONNECTED: printf("pppoe: sendpacket: unexpected state\n"); break; case PPPOE_NEWCONNECTED: /* send the PADS without a timeout - we're now connected */ m0 = m_copypacket(sp->neg->m, M_DONTWAIT); NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy); break; case PPPOE_PRIMED: /* No packet to send, but set up the timeout */ neg->timeout_handle = timeout(pppoe_ticker, hook, PPPOE_OFFER_TIMEOUT * hz); break; case PPPOE_SOFFER: /* * send the offer but if they don't respond * in PPPOE_OFFER_TIMEOUT seconds, forget about it. */ m0 = m_copypacket(sp->neg->m, M_DONTWAIT); NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy); neg->timeout_handle = timeout(pppoe_ticker, hook, PPPOE_OFFER_TIMEOUT * hz); break; case PPPOE_SINIT: case PPPOE_SREQ: m0 = m_copypacket(sp->neg->m, M_DONTWAIT); NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy); neg->timeout_handle = timeout(pppoe_ticker, hook, (hz * PPPOE_INITIAL_TIMEOUT)); neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; break; default: error = EINVAL; printf("pppoe: timeout: bad state\n"); } /* return (error); */ } /* * Parse an incoming packet to see if any tags should be copied to the * output packet. Don't do any tags that have been handled in the main * state machine. */ static struct pppoe_tag* scan_tags(sessp sp, struct pppoe_hdr* ph) { char *end = (char *)next_tag(ph); char *ptn; struct pppoe_tag *pt = &ph->tag[0]; /* * Keep processing tags while a tag header will still fit. */ AAA while((char*)(pt + 1) <= end) { /* * If the tag data would go past the end of the packet, abort. */ ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); if(ptn > end) return NULL; switch (pt->tag_type) { case PTT_RELAY_SID: insert_tag(sp, pt); break; case PTT_EOL: return NULL; case PTT_SRV_NAME: case PTT_AC_NAME: case PTT_HOST_UNIQ: case PTT_AC_COOKIE: case PTT_VENDOR: case PTT_SRV_ERR: case PTT_SYS_ERR: case PTT_GEN_ERR: break; } pt = (struct pppoe_tag*)ptn; } return NULL; } static int pppoe_send_event(sessp sp, enum cmd cmdid) { int error; struct ng_mesg *msg; struct ngpppoe_sts *sts; AAA NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid, sizeof(struct ngpppoe_sts), M_NOWAIT); + if (msg == NULL) + return (ENOMEM); sts = (struct ngpppoe_sts *)msg->data; strncpy(sts->hook, sp->hook->name, NG_HOOKLEN + 1); - error = ng_send_msg(sp->hook->node, msg, sp->creator, NULL); + error = ng_send_msg(sp->hook->node, msg, sp->creator, NULL, NULL, NULL); return (error); } Index: head/sys/netgraph/ng_pptpgre.c =================================================================== --- head/sys/netgraph/ng_pptpgre.c (revision 69921) +++ head/sys/netgraph/ng_pptpgre.c (revision 69922) @@ -1,939 +1,938 @@ /* * ng_pptpgre.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_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $ */ /* * PPTP/GRE netgraph node type. * * This node type does the GRE encapsulation as specified for the PPTP * protocol (RFC 2637, section 4). This includes sequencing and * retransmission of frames, but not the actual packet delivery nor * any of the TCP control stream protocol. * * The "upper" hook of this node is suitable for attaching to a "ppp" * node link hook. The "lower" hook of this node is suitable for attaching * to a "ksocket" node on hook "inet/raw/gre". */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* GRE packet format, as used by PPTP */ struct greheader { #if BYTE_ORDER == LITTLE_ENDIAN u_char recursion:3; /* recursion control */ u_char ssr:1; /* strict source route */ u_char hasSeq:1; /* sequence number present */ u_char hasKey:1; /* key present */ u_char hasRoute:1; /* routing present */ u_char hasSum:1; /* checksum present */ u_char vers:3; /* version */ u_char flags:4; /* flags */ u_char hasAck:1; /* acknowlege number present */ #elif BYTE_ORDER == BIG_ENDIAN u_char hasSum:1; /* checksum present */ u_char hasRoute:1; /* routing present */ u_char hasKey:1; /* key present */ u_char hasSeq:1; /* sequence number present */ u_char ssr:1; /* strict source route */ u_char recursion:3; /* recursion control */ u_char hasAck:1; /* acknowlege number present */ u_char flags:4; /* flags */ u_char vers:3; /* version */ #else #error BYTE_ORDER is not defined properly #endif u_int16_t proto; /* protocol (ethertype) */ u_int16_t length; /* payload length */ u_int16_t cid; /* call id */ u_int32_t data[0]; /* opt. seq, ack, then data */ }; /* The PPTP protocol ID used in the GRE 'proto' field */ #define PPTP_GRE_PROTO 0x880b /* Bits that must be set a certain way in all PPTP/GRE packets */ #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO) #define PPTP_INIT_MASK 0xef7fffff /* Min and max packet length */ #define PPTP_MAX_PAYLOAD (0xffff - sizeof(struct greheader) - 8) /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */ #define PPTP_TIME_SCALE 1000 /* milliseconds */ typedef u_int64_t pptptime_t; /* Acknowledgment timeout parameters and functions */ #define PPTP_XMIT_WIN 16 /* max xmit window */ #define PPTP_MIN_RTT (PPTP_TIME_SCALE / 10) /* 100 milliseconds */ #define PPTP_MIN_TIMEOUT (PPTP_TIME_SCALE / 500) /* 2 milliseconds */ #define PPTP_MAX_TIMEOUT (10 * PPTP_TIME_SCALE) /* 10 seconds */ /* When we recieve a packet, we wait to see if there's an outgoing packet we can piggy-back the ACK off of. These parameters determine the mimimum and maxmimum length of time we're willing to wait in order to do that. These have no effect unless "enableDelayedAck" is turned on. */ #define PPTP_MIN_ACK_DELAY (PPTP_TIME_SCALE / 500) /* 2 milliseconds */ #define PPTP_MAX_ACK_DELAY (PPTP_TIME_SCALE / 2) /* 500 milliseconds */ /* See RFC 2637 section 4.4 */ #define PPTP_ACK_ALPHA(x) ((x) >> 3) /* alpha = 0.125 */ #define PPTP_ACK_BETA(x) ((x) >> 2) /* beta = 0.25 */ #define PPTP_ACK_CHI(x) ((x) << 2) /* chi = 4 */ #define PPTP_ACK_DELTA(x) ((x) << 1) /* delta = 2 */ #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y)) /* We keep packet retransmit and acknowlegement state in this struct */ struct ng_pptpgre_ackp { int32_t ato; /* adaptive time-out value */ int32_t rtt; /* round trip time estimate */ int32_t dev; /* deviation estimate */ u_int16_t xmitWin; /* size of xmit window */ struct callout_handle sackTimer; /* send ack timer */ struct callout_handle rackTimer; /* recv ack timer */ node_p *sackTimerPtr; /* send ack timer pointer */ node_p *rackTimerPtr; /* recv ack timer pointer */ u_int32_t winAck; /* seq when xmitWin will grow */ pptptime_t timeSent[PPTP_XMIT_WIN]; #ifdef DEBUG_RAT pptptime_t timerStart; /* when rackTimer started */ pptptime_t timerLength; /* rackTimer duration */ #endif }; /* Node private data */ struct ng_pptpgre_private { hook_p upper; /* hook to upper layers */ hook_p lower; /* hook to lower layers */ struct ng_pptpgre_conf conf; /* configuration info */ struct ng_pptpgre_ackp ackp; /* packet transmit ack state */ u_int32_t recvSeq; /* last seq # we rcv'd */ u_int32_t xmitSeq; /* last seq # we sent */ u_int32_t recvAck; /* last seq # peer ack'd */ u_int32_t xmitAck; /* last seq # we ack'd */ struct timeval startTime; /* time node was created */ struct ng_pptpgre_stats stats; /* node statistics */ }; typedef struct ng_pptpgre_private *priv_p; /* Netgraph node methods */ static ng_constructor_t ng_pptpgre_constructor; static ng_rcvmsg_t ng_pptpgre_rcvmsg; static ng_shutdown_t ng_pptpgre_rmnode; static ng_newhook_t ng_pptpgre_newhook; static ng_rcvdata_t ng_pptpgre_rcvdata; static ng_disconnect_t ng_pptpgre_disconnect; /* Helper functions */ static int ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta); static int ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta); static void ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout); static void ng_pptpgre_start_recv_ack_timer(node_p node); static void ng_pptpgre_recv_ack_timeout(void *arg); static void ng_pptpgre_send_ack_timeout(void *arg); static void ng_pptpgre_reset(node_p node); static pptptime_t ng_pptpgre_time(node_p node); /* Parse type for struct ng_pptpgre_conf */ static const struct ng_parse_struct_info ng_pptpgre_conf_type_info = NG_PPTPGRE_CONF_TYPE_INFO; static const struct ng_parse_type ng_pptpgre_conf_type = { &ng_parse_struct_type, &ng_pptpgre_conf_type_info, }; /* Parse type for struct ng_pptpgre_stats */ static const struct ng_parse_struct_info ng_pptpgre_stats_type_info = NG_PPTPGRE_STATS_TYPE_INFO; static const struct ng_parse_type ng_pptp_stats_type = { &ng_parse_struct_type, &ng_pptpgre_stats_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_pptpgre_cmdlist[] = { { NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_SET_CONFIG, "setconfig", &ng_pptpgre_conf_type, NULL }, { NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_GET_CONFIG, "getconfig", NULL, &ng_pptpgre_conf_type }, { NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_GET_STATS, "getstats", NULL, &ng_pptp_stats_type }, { NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_CLR_STATS, "clrstats", NULL, NULL }, { NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_GETCLR_STATS, "getclrstats", NULL, &ng_pptp_stats_type }, { 0 } }; /* Node type descriptor */ static struct ng_type ng_pptpgre_typestruct = { NG_VERSION, NG_PPTPGRE_NODE_TYPE, NULL, ng_pptpgre_constructor, ng_pptpgre_rcvmsg, ng_pptpgre_rmnode, ng_pptpgre_newhook, NULL, NULL, ng_pptpgre_rcvdata, - ng_pptpgre_rcvdata, ng_pptpgre_disconnect, ng_pptpgre_cmdlist }; NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct); #define ERROUT(x) do { error = (x); goto done; } while (0) /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Node type constructor */ static int ng_pptpgre_constructor(node_p *nodep) { priv_p priv; int error; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv == NULL) return (ENOMEM); /* Call generic node constructor */ if ((error = ng_make_node_common(&ng_pptpgre_typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Initialize state */ callout_handle_init(&priv->ackp.sackTimer); callout_handle_init(&priv->ackp.rackTimer); /* Done */ return (0); } /* * Give our OK for a hook to be added. */ static int ng_pptpgre_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; hook_p *hookPtr; /* Check hook name */ if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) hookPtr = &priv->upper; else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) hookPtr = &priv->lower; else return (EINVAL); /* See if already connected */ if (*hookPtr != NULL) return (EISCONN); /* OK */ *hookPtr = hook; return (0); } /* * Receive a control message. */ static int ng_pptpgre_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rptr, hook_p lasthook) { const priv_p priv = node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_PPTPGRE_COOKIE: switch (msg->header.cmd) { case NGM_PPTPGRE_SET_CONFIG: { struct ng_pptpgre_conf *const newConf = (struct ng_pptpgre_conf *) msg->data; /* Check for invalid or illegal config */ if (msg->header.arglen != sizeof(*newConf)) ERROUT(EINVAL); ng_pptpgre_reset(node); /* reset on configure */ priv->conf = *newConf; break; } case NGM_PPTPGRE_GET_CONFIG: NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); bcopy(&priv->conf, resp->data, sizeof(priv->conf)); break; case NGM_PPTPGRE_GET_STATS: case NGM_PPTPGRE_CLR_STATS: case NGM_PPTPGRE_GETCLR_STATS: { if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) { NG_MKRESPONSE(resp, msg, sizeof(priv->stats), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); bcopy(&priv->stats, resp->data, sizeof(priv->stats)); } if (msg->header.cmd != NGM_PPTPGRE_GET_STATS) bzero(&priv->stats, sizeof(priv->stats)); break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /* * Receive incoming data on a hook. */ static int ng_pptpgre_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const node_p node = hook->node; const priv_p priv = node->private; /* If not configured, reject */ if (!priv->conf.enabled) { NG_FREE_DATA(m, meta); return (ENXIO); } /* Treat as xmit or recv data */ if (hook == priv->upper) return ng_pptpgre_xmit(node, m, meta); if (hook == priv->lower) return ng_pptpgre_recv(node, m, meta); panic("%s: weird hook", __FUNCTION__); } /* * Destroy node */ static int ng_pptpgre_rmnode(node_p node) { const priv_p priv = node->private; /* Reset node */ ng_pptpgre_reset(node); /* Take down netgraph node */ node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); bzero(priv, sizeof(*priv)); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); return (0); } /* * Hook disconnection */ static int ng_pptpgre_disconnect(hook_p hook) { const node_p node = hook->node; const priv_p priv = node->private; /* Zero out hook pointer */ if (hook == priv->upper) priv->upper = NULL; else if (hook == priv->lower) priv->lower = NULL; else panic("%s: unknown hook", __FUNCTION__); /* Go away if no longer connected to anything */ if (node->numhooks == 0) ng_rmnode(node); return (0); } /************************************************************************* TRANSMIT AND RECEIVE FUNCTIONS *************************************************************************/ /* * Transmit an outgoing frame, or just an ack if m is NULL. */ static int ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)]; struct greheader *const gre = (struct greheader *)buf; int grelen, error; /* Check if there's data */ if (m != NULL) { /* Is our transmit window full? */ if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, priv->recvAck) >= a->xmitWin) { priv->stats.xmitDrops++; NG_FREE_DATA(m, meta); return (ENOBUFS); } /* Sanity check frame length */ if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) { priv->stats.xmitTooBig++; NG_FREE_DATA(m, meta); return (EMSGSIZE); } } else priv->stats.xmitLoneAcks++; /* Build GRE header */ ((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE); gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0; gre->cid = htons(priv->conf.peerCid); /* Include sequence number if packet contains any data */ if (m != NULL) { gre->hasSeq = 1; a->timeSent[priv->xmitSeq - priv->recvAck] = ng_pptpgre_time(node); priv->xmitSeq++; gre->data[0] = htonl(priv->xmitSeq); } /* Include acknowledgement (and stop send ack timer) if needed */ if (priv->conf.enableAlwaysAck || priv->xmitAck != priv->recvSeq) { gre->hasAck = 1; gre->data[gre->hasSeq] = htonl(priv->recvSeq); priv->xmitAck = priv->recvSeq; a->sackTimerPtr = NULL; /* "stop" timer */ } /* Prepend GRE header to outgoing frame */ grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); if (m == NULL) { MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { priv->stats.memoryFailures++; NG_FREE_META(meta); return (ENOBUFS); } m->m_len = m->m_pkthdr.len = grelen; m->m_pkthdr.rcvif = NULL; } else { M_PREPEND(m, grelen, M_NOWAIT); if (m == NULL || (m->m_len < grelen && (m = m_pullup(m, grelen)) == NULL)) { priv->stats.memoryFailures++; NG_FREE_META(meta); return (ENOBUFS); } } bcopy(gre, mtod(m, u_char *), grelen); /* Update stats */ priv->stats.xmitPackets++; priv->stats.xmitOctets += m->m_pkthdr.len; /* Deliver packet */ NG_SEND_DATA(error, priv->lower, m, meta); /* Start receive ACK timer if data was sent and not already running */ if (error == 0 && gre->hasSeq && priv->xmitSeq == priv->recvAck + 1) ng_pptpgre_start_recv_ack_timer(node); return (error); } /* * Handle an incoming packet. The packet includes the IP header. */ static int ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta) { const priv_p priv = node->private; int iphlen, grelen, extralen; struct greheader *gre; struct ip *ip; int error = 0; /* Update stats */ priv->stats.recvPackets++; priv->stats.recvOctets += m->m_pkthdr.len; /* Sanity check packet length */ if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) { priv->stats.recvRunts++; bad: NG_FREE_DATA(m, meta); return (EINVAL); } /* Safely pull up the complete IP+GRE headers */ if (m->m_len < sizeof(*ip) + sizeof(*gre) && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) { priv->stats.memoryFailures++; NG_FREE_META(meta); return (ENOBUFS); } ip = mtod(m, struct ip *); iphlen = ip->ip_hl << 2; if (m->m_len < iphlen + sizeof(*gre)) { if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) { priv->stats.memoryFailures++; NG_FREE_META(meta); return (ENOBUFS); } ip = mtod(m, struct ip *); } gre = (struct greheader *)((u_char *)ip + iphlen); grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); if (m->m_pkthdr.len < iphlen + grelen) { priv->stats.recvRunts++; goto bad; } if (m->m_len < iphlen + grelen) { if ((m = m_pullup(m, iphlen + grelen)) == NULL) { priv->stats.memoryFailures++; NG_FREE_META(meta); return (ENOBUFS); } ip = mtod(m, struct ip *); gre = (struct greheader *)((u_char *)ip + iphlen); } /* Sanity check packet length and GRE header bits */ extralen = m->m_pkthdr.len - (iphlen + grelen + (u_int16_t)ntohs(gre->length)); if (extralen < 0) { priv->stats.recvBadGRE++; goto bad; } if ((ntohl(*((u_int32_t *)gre)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) { priv->stats.recvBadGRE++; goto bad; } if (ntohs(gre->cid) != priv->conf.cid) { priv->stats.recvBadCID++; goto bad; } /* Look for peer ack */ if (gre->hasAck) { struct ng_pptpgre_ackp *const a = &priv->ackp; const u_int32_t ack = ntohl(gre->data[gre->hasSeq]); const int index = ack - priv->recvAck - 1; const long sample = ng_pptpgre_time(node) - a->timeSent[index]; long diff; /* Sanity check ack value */ if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) { priv->stats.recvBadAcks++; goto badAck; /* we never sent it! */ } if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0) goto badAck; /* ack already timed out */ priv->recvAck = ack; /* Update adaptive timeout stuff */ diff = sample - a->rtt; a->rtt += PPTP_ACK_ALPHA(diff); if (diff < 0) diff = -diff; a->dev += PPTP_ACK_BETA(diff - a->dev); a->ato = a->rtt + PPTP_ACK_CHI(a->dev); if (a->ato > PPTP_MAX_TIMEOUT) a->ato = PPTP_MAX_TIMEOUT; if (a->ato < PPTP_MIN_TIMEOUT) a->ato = PPTP_MIN_TIMEOUT; /* Shift packet transmit times in our transmit window */ ovbcopy(a->timeSent + index + 1, a->timeSent, sizeof(*a->timeSent) * (PPTP_XMIT_WIN - (index + 1))); /* If we sent an entire window, increase window size by one */ if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0 && a->xmitWin < PPTP_XMIT_WIN) { a->xmitWin++; a->winAck = ack + a->xmitWin; } /* Stop/(re)start receive ACK timer as necessary */ a->rackTimerPtr = NULL; if (priv->recvAck != priv->xmitSeq) ng_pptpgre_start_recv_ack_timer(node); } badAck: /* See if frame contains any data */ if (gre->hasSeq) { struct ng_pptpgre_ackp *const a = &priv->ackp; const u_int32_t seq = ntohl(gre->data[0]); /* Sanity check sequence number */ if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) { if (seq == priv->recvSeq) priv->stats.recvDuplicates++; else priv->stats.recvOutOfOrder++; goto bad; /* out-of-order or dup */ } priv->recvSeq = seq; /* We need to acknowledge this packet; do it soon... */ if (a->sackTimerPtr == NULL) { int maxWait; /* Take 1/4 of the estimated round trip time */ maxWait = (a->rtt >> 2); /* If delayed ACK is disabled, send it now */ if (!priv->conf.enableDelayedAck || maxWait < PPTP_MIN_ACK_DELAY) ng_pptpgre_xmit(node, NULL, NULL); else { /* send the ack later */ if (maxWait > PPTP_MAX_ACK_DELAY) maxWait = PPTP_MAX_ACK_DELAY; ng_pptpgre_start_send_ack_timer(node, maxWait); } } /* Trim mbuf down to internal payload */ m_adj(m, iphlen + grelen); if (extralen > 0) m_adj(m, -extralen); /* Deliver frame to upper layers */ NG_SEND_DATA(error, priv->upper, m, meta); } else { priv->stats.recvLoneAcks++; NG_FREE_DATA(m, meta); /* no data to deliver */ } return (error); } /************************************************************************* TIMER RELATED FUNCTIONS *************************************************************************/ /* * Start a timer for the peer's acknowledging our oldest unacknowledged * sequence number. If we get an ack for this sequence number before * the timer goes off, we cancel the timer. Resets currently running * recv ack timer, if any. */ static void ng_pptpgre_start_recv_ack_timer(node_p node) { const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; int remain, ticks; /* Compute how long until oldest unack'd packet times out, and reset the timer to that time. */ KASSERT(a->rackTimerPtr == NULL, ("%s: rackTimer", __FUNCTION__)); remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node); if (remain < 0) remain = 0; #ifdef DEBUG_RAT a->timerLength = remain; a->timerStart = ng_pptpgre_time(node); #endif /* Start new timer */ MALLOC(a->rackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT); if (a->rackTimerPtr == NULL) { priv->stats.memoryFailures++; return; /* XXX potential hang here */ } *a->rackTimerPtr = node; /* insures the correct timeout event */ node->refs++; /* Be conservative: timeout() can return up to 1 tick early */ ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1; a->rackTimer = timeout(ng_pptpgre_recv_ack_timeout, a->rackTimerPtr, ticks); } /* * The peer has failed to acknowledge the oldest unacknowledged sequence * number within the time allotted. Update our adaptive timeout parameters * and reset/restart the recv ack timer. */ static void ng_pptpgre_recv_ack_timeout(void *arg) { int s = splnet(); const node_p node = *((node_p *)arg); const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; /* This complicated stuff is needed to avoid race conditions */ FREE(arg, M_NETGRAPH); KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__)); if ((node->flags & NG_INVALID) != 0) { /* shutdown race condition */ ng_unref(node); splx(s); return; } ng_unref(node); if (arg != a->rackTimerPtr) { /* timer stopped race condition */ splx(s); return; } a->rackTimerPtr = NULL; /* Update adaptive timeout stuff */ priv->stats.recvAckTimeouts++; a->rtt = PPTP_ACK_DELTA(a->rtt); a->ato = a->rtt + PPTP_ACK_CHI(a->dev); if (a->ato > PPTP_MAX_TIMEOUT) a->ato = PPTP_MAX_TIMEOUT; if (a->ato < PPTP_MIN_TIMEOUT) a->ato = PPTP_MIN_TIMEOUT; #ifdef DEBUG_RAT log(LOG_DEBUG, "RAT now=%d seq=0x%x sent=%d tstart=%d tlen=%d ato=%d\n", (int)ng_pptpgre_time(node), priv->recvAck + 1, (int)a->timeSent[0], (int)a->timerStart, (int)a->timerLength, a->ato); #endif /* Reset ack and sliding window */ priv->recvAck = priv->xmitSeq; /* pretend we got the ack */ a->xmitWin = (a->xmitWin + 1) / 2; /* shrink transmit window */ a->winAck = priv->recvAck + a->xmitWin; /* reset win expand time */ splx(s); } /* * Start the send ack timer. This assumes the timer is not * already running. */ static void ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout) { const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; int ticks; /* Start new timer */ KASSERT(a->sackTimerPtr == NULL, ("%s: sackTimer", __FUNCTION__)); MALLOC(a->sackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT); if (a->sackTimerPtr == NULL) { priv->stats.memoryFailures++; return; /* XXX potential hang here */ } *a->sackTimerPtr = node; node->refs++; /* Be conservative: timeout() can return up to 1 tick early */ ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE); a->sackTimer = timeout(ng_pptpgre_send_ack_timeout, a->sackTimerPtr, ticks); } /* * We've waited as long as we're willing to wait before sending an * acknowledgement to the peer for received frames. We had hoped to * be able to piggy back our acknowledgement on an outgoing data frame, * but apparently there haven't been any since. So send the ack now. */ static void ng_pptpgre_send_ack_timeout(void *arg) { int s = splnet(); const node_p node = *((node_p *)arg); const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; /* This complicated stuff is needed to avoid race conditions */ FREE(arg, M_NETGRAPH); KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__)); if ((node->flags & NG_INVALID) != 0) { /* shutdown race condition */ ng_unref(node); splx(s); return; } ng_unref(node); if (a->sackTimerPtr != arg) { /* timer stopped race condition */ splx(s); return; } a->sackTimerPtr = NULL; /* Send a frame with an ack but no payload */ ng_pptpgre_xmit(node, NULL, NULL); splx(s); } /************************************************************************* MISC FUNCTIONS *************************************************************************/ /* * Reset state */ static void ng_pptpgre_reset(node_p node) { const priv_p priv = node->private; struct ng_pptpgre_ackp *const a = &priv->ackp; /* Reset adaptive timeout state */ a->ato = PPTP_MAX_TIMEOUT; a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10; /* ppd in 10ths */ if (a->rtt < PPTP_MIN_RTT) a->rtt = PPTP_MIN_RTT; a->dev = 0; a->xmitWin = (priv->conf.recvWin + 1) / 2; if (a->xmitWin < 2) /* often the first packet is lost */ a->xmitWin = 2; /* because the peer isn't ready */ if (a->xmitWin > PPTP_XMIT_WIN) a->xmitWin = PPTP_XMIT_WIN; a->winAck = a->xmitWin; /* Reset sequence numbers */ priv->recvSeq = ~0; priv->recvAck = ~0; priv->xmitSeq = ~0; priv->xmitAck = ~0; /* Reset start time */ getmicrouptime(&priv->startTime); /* Reset stats */ bzero(&priv->stats, sizeof(priv->stats)); /* "Stop" timers */ a->sackTimerPtr = NULL; a->rackTimerPtr = NULL; } /* * Return the current time scaled & translated to our internally used format. */ static pptptime_t ng_pptpgre_time(node_p node) { const priv_p priv = node->private; struct timeval tv; pptptime_t t; microuptime(&tv); if (tv.tv_sec < priv->startTime.tv_sec || (tv.tv_sec == priv->startTime.tv_sec && tv.tv_usec < priv->startTime.tv_usec)) return (0); timevalsub(&tv, &priv->startTime); t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE; t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE); return(t); } Index: head/sys/netgraph/ng_rfc1490.c =================================================================== --- head/sys/netgraph/ng_rfc1490.c (revision 69921) +++ head/sys/netgraph/ng_rfc1490.c (revision 69922) @@ -1,346 +1,345 @@ /* * ng_rfc1490.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_rfc1490.c,v 1.22 1999/11/01 09:24:52 julian Exp $ */ /* * This node does RFC 1490 multiplexing. * * NOTE: RFC 1490 is updated by RFC 2427. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * DEFINITIONS */ /* Q.922 stuff -- see RFC 1490 */ #define HDLC_UI 0x03 #define NLPID_IP 0xCC #define NLPID_PPP 0xCF #define NLPID_SNAP 0x80 #define NLPID_Q933 0x08 #define NLPID_CLNP 0x81 #define NLPID_ESIS 0x82 #define NLPID_ISIS 0x83 /* Node private data */ struct ng_rfc1490_private { hook_p downlink; hook_p ppp; hook_p inet; }; typedef struct ng_rfc1490_private *priv_p; /* Netgraph node methods */ static ng_constructor_t ng_rfc1490_constructor; static ng_rcvmsg_t ng_rfc1490_rcvmsg; static ng_shutdown_t ng_rfc1490_rmnode; static ng_newhook_t ng_rfc1490_newhook; static ng_rcvdata_t ng_rfc1490_rcvdata; static ng_disconnect_t ng_rfc1490_disconnect; /* Node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_RFC1490_NODE_TYPE, NULL, ng_rfc1490_constructor, ng_rfc1490_rcvmsg, ng_rfc1490_rmnode, ng_rfc1490_newhook, NULL, NULL, ng_rfc1490_rcvdata, - ng_rfc1490_rcvdata, ng_rfc1490_disconnect, NULL }; NETGRAPH_INIT(rfc1490, &typestruct); /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ /* * Node constructor */ static int ng_rfc1490_constructor(node_p *nodep) { priv_p priv; int error; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv == NULL) return (ENOMEM); /* Call generic node constructor */ if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Done */ return (0); } /* * Give our ok for a hook to be added */ static int ng_rfc1490_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = node->private; if (!strcmp(name, NG_RFC1490_HOOK_DOWNSTREAM)) { if (priv->downlink) return (EISCONN); priv->downlink = hook; } else if (!strcmp(name, NG_RFC1490_HOOK_PPP)) { if (priv->ppp) return (EISCONN); priv->ppp = hook; } else if (!strcmp(name, NG_RFC1490_HOOK_INET)) { if (priv->inet) return (EISCONN); priv->inet = hook; } else return (EINVAL); return (0); } /* * Receive a control message. We don't support any special ones. */ static int ng_rfc1490_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rp, hook_p lasthook) { FREE(msg, M_NETGRAPH); return (EINVAL); } /* * Receive data on a hook and encapsulate according to RFC 1490. * Only those nodes marked (*) are supported by this routine so far. * * Q.922 control * | * | * -------------------------------------------- * | 0x03 | * UI I Frame * | | * --------------------------------- -------------- * | 0x08 | 0x81 |0xCC |0xCF | 0x00 |..01.... |..10.... * | | | | | 0x80 | | * Q.933 CLNP IP(*) PPP(*) SNAP ISO 8208 ISO 8208 * | (rfc1973) | Modulo 8 Modulo 128 * | | * -------------------- OUI * | | | * L2 ID L3 ID ------------------------- * | User |00-80-C2 |00-00-00 * | specified | | * | 0x70 PID Ethertype * | | | * ------------------- --------------... ---------- * |0x51 |0x4E | |0x4C |0x1 |0xB | |0x806 | * | | | | | | | | | * 7776 Q.922 Others 802.2 802.3 802.6 Others ARP(*) Others * * */ #define MAX_ENCAPS_HDR 8 #define ERROUT(x) do { error = (x); goto done; } while (0) #define OUICMP(P,A,B,C) ((P)[0]==(A) && (P)[1]==(B) && (P)[2]==(C)) static int ng_rfc1490_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const node_p node = hook->node; const priv_p priv = node->private; int error = 0; if (hook == priv->downlink) { u_char *start, *ptr; if (!m || (m->m_len < MAX_ENCAPS_HDR && !(m = m_pullup(m, MAX_ENCAPS_HDR)))) ERROUT(ENOBUFS); ptr = start = mtod(m, u_char *); /* Must be UI frame */ if (*ptr++ != HDLC_UI) ERROUT(0); /* Eat optional zero pad byte */ if (*ptr == 0x00) ptr++; /* Multiplex on NLPID */ switch (*ptr++) { case NLPID_SNAP: if (OUICMP(ptr, 0, 0, 0)) { /* It's an ethertype */ u_int16_t etype; ptr += 3; etype = ntohs(*((u_int16_t *) ptr)); ptr += 2; m_adj(m, ptr - start); switch (etype) { case ETHERTYPE_IP: NG_SEND_DATA(error, priv->inet, m, meta); break; case ETHERTYPE_ARP: case ETHERTYPE_REVARP: default: ERROUT(0); } } else if (OUICMP(ptr, 0x00, 0x80, 0xc2)) /* 802.1 bridging */ ERROUT(0); else /* Other weird stuff... */ ERROUT(0); break; case NLPID_IP: m_adj(m, ptr - start); NG_SEND_DATA(error, priv->inet, m, meta); break; case NLPID_PPP: m_adj(m, ptr - start); NG_SEND_DATA(error, priv->ppp, m, meta); break; case NLPID_Q933: case NLPID_CLNP: case NLPID_ESIS: case NLPID_ISIS: ERROUT(0); default: /* Try PPP (see RFC 1973) */ ptr--; /* NLPID becomes PPP proto */ if ((*ptr & 0x01) == 0x01) ERROUT(0); m_adj(m, ptr - start); NG_SEND_DATA(error, priv->ppp, m, meta); break; } } else if (hook == priv->ppp) { M_PREPEND(m, 2, M_DONTWAIT); /* Prepend PPP NLPID */ if (!m) ERROUT(ENOBUFS); mtod(m, u_char *)[0] = HDLC_UI; mtod(m, u_char *)[1] = NLPID_PPP; NG_SEND_DATA(error, priv->downlink, m, meta); } else if (hook == priv->inet) { M_PREPEND(m, 2, M_DONTWAIT); /* Prepend IP NLPID */ if (!m) ERROUT(ENOBUFS); mtod(m, u_char *)[0] = HDLC_UI; mtod(m, u_char *)[1] = NLPID_IP; NG_SEND_DATA(error, priv->downlink, m, meta); } else panic(__FUNCTION__); done: NG_FREE_DATA(m, meta); return (error); } /* * Nuke node */ static int ng_rfc1490_rmnode(node_p node) { const priv_p priv = node->private; /* Take down netgraph node */ node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); bzero(priv, sizeof(*priv)); node->private = NULL; ng_unref(node); /* let the node escape */ return (0); } /* * Hook disconnection */ static int ng_rfc1490_disconnect(hook_p hook) { const priv_p priv = hook->node->private; if (hook->node->numhooks == 0) ng_rmnode(hook->node); else if (hook == priv->downlink) priv->downlink = NULL; else if (hook == priv->inet) priv->inet = NULL; else if (hook == priv->ppp) priv->ppp = NULL; else panic(__FUNCTION__); return (0); } Index: head/sys/netgraph/ng_sample.c =================================================================== --- head/sys/netgraph/ng_sample.c (revision 69921) +++ head/sys/netgraph/ng_sample.c (revision 69922) @@ -1,470 +1,477 @@ /* * ng_sample.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_sample.c,v 1.13 1999/11/01 09:24:52 julian Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include /* * This section contains the netgraph method declarations for the * sample node. These methods define the netgraph 'type'. */ static ng_constructor_t ng_xxx_constructor; static ng_rcvmsg_t ng_xxx_rcvmsg; static ng_shutdown_t ng_xxx_rmnode; static ng_newhook_t ng_xxx_newhook; static ng_connect_t ng_xxx_connect; static ng_rcvdata_t ng_xxx_rcvdata; /* note these are both ng_rcvdata_t */ -static ng_rcvdata_t ng_xxx_rcvdataq; /* note these are both ng_rcvdata_t */ static ng_disconnect_t ng_xxx_disconnect; /* Parse type for struct ngxxxstat */ static const struct ng_parse_struct_info ng_xxx_stat_type_info = NG_XXX_STATS_TYPE_INFO; static const struct ng_parse_type ng_xxx_stat_type = { &ng_parse_struct_type, &ng_xxx_stat_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_xxx_cmdlist[] = { { NGM_XXX_COOKIE, NGM_XXX_GET_STATUS, "getstatus", NULL, &ng_xxx_stat_type, }, { NGM_XXX_COOKIE, NGM_XXX_SET_FLAG, "setflag", &ng_parse_int32_type, NULL }, { 0 } }; /* Netgraph node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_XXX_NODE_TYPE, NULL, ng_xxx_constructor, ng_xxx_rcvmsg, ng_xxx_rmnode, ng_xxx_newhook, NULL, ng_xxx_connect, ng_xxx_rcvdata, - ng_xxx_rcvdataq, ng_xxx_disconnect, ng_xxx_cmdlist }; NETGRAPH_INIT(xxx, &typestruct); /* Information we store for each hook on each node */ struct XXX_hookinfo { int dlci; /* The DLCI it represents, -1 == downstream */ int channel; /* The channel representing this DLCI */ hook_p hook; }; /* Information we store for each node */ struct XXX { struct XXX_hookinfo channel[XXX_NUM_DLCIS]; struct XXX_hookinfo downstream_hook; node_p node; /* back pointer to node */ hook_p debughook; u_int packets_in; /* packets in from downstream */ u_int packets_out; /* packets out towards downstream */ u_int32_t flags; }; typedef struct XXX *xxx_p; /* * Allocate the private data structure and the generic node * and link them together. * * ng_make_node_common() returns with a generic node struct * with a single reference for us.. we transfer it to the * private structure.. when we free the private struct we must * unref the node so it gets freed too. * * If this were a device node than this work would be done in the attach() * routine and the constructor would return EINVAL as you should not be able * to creatednodes that depend on hardware (unless you can add the hardware :) */ static int ng_xxx_constructor(node_p *nodep) { xxx_p privdata; int i, error; /* Initialize private descriptor */ MALLOC(privdata, xxx_p, sizeof(*privdata), M_NETGRAPH, M_NOWAIT | M_ZERO); if (privdata == NULL) return (ENOMEM); for (i = 0; i < XXX_NUM_DLCIS; i++) { privdata->channel[i].dlci = -2; privdata->channel[i].channel = i; } /* Call the 'generic' (ie, superclass) node constructor */ if ((error = ng_make_node_common(&typestruct, nodep))) { FREE(privdata, M_NETGRAPH); return (error); } /* Link structs together; this counts as our one reference to *nodep */ (*nodep)->private = privdata; privdata->node = *nodep; return (0); } /* * 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 XXX_hookinfo struct * so that the source of an input packet is easily identified. * (a dlci is a frame relay channel) */ static int ng_xxx_newhook(node_p node, hook_p hook, const char *name) { const xxx_p xxxp = node->private; const char *cp; int dlci = 0; int chan; #if 0 /* Possibly start up the device if it's not already going */ if ((xxxp->flags & SCF_RUNNING) == 0) { ng_xxx_start_hardware(xxxp); } #endif /* Example of how one might use hooks with embedded numbers: All * hooks start with 'dlci' and have a decimal trailing channel * number up to 4 digits Use the leadin defined int he associated .h * file. */ if (strncmp(name, NG_XXX_HOOK_DLCI_LEADIN, strlen(NG_XXX_HOOK_DLCI_LEADIN)) == 0) { char *eptr; cp = name + sizeof(NG_XXX_HOOK_DLCI_LEADIN); if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) return (EINVAL); dlci = (int)strtoul(cp, &eptr, 10); if (*eptr != '\0' || dlci < 0 || dlci > 1023) return (EINVAL); /* We have a dlci, now either find it, or allocate it */ for (chan = 0; chan < XXX_NUM_DLCIS; chan++) if (xxxp->channel[chan].dlci == dlci) break; if (chan == XXX_NUM_DLCIS) { for (chan = 0; chan < XXX_NUM_DLCIS; chan++) if (xxxp->channel[chan].dlci != -2) continue; if (chan == XXX_NUM_DLCIS) return (ENOBUFS); } if (xxxp->channel[chan].hook != NULL) return (EADDRINUSE); hook->private = xxxp->channel + chan; xxxp->channel[chan].hook = hook; return (0); } else if (strcmp(name, NG_XXX_HOOK_DOWNSTREAM) == 0) { /* Example of simple predefined hooks. */ /* do something specific to the downstream connection */ xxxp->downstream_hook.hook = hook; hook->private = &xxxp->downstream_hook; } else if (strcmp(name, NG_XXX_HOOK_DEBUG) == 0) { /* do something specific to a debug connection */ xxxp->debughook = hook; hook->private = 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_xxx_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { const xxx_p xxxp = node->private; struct ng_mesg *resp = NULL; int error = 0; /* Deal with message according to cookie and command */ switch (msg->header.typecookie) { case NGM_XXX_COOKIE: switch (msg->header.cmd) { case NGM_XXX_GET_STATUS: { struct ngxxxstat *stats; NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); if (!resp) { error = ENOMEM; break; } stats = (struct ngxxxstat *) resp->data; stats->packets_in = xxxp->packets_in; stats->packets_out = xxxp->packets_out; break; } case NGM_XXX_SET_FLAG: if (msg->header.arglen != sizeof(u_int32_t)) { error = EINVAL; break; } xxxp->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 */ if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); /* Free the message and return */ FREE(msg, M_NETGRAPH); return(error); } /* * Receive data, and do something with it. * Possibly send it out on another link after processing. * Possibly do something different if it comes from different * hooks. the caller will never free m or meta, so * if we use up this data or abort we must free BOTH of these. * * If we want, we may decide to force this data to be queued and reprocessed - * at the netgraph NETISR time. (at which time it will be entered using ng_xxx_rcvdataq(). + * at the netgraph NETISR time. + * We would do that by setting the HK_QUEUE flag on our hook. We would do that + * in the connect() method. */ static int ng_xxx_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { - int dlci = -2; - - if (hook->private) { - /* - * If it's dlci 1023, requeue it so that it's handled - * at a lower priority. This is how a node decides to - * defer a data message. - */ - dlci = ((struct XXX_hookinfo *) hook->private)->dlci; - if (dlci == 1023) { - return(ng_queue_data(hook->peer, m, meta)); - } - } - return(ng_xxx_rcvdataq(hook, m, meta)); -} - -/* - * Always accept the data. This version of rcvdata is called from the dequeueing routine. - */ -static int -ng_xxx_rcvdataq(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) -{ const xxx_p xxxp = hook->node->private; int chan = -2; int dlci = -2; int error; if (hook->private) { dlci = ((struct XXX_hookinfo *) hook->private)->dlci; chan = ((struct XXX_hookinfo *) hook->private)->channel; if (dlci != -1) { /* If received on a DLCI hook process for this * channel and pass it to the downstream module. * Normally one would add a multiplexing header at * the front here */ /* M_PREPEND(....) ; */ /* mtod(m, xxxxxx)->dlci = dlci; */ - error = ng_send_data(xxxp->downstream_hook.hook, + NG_SEND_DATA(error, xxxp->downstream_hook.hook, m, meta); xxxp->packets_out++; } else { /* data came from the multiplexed link */ dlci = 1; /* get dlci from header */ /* madjust(....) *//* chop off header */ for (chan = 0; chan < XXX_NUM_DLCIS; chan++) if (xxxp->channel[chan].dlci == dlci) break; if (chan == XXX_NUM_DLCIS) { NG_FREE_DATA(m, meta); return (ENETUNREACH); } /* If we were called at splnet, use the following: * NG_SEND_DATA(error, otherhook, m, meta); if this * node is running at some SPL other than SPLNET * then you should use instead: error = * ng_queueit(otherhook, m, meta); m = NULL: meta = * NULL; this queues the data using the standard * NETISR system and schedules the data to be picked * up again once the system has moved to SPLNET and * the processing of the data can continue. after * these are run 'm' and 'meta' should be considered * as invalid and NG_SEND_DATA actually zaps them. */ NG_SEND_DATA(error, xxxp->channel[chan].hook, m, meta); xxxp->packets_in++; } } else { /* It's the debug hook, throw it away.. */ if (hook == xxxp->downstream_hook.hook) NG_FREE_DATA(m, meta); } return 0; } #if 0 /* * If this were a device node, the data may have been received in response * to some interrupt. * in which case it would probably look as follows: */ devintr() { - meta_p meta = NULL; /* whatever metadata we might imagine goes + int error; * here */ /* get packet from device and send on */ m = MGET(blah blah) - error = ng_queueit(upstream, m, meta); /* see note above in - * xxx_rcvdata() */ + + NG_SEND_DATA_ONLY(error, xxxp->upstream_hook.hook, m); + /* see note above in xxx_rcvdata() */ + /* and ng_xxx_connect() */ } #endif /* 0 */ /* * Do local shutdown processing.. * If we are a persistant device, we might refuse to go away, and * we'd only remove our links and reset ourself. */ static int ng_xxx_rmnode(node_p node) { const xxx_p privdata = node->private; node->flags |= NG_INVALID; ng_cutlinks(node); #ifndef PERSISTANT_NODE ng_unname(node); node->private = NULL; ng_unref(privdata->node); FREE(privdata, M_NETGRAPH); #else privdata->packets_in = 0; /* reset stats */ privdata->packets_out = 0; node->flags &= ~NG_INVALID; /* reset invalid flag */ #endif /* PERSISTANT_NODE */ return (0); } /* * 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_xxx_connect(hook_p hook) { - /* be really amiable and just say "YUP that's OK by me! " */ +#if 0 + /* + * If we were a driver running at other than splnet then + * we should set the QUEUE bit on the edge so that we + * will deliver by queing. + */ + if /*it is the upstream hook */ + hook->peer->flags |= HK_QUEUE; +#endif +#if 0 + /* + * If for some reason we want incoming date to be queued + * by the NETISR system and delivered later we can set the same bit on + * OUR hook. (maybe to allow unwinding of the stack) + */ + + if (hook->private) { + int dlci; + /* + * If it's dlci 1023, requeue it so that it's handled + * at a lower priority. This is how a node decides to + * defer a data message. + */ + dlci = ((struct XXX_hookinfo *) hook->private)->dlci; + if (dlci == 1023) { + hook->flags |= HK_QUEUE; + } +#endif + /* otherwise 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_xxx_disconnect(hook_p hook) { if (hook->private) ((struct XXX_hookinfo *) (hook->private))->hook = NULL; if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } Index: head/sys/netgraph/ng_socket.c =================================================================== --- head/sys/netgraph/ng_socket.c (revision 69921) +++ head/sys/netgraph/ng_socket.c (revision 69922) @@ -1,1018 +1,1019 @@ /* * ng_socket.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_socket.c,v 1.28 1999/11/01 09:24:52 julian Exp $ */ /* * Netgraph socket nodes * * There are two types of netgraph sockets, control and data. * Control sockets have a netgraph node, but data sockets are * parasitic on control sockets, and have no node of their own. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NOTYET #include #endif #include #include #include #include /* * It's Ascii-art time! * +-------------+ +-------------+ * |socket (ctl)| |socket (data)| * +-------------+ +-------------+ * ^ ^ * | | * v v * +-----------+ +-----------+ * |pcb (ctl)| |pcb (data)| * +-----------+ +-----------+ * ^ ^ * | | * v v * +--------------------------+ * | Socket type private | * | data | * +--------------------------+ * ^ * | * v * +----------------+ * | struct ng_node | * +----------------+ */ /* Netgraph node methods */ static ng_constructor_t ngs_constructor; static ng_rcvmsg_t ngs_rcvmsg; static ng_shutdown_t ngs_rmnode; static ng_newhook_t ngs_newhook; static ng_rcvdata_t ngs_rcvdata; static ng_disconnect_t ngs_disconnect; /* Internal methods */ static int ng_attach_data(struct socket *so); static int ng_attach_cntl(struct socket *so); static int ng_attach_common(struct socket *so, int type); static void ng_detach_common(struct ngpcb *pcbp, int type); /*static int ng_internalize(struct mbuf *m, struct proc *p); */ static int ng_connect_data(struct sockaddr *nam, struct ngpcb *pcbp); static int ng_connect_cntl(struct sockaddr *nam, struct ngpcb *pcbp); static int ng_bind(struct sockaddr *nam, struct ngpcb *pcbp); static int ngs_mod_event(module_t mod, int event, void *data); static int ship_msg(struct ngpcb *pcbp, struct ng_mesg *msg, struct sockaddr_ng *addr); /* Netgraph type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_SOCKET_NODE_TYPE, ngs_mod_event, ngs_constructor, ngs_rcvmsg, ngs_rmnode, ngs_newhook, NULL, NULL, ngs_rcvdata, - ngs_rcvdata, ngs_disconnect, NULL }; NETGRAPH_INIT(socket, &typestruct); /* Buffer space */ static u_long ngpdg_sendspace = 20 * 1024; /* really max datagram size */ static u_long ngpdg_recvspace = 20 * 1024; /* List of all sockets */ LIST_HEAD(, ngpcb) ngsocklist; #define sotongpcb(so) ((struct ngpcb *)(so)->so_pcb) /* If getting unexplained errors returned, set this to "Debugger("X"); */ #ifndef TRAP_ERROR #define TRAP_ERROR #endif /*************************************************************** Control sockets ***************************************************************/ static int ngc_attach(struct socket *so, int proto, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); if (suser(p)) return (EPERM); if (pcbp != NULL) return (EISCONN); return (ng_attach_cntl(so)); } static int ngc_detach(struct socket *so) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp == NULL) return (EINVAL); ng_detach_common(pcbp, NG_CONTROL); return (0); } static int ngc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); struct sockaddr_ng *const sap = (struct sockaddr_ng *) addr; struct ng_mesg *resp; struct mbuf *m0; char *msg, *path = NULL; int len, error = 0; if (pcbp == NULL) { error = EINVAL; goto release; } #ifdef NOTYET if (control && (error = ng_internalize(control, p))) { if (pcbp->sockdata == NULL) { error = ENOTCONN; goto release; } } #else /* NOTYET */ if (control) { error = EINVAL; goto release; } #endif /* NOTYET */ /* Require destination as there may be >= 1 hooks on this node */ if (addr == NULL) { error = EDESTADDRREQ; goto release; } /* Allocate an expendable buffer for the path, chop off * the sockaddr header, and make sure it's NUL terminated */ len = sap->sg_len - 2; MALLOC(path, char *, len + 1, M_NETGRAPH, M_WAITOK); if (path == NULL) { error = ENOMEM; goto release; } bcopy(sap->sg_data, path, len); path[len] = '\0'; /* Move the actual message out of mbufs into a linear buffer. * Start by adding up the size of the data. (could use mh_len?) */ for (len = 0, m0 = m; m0 != NULL; m0 = m0->m_next) len += m0->m_len; /* Move the data into a linear buffer as well. Messages are not * delivered in mbufs. */ MALLOC(msg, char *, len + 1, M_NETGRAPH, M_WAITOK); if (msg == NULL) { error = ENOMEM; goto release; } m_copydata(m, 0, len, msg); /* The callee will free the msg when done. The addr is our business. */ error = ng_send_msg(pcbp->sockdata->node, - (struct ng_mesg *) msg, path, &resp); + (struct ng_mesg *) msg, path, NULL, NULL, &resp); /* If the callee responded with a synchronous response, then put it * back on the receive side of the socket; sap is source address. */ if (error == 0 && resp != NULL) error = ship_msg(pcbp, resp, sap); release: if (path != NULL) FREE(path, M_NETGRAPH); if (control != NULL) m_freem(control); if (m != NULL) m_freem(m); return (error); } static int ngc_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp == 0) return (EINVAL); return (ng_bind(nam, pcbp)); } static int ngc_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp == 0) return (EINVAL); return (ng_connect_cntl(nam, pcbp)); } /*************************************************************** Data sockets ***************************************************************/ static int ngd_attach(struct socket *so, int proto, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp != NULL) return (EISCONN); return (ng_attach_data(so)); } static int ngd_detach(struct socket *so) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp == NULL) return (EINVAL); ng_detach_common(pcbp, NG_DATA); return (0); } static int ngd_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); struct sockaddr_ng *const sap = (struct sockaddr_ng *) addr; - meta_p mp = NULL; int len, error; hook_p hook = NULL; char hookname[NG_HOOKLEN + 1]; if ((pcbp == NULL) || (control != NULL)) { error = EINVAL; goto release; } if (pcbp->sockdata == NULL) { error = ENOTCONN; goto release; } /* * If the user used any of these ways to not specify an address * then handle specially. */ if ((sap == NULL) || ((len = sap->sg_len) <= 2) || (*sap->sg_data == '\0')) { if (pcbp->sockdata->node->numhooks != 1) { error = EDESTADDRREQ; goto release; } /* * if exactly one hook exists, just use it. * Special case to allow write(2) to work on an ng_socket. */ hook = LIST_FIRST(&pcbp->sockdata->node->hooks); } else { if (len > NG_HOOKLEN) { error = EINVAL; goto release; } /* * chop off the sockaddr header, and make sure it's NUL * terminated */ bcopy(sap->sg_data, hookname, len); hookname[len] = '\0'; /* Find the correct hook from 'hookname' */ LIST_FOREACH(hook, &pcbp->sockdata->node->hooks, hooks) { if (strcmp(hookname, hook->name) == 0) break; } if (hook == NULL) error = EHOSTUNREACH; } /* Send data (OK if hook is NULL) */ - NG_SEND_DATA(error, hook, m, mp); /* makes m NULL */ + NG_SEND_DATA_ONLY(error, hook, m); /* makes m NULL */ release: if (control != NULL) m_freem(control); if (m != NULL) m_freem(m); return (error); } static int ngd_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { struct ngpcb *const pcbp = sotongpcb(so); if (pcbp == 0) return (EINVAL); return (ng_connect_data(nam, pcbp)); } /* * Used for both data and control sockets */ static int ng_setsockaddr(struct socket *so, struct sockaddr **addr) { struct ngpcb *pcbp; struct sockaddr_ng *sg; int sg_len, namelen, s; /* Why isn't sg_data a `char[1]' ? :-( */ sg_len = sizeof(struct sockaddr_ng) - sizeof(sg->sg_data) + 1; s = splnet(); pcbp = sotongpcb(so); - if (pcbp == 0) { + if ((pcbp == NULL) || (pcbp->sockdata == NULL)) { splx(s); return (EINVAL); } namelen = 0; /* silence compiler ! */ if (pcbp->sockdata->node->name != NULL) sg_len += namelen = strlen(pcbp->sockdata->node->name); MALLOC(sg, struct sockaddr_ng *, sg_len, M_SONAME, M_WAITOK | M_ZERO); if (pcbp->sockdata->node->name != NULL) bcopy(pcbp->sockdata->node->name, sg->sg_data, namelen); splx(s); sg->sg_len = sg_len; sg->sg_family = AF_NETGRAPH; *addr = (struct sockaddr *)sg; return (0); } /* * Attach a socket to it's protocol specific partner. * For a control socket, actually create a netgraph node and attach * to it as well. */ static int ng_attach_cntl(struct socket *so) { struct ngsock *privdata; struct ngpcb *pcbp; int error; /* Setup protocol control block */ if ((error = ng_attach_common(so, NG_CONTROL)) != 0) return (error); pcbp = sotongpcb(so); /* Allocate node private info */ MALLOC(privdata, struct ngsock *, sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO); if (privdata == NULL) { ng_detach_common(pcbp, NG_CONTROL); return (ENOMEM); } /* Make the generic node components */ if ((error = ng_make_node_common(&typestruct, &privdata->node)) != 0) { FREE(privdata, M_NETGRAPH); ng_detach_common(pcbp, NG_CONTROL); return (error); } privdata->node->private = privdata; /* Link the pcb and the node private data */ privdata->ctlsock = pcbp; pcbp->sockdata = privdata; privdata->refs++; return (0); } static int ng_attach_data(struct socket *so) { return(ng_attach_common(so, NG_DATA)); } /* * Set up a socket protocol control block. * This code is shared between control and data sockets. */ static int ng_attach_common(struct socket *so, int type) { struct ngpcb *pcbp; int error; /* Standard socket setup stuff */ error = soreserve(so, ngpdg_sendspace, ngpdg_recvspace); if (error) return (error); /* Allocate the pcb */ MALLOC(pcbp, struct ngpcb *, sizeof(*pcbp), M_PCB, M_WAITOK | M_ZERO); if (pcbp == NULL) return (ENOMEM); pcbp->type = type; /* Link the pcb and the socket */ so->so_pcb = (caddr_t) pcbp; pcbp->ng_socket = so; /* Add the socket to linked list */ LIST_INSERT_HEAD(&ngsocklist, pcbp, socks); return (0); } /* * Disassociate the socket from it's protocol specific * partner. If it's attached to a node's private data structure, * then unlink from that too. If we were the last socket attached to it, * then shut down the entire node. Shared code for control and data sockets. */ static void ng_detach_common(struct ngpcb *pcbp, int which) { struct ngsock *sockdata; if (pcbp->sockdata) { sockdata = pcbp->sockdata; pcbp->sockdata = NULL; switch (which) { case NG_CONTROL: sockdata->ctlsock = NULL; break; case NG_DATA: sockdata->datasock = NULL; break; default: panic(__FUNCTION__); } if ((--sockdata->refs == 0) && (sockdata->node != NULL)) ng_rmnode(sockdata->node); } pcbp->ng_socket->so_pcb = NULL; pcbp->ng_socket = NULL; LIST_REMOVE(pcbp, socks); FREE(pcbp, M_PCB); } #ifdef NOTYET /* * File descriptors can be passed into a AF_NETGRAPH socket. * Note, that file descriptors cannot be passed OUT. * Only character device descriptors are accepted. * Character devices are useful to connect a graph to a device, * which after all is the purpose of this whole system. */ static int ng_internalize(struct mbuf *control, struct proc *p) { struct filedesc *fdp = p->p_fd; struct cmsghdr *cm = mtod(control, struct cmsghdr *); struct file *fp; struct vnode *vn; int oldfds; int fd; if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET || cm->cmsg_len != control->m_len) { TRAP_ERROR; return (EINVAL); } /* Check there is only one FD. XXX what would more than one signify? */ oldfds = (cm->cmsg_len - sizeof(*cm)) / sizeof(int); if (oldfds != 1) { TRAP_ERROR; return (EINVAL); } /* Check that the FD given is legit. and change it to a pointer to a * struct file. */ fd = *(int *) (cm + 1); if ((unsigned) fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) { return (EBADF); } /* Depending on what kind of resource it is, act differently. For * devices, we treat it as a file. For a AF_NETGRAPH socket, * shortcut straight to the node. */ switch (fp->f_type) { case DTYPE_VNODE: vn = (struct vnode *) fp->f_data; if (vn && (vn->v_type == VCHR)) { /* for a VCHR, actually reference the FILE */ fp->f_count++; /* XXX then what :) */ /* how to pass on to other modules? */ } else { TRAP_ERROR; return (EINVAL); } break; default: TRAP_ERROR; return (EINVAL); } return (0); } #endif /* NOTYET */ /* * Connect the data socket to a named control socket node. */ static int ng_connect_data(struct sockaddr *nam, struct ngpcb *pcbp) { struct sockaddr_ng *sap; node_p farnode; struct ngsock *sockdata; int error; /* If we are already connected, don't do it again */ if (pcbp->sockdata != NULL) return (EISCONN); /* Find the target (victim) and check it doesn't already have a data * socket. Also check it is a 'socket' type node. */ sap = (struct sockaddr_ng *) nam; - if ((error = ng_path2node(NULL, sap->sg_data, &farnode, NULL, NULL))) + if ((error = ng_path2node(NULL, sap->sg_data, &farnode, NULL))) return (error); if (strcmp(farnode->type->name, NG_SOCKET_NODE_TYPE) != 0) return (EINVAL); sockdata = farnode->private; if (sockdata->datasock != NULL) return (EADDRINUSE); /* Link the PCB and the private data struct. and note the extra * reference */ sockdata->datasock = pcbp; pcbp->sockdata = sockdata; sockdata->refs++; return (0); } /* * Connect the existing control socket node to a named node:hook. * The hook we use on this end is the same name as the remote node name. */ static int ng_connect_cntl(struct sockaddr *nam, struct ngpcb *pcbp) { struct ngsock *const sockdata = pcbp->sockdata; struct sockaddr_ng *sap; char *node, *hook; node_p farnode; int rtn, error; sap = (struct sockaddr_ng *) nam; rtn = ng_path_parse(sap->sg_data, &node, NULL, &hook); if (rtn < 0 || node == NULL || hook == NULL) { TRAP_ERROR; return (EINVAL); } farnode = ng_findname(sockdata->node, node); if (farnode == NULL) { TRAP_ERROR; return (EADDRNOTAVAIL); } /* Connect, using a hook name the same as the far node name. */ error = ng_con_nodes(sockdata->node, node, farnode, hook); return error; } /* * Binding a socket means giving the corresponding node a name */ static int ng_bind(struct sockaddr *nam, struct ngpcb *pcbp) { struct ngsock *const sockdata = pcbp->sockdata; struct sockaddr_ng *const sap = (struct sockaddr_ng *) nam; if (sockdata == NULL) { TRAP_ERROR; return (EINVAL); } - if (sap->sg_len < 3 || sap->sg_data[sap->sg_len - 3] != '\0') { + if ((sap->sg_len < 4) + || (sap->sg_len > (NG_NODELEN + 3)) + || (sap->sg_data[0] == '\0') + || (sap->sg_data[sap->sg_len - 3] != '\0')) { TRAP_ERROR; return (EINVAL); } return (ng_name_node(sockdata->node, sap->sg_data)); } /* * Take a message and pass it up to the control socket associated * with the node. */ static int ship_msg(struct ngpcb *pcbp, struct ng_mesg *msg, struct sockaddr_ng *addr) { struct socket *const so = pcbp->ng_socket; struct mbuf *mdata; int msglen; /* Copy the message itself into an mbuf chain */ msglen = sizeof(struct ng_mesg) + msg->header.arglen; mdata = m_devget((caddr_t) msg, msglen, 0, NULL, NULL); /* Here we free the message, as we are the end of the line. * We need to do that regardless of whether we got mbufs. */ FREE(msg, M_NETGRAPH); if (mdata == NULL) { TRAP_ERROR; return (ENOBUFS); } /* Send it up to the socket */ if (sbappendaddr(&so->so_rcv, (struct sockaddr *) addr, mdata, NULL) == 0) { TRAP_ERROR; m_freem(mdata); return (ENOBUFS); } sorwakeup(so); return (0); } /* * You can only create new nodes from the socket end of things. */ static int ngs_constructor(node_p *nodep) { return (EINVAL); } /* * We allow any hook to be connected to the node. * There is no per-hook private information though. */ static int ngs_newhook(node_p node, hook_p hook, const char *name) { hook->private = node->private; return (0); } /* * Incoming messages get passed up to the control socket. * Unless they are for us specifically (socket_type) */ static int ngs_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook) { struct ngsock *const sockdata = node->private; struct ngpcb *const pcbp = sockdata->ctlsock; struct sockaddr_ng *addr; int addrlen; int error = 0; /* Only allow mesgs to be passed if we have the control socket. * Data sockets can only support the generic messages. */ if (pcbp == NULL) { TRAP_ERROR; return (EINVAL); } if (msg->header.typecookie == NGM_SOCKET_COOKIE) { switch (msg->header.cmd) { case NGM_SOCK_CMD_NOLINGER: sockdata->flags |= NGS_FLAG_NOLINGER; break; case NGM_SOCK_CMD_LINGER: sockdata->flags &= ~NGS_FLAG_NOLINGER; break; default: error = EINVAL; /* unknown command */ } /* Free the message and return */ FREE(msg, M_NETGRAPH); return(error); } /* Get the return address into a sockaddr */ if ((retaddr == NULL) || (*retaddr == '\0')) retaddr = ""; addrlen = strlen(retaddr); MALLOC(addr, struct sockaddr_ng *, addrlen + 4, M_NETGRAPH, M_NOWAIT); if (addr == NULL) { TRAP_ERROR; return (ENOMEM); } addr->sg_len = addrlen + 3; addr->sg_family = AF_NETGRAPH; bcopy(retaddr, addr->sg_data, addrlen); addr->sg_data[addrlen] = '\0'; /* Send it up */ error = ship_msg(pcbp, msg, addr); FREE(addr, M_NETGRAPH); return (error); } /* * Receive data on a hook */ static int ngs_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { struct ngsock *const sockdata = hook->node->private; struct ngpcb *const pcbp = sockdata->datasock; struct socket *so; struct sockaddr_ng *addr; char *addrbuf[NG_HOOKLEN + 1 + 4]; int addrlen; /* If there is no data socket, black-hole it */ if (pcbp == NULL) { NG_FREE_DATA(m, meta); return (0); } so = pcbp->ng_socket; /* Get the return address into a sockaddr. */ addrlen = strlen(hook->name); /* <= NG_HOOKLEN */ addr = (struct sockaddr_ng *) addrbuf; addr->sg_len = addrlen + 3; addr->sg_family = AF_NETGRAPH; bcopy(hook->name, addr->sg_data, addrlen); addr->sg_data[addrlen] = '\0'; /* We have no use for the meta data, free/clear it now. */ NG_FREE_META(meta); /* Try to tell the socket which hook it came in on */ if (sbappendaddr(&so->so_rcv, (struct sockaddr *) addr, m, NULL) == 0) { m_freem(m); TRAP_ERROR; return (ENOBUFS); } sorwakeup(so); return (0); } /* * Hook disconnection * * For this type, removal of the last link destroys the node * if the NOLINGER flag is set. */ static int ngs_disconnect(hook_p hook) { struct ngsock *const sockdata = hook->node->private; if ((sockdata->flags & NGS_FLAG_NOLINGER ) && (hook->node->numhooks == 0)) { ng_rmnode(hook->node); } return (0); } /* * Do local shutdown processing. * In this case, that involves making sure the socket * knows we should be shutting down. */ static int ngs_rmnode(node_p node) { struct ngsock *const sockdata = node->private; struct ngpcb *const dpcbp = sockdata->datasock; struct ngpcb *const pcbp = sockdata->ctlsock; ng_cutlinks(node); ng_unname(node); if (dpcbp != NULL) { soisdisconnected(dpcbp->ng_socket); dpcbp->sockdata = NULL; sockdata->datasock = NULL; sockdata->refs--; } if (pcbp != NULL) { soisdisconnected(pcbp->ng_socket); pcbp->sockdata = NULL; sockdata->ctlsock = NULL; sockdata->refs--; } node->private = NULL; ng_unref(node); FREE(sockdata, M_NETGRAPH); return (0); } /* * Control and data socket type descriptors */ static struct pr_usrreqs ngc_usrreqs = { NULL, /* abort */ pru_accept_notsupp, ngc_attach, ngc_bind, ngc_connect, pru_connect2_notsupp, pru_control_notsupp, ngc_detach, NULL, /* disconnect */ pru_listen_notsupp, NULL, /* setpeeraddr */ pru_rcvd_notsupp, pru_rcvoob_notsupp, ngc_send, pru_sense_null, NULL, /* shutdown */ ng_setsockaddr, sosend, soreceive, sopoll }; static struct pr_usrreqs ngd_usrreqs = { NULL, /* abort */ pru_accept_notsupp, ngd_attach, NULL, /* bind */ ngd_connect, pru_connect2_notsupp, pru_control_notsupp, ngd_detach, NULL, /* disconnect */ pru_listen_notsupp, NULL, /* setpeeraddr */ pru_rcvd_notsupp, pru_rcvoob_notsupp, ngd_send, pru_sense_null, NULL, /* shutdown */ ng_setsockaddr, sosend, soreceive, sopoll }; /* * Definitions of protocols supported in the NETGRAPH domain. */ extern struct domain ngdomain; /* stop compiler warnings */ static struct protosw ngsw[] = { { SOCK_DGRAM, &ngdomain, NG_CONTROL, PR_ATOMIC | PR_ADDR /* | PR_RIGHTS */, 0, 0, 0, 0, NULL, 0, 0, 0, 0, &ngc_usrreqs }, { SOCK_DGRAM, &ngdomain, NG_DATA, PR_ATOMIC | PR_ADDR, 0, 0, 0, 0, NULL, 0, 0, 0, 0, &ngd_usrreqs } }; struct domain ngdomain = { AF_NETGRAPH, "netgraph", 0, NULL, NULL, ngsw, &ngsw[sizeof(ngsw) / sizeof(ngsw[0])], 0, NULL, 0, 0 }; /* * Handle loading and unloading for this node type * This is to handle auxiliary linkages (e.g protocol domain addition). */ static int ngs_mod_event(module_t mod, int event, void *data) { int error = 0; switch (event) { case MOD_LOAD: /* Register protocol domain */ net_add_domain(&ngdomain); break; case MOD_UNLOAD: /* Insure there are no open netgraph sockets */ if (!LIST_EMPTY(&ngsocklist)) { error = EBUSY; break; } #ifdef NOTYET /* Unregister protocol domain XXX can't do this yet.. */ if ((error = net_rm_domain(&ngdomain)) != 0) break; #else error = EBUSY; #endif break; default: error = EOPNOTSUPP; break; } return (error); } SYSCTL_NODE(_net, AF_NETGRAPH, graph, CTLFLAG_RW, 0, "netgraph Family"); SYSCTL_INT(_net_graph, OID_AUTO, family, CTLFLAG_RD, 0, AF_NETGRAPH, ""); SYSCTL_NODE(_net_graph, OID_AUTO, data, CTLFLAG_RW, 0, "DATA"); SYSCTL_INT(_net_graph_data, OID_AUTO, proto, CTLFLAG_RD, 0, NG_DATA, ""); SYSCTL_NODE(_net_graph, OID_AUTO, control, CTLFLAG_RW, 0, "CONTROL"); SYSCTL_INT(_net_graph_control, OID_AUTO, proto, CTLFLAG_RD, 0, NG_CONTROL, ""); Index: head/sys/netgraph/ng_tee.c =================================================================== --- head/sys/netgraph/ng_tee.c (revision 69921) +++ head/sys/netgraph/ng_tee.c (revision 69922) @@ -1,386 +1,385 @@ /* * ng_tee.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_tee.c,v 1.18 1999/11/01 09:24:52 julian Exp $ */ /* * This node is like the tee(1) command and is useful for ``snooping.'' * It has 4 hooks: left, right, left2right, and right2left. Data * entering from the right is passed to the left and duplicated on * right2left, and data entering from the left is passed to the right * and duplicated on left2right. Data entering from left2right is * sent to right, and data from right2left to left. */ #include #include #include #include #include #include #include #include #include #include /* Per hook info */ struct hookinfo { hook_p hook; struct ng_tee_hookstat stats; }; /* Per node info */ struct privdata { node_p node; struct hookinfo left; struct hookinfo right; struct hookinfo left2right; struct hookinfo right2left; }; typedef struct privdata *sc_p; /* Netgraph methods */ static ng_constructor_t ngt_constructor; static ng_rcvmsg_t ngt_rcvmsg; static ng_shutdown_t ngt_rmnode; static ng_newhook_t ngt_newhook; static ng_rcvdata_t ngt_rcvdata; static ng_disconnect_t ngt_disconnect; /* Parse type for struct ng_tee_hookstat */ static const struct ng_parse_struct_info ng_tee_hookstat_type_info = NG_TEE_HOOKSTAT_INFO; static const struct ng_parse_type ng_tee_hookstat_type = { &ng_parse_struct_type, &ng_tee_hookstat_type_info, }; /* Parse type for struct ng_tee_stats */ static const struct ng_parse_struct_info ng_tee_stats_type_info = NG_TEE_STATS_INFO(&ng_tee_hookstat_type); static const struct ng_parse_type ng_tee_stats_type = { &ng_parse_struct_type, &ng_tee_stats_type_info, }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_tee_cmds[] = { { NGM_TEE_COOKIE, NGM_TEE_GET_STATS, "getstats", NULL, &ng_tee_stats_type }, { NGM_TEE_COOKIE, NGM_TEE_CLR_STATS, "clrstats", NULL, NULL }, { NGM_TEE_COOKIE, NGM_TEE_GETCLR_STATS, "getclrstats", NULL, &ng_tee_stats_type }, { 0 } }; /* Netgraph type descriptor */ static struct ng_type ng_tee_typestruct = { NG_VERSION, NG_TEE_NODE_TYPE, NULL, ngt_constructor, ngt_rcvmsg, ngt_rmnode, ngt_newhook, NULL, NULL, ngt_rcvdata, - ngt_rcvdata, ngt_disconnect, ng_tee_cmds }; NETGRAPH_INIT(tee, &ng_tee_typestruct); /* * Node constructor */ static int ngt_constructor(node_p *nodep) { sc_p privdata; int error = 0; MALLOC(privdata, sc_p, sizeof(*privdata), M_NETGRAPH, M_NOWAIT|M_ZERO); if (privdata == NULL) return (ENOMEM); if ((error = ng_make_node_common(&ng_tee_typestruct, nodep))) { FREE(privdata, M_NETGRAPH); return (error); } (*nodep)->private = privdata; privdata->node = *nodep; return (0); } /* * Add a hook */ static int ngt_newhook(node_p node, hook_p hook, const char *name) { const sc_p sc = node->private; if (strcmp(name, NG_TEE_HOOK_RIGHT) == 0) { sc->right.hook = hook; bzero(&sc->right.stats, sizeof(sc->right.stats)); hook->private = &sc->right; } else if (strcmp(name, NG_TEE_HOOK_LEFT) == 0) { sc->left.hook = hook; bzero(&sc->left.stats, sizeof(sc->left.stats)); hook->private = &sc->left; } else if (strcmp(name, NG_TEE_HOOK_RIGHT2LEFT) == 0) { sc->right2left.hook = hook; bzero(&sc->right2left.stats, sizeof(sc->right2left.stats)); hook->private = &sc->right2left; } else if (strcmp(name, NG_TEE_HOOK_LEFT2RIGHT) == 0) { sc->left2right.hook = hook; bzero(&sc->left2right.stats, sizeof(sc->left2right.stats)); hook->private = &sc->left2right; } else return (EINVAL); return (0); } /* * Receive a control message */ static int ngt_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { const sc_p sc = node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_TEE_COOKIE: switch (msg->header.cmd) { case NGM_TEE_GET_STATS: case NGM_TEE_CLR_STATS: case NGM_TEE_GETCLR_STATS: { struct ng_tee_stats *stats; if (msg->header.cmd != NGM_TEE_CLR_STATS) { NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); if (resp == NULL) { error = ENOMEM; goto done; } stats = (struct ng_tee_stats *)resp->data; bcopy(&sc->right.stats, &stats->right, sizeof(stats->right)); bcopy(&sc->left.stats, &stats->left, sizeof(stats->left)); bcopy(&sc->right2left.stats, &stats->right2left, sizeof(stats->right2left)); bcopy(&sc->left2right.stats, &stats->left2right, sizeof(stats->left2right)); } if (msg->header.cmd != NGM_TEE_GET_STATS) { bzero(&sc->right.stats, sizeof(sc->right.stats)); bzero(&sc->left.stats, sizeof(sc->left.stats)); bzero(&sc->right2left.stats, sizeof(sc->right2left.stats)); bzero(&sc->left2right.stats, sizeof(sc->left2right.stats)); } break; } default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /* * Receive data on a hook * * If data comes in the right link send a copy out right2left, and then * send the original onwards out through the left link. * Do the opposite for data coming in from the left link. * Data coming in right2left or left2right is forwarded * on through the appropriate destination hook as if it had come * from the other side. */ static int ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const sc_p sc = hook->node->private; struct hookinfo *const hinfo = (struct hookinfo *) hook->private; struct hookinfo *dest; struct hookinfo *dup; int error = 0; /* Which hook? */ if (hinfo == &sc->left) { dup = &sc->left2right; dest = &sc->right; } else if (hinfo == &sc->right) { dup = &sc->right2left; dest = &sc->left; } else if (hinfo == &sc->right2left) { dup = NULL; dest = &sc->left; } else if (hinfo == &sc->left2right) { dup = NULL; dest = &sc->right; } else panic("%s: no hook!", __FUNCTION__); /* Update stats on incoming hook */ hinfo->stats.inOctets += m->m_pkthdr.len; hinfo->stats.inFrames++; /* Duplicate packet and meta info if requried */ if (dup != NULL) { struct mbuf *m2; meta_p meta2; /* Copy packet */ m2 = m_dup(m, M_NOWAIT); if (m2 == NULL) { NG_FREE_DATA(m, meta); return (ENOBUFS); } /* Copy meta info */ if (meta != NULL) { MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH, M_NOWAIT); if (meta2 == NULL) { m_freem(m2); NG_FREE_DATA(m, meta); return (ENOMEM); } bcopy(meta, meta2, meta->used_len); meta2->allocated_len = meta->used_len; } else meta2 = NULL; /* Deliver duplicate */ dup->stats.outOctets += m->m_pkthdr.len; dup->stats.outFrames++; NG_SEND_DATA(error, dup->hook, m2, meta2); } /* Deliver frame out destination hook */ dest->stats.outOctets += m->m_pkthdr.len; dest->stats.outFrames++; NG_SEND_DATA(error, dest->hook, m, meta); return (0); } /* * Shutdown processing * * This is tricky. If we have both a left and right hook, then we * probably want to extricate ourselves and leave the two peers * still linked to each other. Otherwise we should just shut down as * a normal node would. * * To keep the scope of info correct the routine to "extract" a node * from two links is in ng_base.c. */ static int ngt_rmnode(node_p node) { const sc_p privdata = node->private; node->flags |= NG_INVALID; if (privdata->left.hook && privdata->right.hook) ng_bypass(privdata->left.hook, privdata->right.hook); ng_cutlinks(node); ng_unname(node); node->private = NULL; ng_unref(privdata->node); FREE(privdata, M_NETGRAPH); return (0); } /* * Hook disconnection */ static int ngt_disconnect(hook_p hook) { struct hookinfo *const hinfo = (struct hookinfo *) hook->private; KASSERT(hinfo != NULL, ("%s: null info", __FUNCTION__)); hinfo->hook = NULL; if (hook->node->numhooks == 0) ng_rmnode(hook->node); return (0); } Index: head/sys/netgraph/ng_tty.c =================================================================== --- head/sys/netgraph/ng_tty.c (revision 69921) +++ head/sys/netgraph/ng_tty.c (revision 69922) @@ -1,687 +1,697 @@ /* * ng_tty.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_tty.c,v 1.21 1999/11/01 09:24:52 julian Exp $ */ /* * This file implements a terminal line discipline that is also a * netgraph node. Installing this line discipline on a terminal device * instantiates a new netgraph node of this type, which allows access * to the device via the "hook" hook of the node. * * Once the line discipline is installed, you can find out the name * of the corresponding netgraph node via a NGIOCGINFO ioctl(). * * Incoming characters are delievered to the hook one at a time, each * in its own mbuf. You may optionally define a ``hotchar,'' which causes * incoming characters to be buffered up until either the hotchar is * seen or the mbuf is full (MHLEN bytes). Then all buffered characters * are immediately delivered. * * NOTE: This node operates at spltty(). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __i386__ /* fiddle with the spl locking */ #include #include #endif /* Misc defs */ #define MAX_MBUFQ 3 /* Max number of queued mbufs */ #define NGT_HIWATER 400 /* High water mark on output */ /* Per-node private info */ struct ngt_sc { struct tty *tp; /* Terminal device */ node_p node; /* Netgraph node */ hook_p hook; /* Netgraph hook */ struct mbuf *m; /* Incoming data buffer */ struct mbuf *qhead, **qtail; /* Queue of outgoing mbuf's */ short qlen; /* Length of queue */ short hotchar; /* Hotchar, or -1 if none */ u_int flags; /* Flags */ struct callout_handle chand; /* See man timeout(9) */ }; typedef struct ngt_sc *sc_p; /* Flags */ #define FLG_TIMEOUT 0x0001 /* A timeout is pending */ #define FLG_DEBUG 0x0002 /* Debugging */ #ifdef INVARIANTS #define QUEUECHECK(sc) \ do { \ struct mbuf **mp; \ int k; \ \ for (k = 0, mp = &sc->qhead; \ k <= MAX_MBUFQ && *mp; \ k++, mp = &(*mp)->m_nextpkt); \ if (k != sc->qlen || k > MAX_MBUFQ || *mp || mp != sc->qtail) \ panic(__FUNCTION__ ": queue"); \ } while (0) #else #define QUEUECHECK(sc) do {} while (0) #endif /* Line discipline methods */ static int ngt_open(dev_t dev, struct tty *tp); static int ngt_close(struct tty *tp, int flag); static int ngt_read(struct tty *tp, struct uio *uio, int flag); static int ngt_write(struct tty *tp, struct uio *uio, int flag); static int ngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *); static int ngt_input(int c, struct tty *tp); static int ngt_start(struct tty *tp); /* Netgraph methods */ static ng_constructor_t ngt_constructor; static ng_rcvmsg_t ngt_rcvmsg; static ng_shutdown_t ngt_shutdown; static ng_newhook_t ngt_newhook; +static ng_connect_t ngt_connect; static ng_rcvdata_t ngt_rcvdata; static ng_disconnect_t ngt_disconnect; static int ngt_mod_event(module_t mod, int event, void *data); /* Other stuff */ static void ngt_timeout(void *arg); #define ERROUT(x) do { error = (x); goto done; } while (0) /* Line discipline descriptor */ static struct linesw ngt_disc = { ngt_open, ngt_close, ngt_read, ngt_write, ngt_tioctl, ngt_input, ngt_start, ttymodem, NG_TTY_DFL_HOTCHAR /* XXX can't change this in serial driver */ }; /* Netgraph node type descriptor */ static struct ng_type typestruct = { NG_VERSION, NG_TTY_NODE_TYPE, ngt_mod_event, ngt_constructor, ngt_rcvmsg, ngt_shutdown, ngt_newhook, NULL, - NULL, + ngt_connect, ngt_rcvdata, - ngt_rcvdata, ngt_disconnect, NULL }; NETGRAPH_INIT(tty, &typestruct); static int ngt_unit; static int ngt_nodeop_ok; /* OK to create/remove node */ static int ngt_ldisc; /****************************************************************** LINE DISCIPLINE METHODS ******************************************************************/ /* * Set our line discipline on the tty. * Called from device open routine or ttioctl() at >= splsofttty() */ static int ngt_open(dev_t dev, struct tty *tp) { struct proc *const p = curproc; /* XXX */ char name[sizeof(NG_TTY_NODE_TYPE) + 8]; sc_p sc; int s, error; /* Super-user only */ if ((error = suser(p))) return (error); s = splnet(); (void) spltty(); /* XXX is this necessary? */ /* Already installed? */ if (tp->t_line == NETGRAPHDISC) { sc = (sc_p) tp->t_sc; if (sc != NULL && sc->tp == tp) goto done; } /* Initialize private struct */ MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO); if (sc == NULL) { error = ENOMEM; goto done; } sc->tp = tp; sc->hotchar = NG_TTY_DFL_HOTCHAR; sc->qtail = &sc->qhead; QUEUECHECK(sc); callout_handle_init(&sc->chand); /* Setup netgraph node */ ngt_nodeop_ok = 1; error = ng_make_node_common(&typestruct, &sc->node); ngt_nodeop_ok = 0; if (error) { FREE(sc, M_NETGRAPH); goto done; } snprintf(name, sizeof(name), "%s%d", typestruct.name, ngt_unit++); /* Set back pointers */ sc->node->private = sc; tp->t_sc = (caddr_t) sc; /* Assign node its name */ if ((error = ng_name_node(sc->node, name))) { log(LOG_ERR, "%s: node name exists?\n", name); ngt_nodeop_ok = 1; ng_rmnode(sc->node); ngt_nodeop_ok = 0; goto done; } /* * Pre-allocate cblocks to the an appropriate amount. * I'm not sure what is appropriate. */ ttyflush(tp, FREAD | FWRITE); clist_alloc_cblocks(&tp->t_canq, 0, 0); clist_alloc_cblocks(&tp->t_rawq, 0, 0); clist_alloc_cblocks(&tp->t_outq, MLEN + NGT_HIWATER, MLEN + NGT_HIWATER); done: /* Done */ splx(s); return (error); } /* * Line specific close routine, called from device close routine * and from ttioctl at >= splsofttty(). This causes the node to * be destroyed as well. */ static int ngt_close(struct tty *tp, int flag) { const sc_p sc = (sc_p) tp->t_sc; int s; s = spltty(); ttyflush(tp, FREAD | FWRITE); clist_free_cblocks(&tp->t_outq); tp->t_line = 0; if (sc != NULL) { if (sc->flags & FLG_TIMEOUT) { untimeout(ngt_timeout, sc, sc->chand); sc->flags &= ~FLG_TIMEOUT; } ngt_nodeop_ok = 1; ng_rmnode(sc->node); ngt_nodeop_ok = 0; tp->t_sc = NULL; } splx(s); return (0); } /* * Once the device has been turned into a node, we don't allow reading. */ static int ngt_read(struct tty *tp, struct uio *uio, int flag) { return (EIO); } /* * Once the device has been turned into a node, we don't allow writing. */ static int ngt_write(struct tty *tp, struct uio *uio, int flag) { return (EIO); } /* * We implement the NGIOCGINFO ioctl() defined in ng_message.h. */ static int ngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p) { const sc_p sc = (sc_p) tp->t_sc; int s, error = 0; s = spltty(); switch (cmd) { case NGIOCGINFO: { struct nodeinfo *const ni = (struct nodeinfo *) data; const node_p node = sc->node; bzero(ni, sizeof(*ni)); if (node->name) strncpy(ni->name, node->name, sizeof(ni->name) - 1); strncpy(ni->type, node->type->name, sizeof(ni->type) - 1); ni->id = (u_int32_t) node; ni->hooks = node->numhooks; break; } default: ERROUT(ENOIOCTL); } done: splx(s); return (error); } /* * Receive data coming from the device. We get one character at * a time, which is kindof silly. * Only guaranteed to be at splsofttty() or spltty(). */ static int ngt_input(int c, struct tty *tp) { const sc_p sc = (sc_p) tp->t_sc; const node_p node = sc->node; struct mbuf *m; int s, error = 0; if (!sc || tp != sc->tp) return (0); s = spltty(); if (!sc->hook) ERROUT(0); /* Check for error conditions */ if ((tp->t_state & TS_CONNECTED) == 0) { if (sc->flags & FLG_DEBUG) log(LOG_DEBUG, "%s: no carrier\n", node->name); ERROUT(0); } if (c & TTY_ERRORMASK) { /* framing error or overrun on this char */ if (sc->flags & FLG_DEBUG) log(LOG_DEBUG, "%s: line error %x\n", node->name, c & TTY_ERRORMASK); ERROUT(0); } c &= TTY_CHARMASK; /* Get a new header mbuf if we need one */ if (!(m = sc->m)) { MGETHDR(m, M_DONTWAIT, MT_DATA); if (!m) { if (sc->flags & FLG_DEBUG) log(LOG_ERR, "%s: can't get mbuf\n", node->name); ERROUT(ENOBUFS); } m->m_len = m->m_pkthdr.len = 0; m->m_pkthdr.rcvif = NULL; sc->m = m; } /* Add char to mbuf */ *mtod(m, u_char *) = c; m->m_data++; m->m_len++; m->m_pkthdr.len++; /* Ship off mbuf if it's time */ if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) { m->m_data = m->m_pktdat; - error = ng_queue_data(sc->hook, m, NULL); + NG_SEND_DATA_ONLY(error, sc->hook, m); sc->m = NULL; } done: splx(s); return (error); } /* * This is called when the device driver is ready for more output. * Called from tty system at splsofttty() or spltty(). * Also call from ngt_rcv_data() when a new mbuf is available for output. */ static int ngt_start(struct tty *tp) { const sc_p sc = (sc_p) tp->t_sc; int s; s = spltty(); while (tp->t_outq.c_cc < NGT_HIWATER) { /* XXX 2.2 specific ? */ struct mbuf *m = sc->qhead; /* Remove first mbuf from queue */ if (!m) break; if ((sc->qhead = m->m_nextpkt) == NULL) sc->qtail = &sc->qhead; sc->qlen--; QUEUECHECK(sc); /* Send as much of it as possible */ while (m) { struct mbuf *m2; int sent; sent = m->m_len - b_to_q(mtod(m, u_char *), m->m_len, &tp->t_outq); m->m_data += sent; m->m_len -= sent; if (m->m_len > 0) break; /* device can't take no more */ MFREE(m, m2); m = m2; } /* Put remainder of mbuf chain (if any) back on queue */ if (m) { m->m_nextpkt = sc->qhead; sc->qhead = m; if (sc->qtail == &sc->qhead) sc->qtail = &m->m_nextpkt; sc->qlen++; QUEUECHECK(sc); break; } } /* Call output process whether or not there is any output. We are * being called in lieu of ttstart and must do what it would. */ if (tp->t_oproc != NULL) (*tp->t_oproc) (tp); /* This timeout is needed for operation on a pseudo-tty, because the * pty code doesn't call pppstart after it has drained the t_outq. */ if (sc->qhead && (sc->flags & FLG_TIMEOUT) == 0) { sc->chand = timeout(ngt_timeout, sc, 1); sc->flags |= FLG_TIMEOUT; } splx(s); return (0); } /* * We still have data to output to the device, so try sending more. */ static void ngt_timeout(void *arg) { const sc_p sc = (sc_p) arg; int s; s = spltty(); sc->flags &= ~FLG_TIMEOUT; ngt_start(sc->tp); splx(s); } /****************************************************************** NETGRAPH NODE METHODS ******************************************************************/ /* * Initialize a new node of this type. * * We only allow nodes to be created as a result of setting * the line discipline on a tty, so always return an error if not. */ static int ngt_constructor(node_p *nodep) { if (!ngt_nodeop_ok) return (EOPNOTSUPP); return (ng_make_node_common(&typestruct, nodep)); } /* * Add a new hook. There can only be one. */ static int ngt_newhook(node_p node, hook_p hook, const char *name) { const sc_p sc = node->private; int s, error = 0; if (strcmp(name, NG_TTY_HOOK)) return (EINVAL); s = spltty(); if (sc->hook) ERROUT(EISCONN); sc->hook = hook; done: splx(s); return (error); } /* + * set the hooks into queueing mode (for outgoing packets) + */ +static int +ngt_connect(hook_p hook) +{ + hook->peer->flags |= HK_QUEUE; + return (0); +} + +/* * Disconnect the hook */ static int ngt_disconnect(hook_p hook) { const sc_p sc = hook->node->private; int s; s = spltty(); if (hook != sc->hook) panic(__FUNCTION__); sc->hook = NULL; m_freem(sc->m); sc->m = NULL; splx(s); return (0); } /* * Remove this node. The does the netgraph portion of the shutdown. * This should only be called indirectly from ngt_close(). */ static int ngt_shutdown(node_p node) { const sc_p sc = node->private; if (!ngt_nodeop_ok) return (EOPNOTSUPP); ng_unname(node); ng_cutlinks(node); node->private = NULL; ng_unref(sc->node); m_freem(sc->qhead); m_freem(sc->m); bzero(sc, sizeof(*sc)); FREE(sc, M_NETGRAPH); return (0); } /* * Receive incoming data from netgraph system. Put it on our * output queue and start output if necessary. */ static int ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const sc_p sc = hook->node->private; int s, error = 0; if (hook != sc->hook) panic(__FUNCTION__); NG_FREE_META(meta); s = spltty(); if (sc->qlen >= MAX_MBUFQ) ERROUT(ENOBUFS); m->m_nextpkt = NULL; *sc->qtail = m; sc->qtail = &m->m_nextpkt; sc->qlen++; QUEUECHECK(sc); m = NULL; if (sc->qlen == 1) ngt_start(sc->tp); done: splx(s); if (m) m_freem(m); return (error); } /* * Receive control message */ static int ngt_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr, hook_p lasthook) { const sc_p sc = (sc_p) node->private; struct ng_mesg *resp = NULL; int error = 0; switch (msg->header.typecookie) { case NGM_TTY_COOKIE: switch (msg->header.cmd) { case NGM_TTY_SET_HOTCHAR: { int hotchar; if (msg->header.arglen != sizeof(int)) ERROUT(EINVAL); hotchar = *((int *) msg->data); if (hotchar != (u_char) hotchar && hotchar != -1) ERROUT(EINVAL); sc->hotchar = hotchar; /* race condition is OK */ break; } case NGM_TTY_GET_HOTCHAR: NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT); if (!resp) ERROUT(ENOMEM); /* Race condition here is OK */ *((int *) resp->data) = sc->hotchar; break; default: ERROUT(EINVAL); } break; default: ERROUT(EINVAL); } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /****************************************************************** INITIALIZATION ******************************************************************/ /* * Handle loading and unloading for this node type */ static int ngt_mod_event(module_t mod, int event, void *data) { /* struct ng_type *const type = data;*/ int s, error = 0; switch (event) { case MOD_LOAD: /* Register line discipline */ s = spltty(); if ((ngt_ldisc = ldisc_register(NETGRAPHDISC, &ngt_disc)) < 0) { splx(s); log(LOG_ERR, "%s: can't register line discipline", __FUNCTION__); return (EIO); } splx(s); break; case MOD_UNLOAD: /* Unregister line discipline */ s = spltty(); ldisc_deregister(ngt_ldisc); splx(s); break; default: error = EOPNOTSUPP; break; } return (error); } Index: head/sys/netgraph/ng_vjc.c =================================================================== --- head/sys/netgraph/ng_vjc.c (revision 69921) +++ head/sys/netgraph/ng_vjc.c (revision 69922) @@ -1,626 +1,625 @@ /* * ng_vjc.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_vjc.c,v 1.17 1999/11/01 09:24:52 julian Exp $ */ /* * This node performs Van Jacobson IP header (de)compression. * You must have included net/slcompress.c in your kernel compilation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Check agreement with slcompress.c */ #if MAX_STATES != NG_VJC_MAX_CHANNELS #error NG_VJC_MAX_CHANNELS must be the same as MAX_STATES #endif /* Maximum length of a compressed TCP VJ header */ #define MAX_VJHEADER 19 /* Node private data */ struct ng_vjc_private { struct ngm_vjc_config conf; struct slcompress slc; hook_p ip; hook_p vjcomp; hook_p vjuncomp; hook_p vjip; }; typedef struct ng_vjc_private *priv_p; #define ERROUT(x) do { error = (x); goto done; } while (0) /* Netgraph node methods */ static ng_constructor_t ng_vjc_constructor; static ng_rcvmsg_t ng_vjc_rcvmsg; static ng_shutdown_t ng_vjc_rmnode; static ng_newhook_t ng_vjc_newhook; static ng_rcvdata_t ng_vjc_rcvdata; static ng_disconnect_t ng_vjc_disconnect; /* Helper stuff */ static struct mbuf *ng_vjc_pulluphdrs(struct mbuf *m, int knownTCP); /* Parse type for struct ngm_vjc_config */ static const struct ng_parse_struct_info ng_vjc_config_type_info = NG_VJC_CONFIG_TYPE_INFO; static const struct ng_parse_type ng_vjc_config_type = { &ng_parse_struct_type, &ng_vjc_config_type_info }; /* Parse type for the 'last_cs' and 'cs_next' fields in struct slcompress, which are pointers converted to integer indices, so parse them that way. */ #if _MACHINE_ARCH == i386 #define NG_VJC_TSTATE_PTR_TYPE &ng_parse_uint32_type #elif _MACHINE_ARCH == alpha #define NG_VJC_TSTATE_PTR_TYPE &ng_parse_uint64_type #else #error Unspported _MACHINE_ARCH #endif /* Parse type for the 'cs_hdr' field in a struct cstate. Ideally we would like to use a 'struct ip' type instead of a simple array of bytes. */ static const struct ng_parse_fixedarray_info ng_vjc_cs_hdr_type_info = { &ng_parse_hint8_type, MAX_HDR }; static const struct ng_parse_type ng_vjc_cs_hdr_type = { &ng_parse_fixedarray_type, &ng_vjc_cs_hdr_type_info }; /* Parse type for a struct cstate */ static const struct ng_parse_struct_info ng_vjc_cstate_type_info = { { { "cs_next", NG_VJC_TSTATE_PTR_TYPE }, { "cs_hlen", &ng_parse_uint16_type }, { "cs_id", &ng_parse_uint8_type }, { "cs_filler", &ng_parse_uint8_type }, { "cs_hdr", &ng_vjc_cs_hdr_type }, { NULL }, } }; static const struct ng_parse_type ng_vjc_cstate_type = { &ng_parse_struct_type, &ng_vjc_cstate_type_info }; /* Parse type for an array of MAX_STATES struct cstate's, ie, tstate & rstate */ static const struct ng_parse_fixedarray_info ng_vjc_cstatearray_type_info = { &ng_vjc_cstate_type, MAX_STATES }; static const struct ng_parse_type ng_vjc_cstatearray_type = { &ng_parse_fixedarray_type, &ng_vjc_cstatearray_type_info }; /* Parse type for struct slcompress. Keep this in sync with the definition of struct slcompress defined in */ static const struct ng_parse_struct_info ng_vjc_slcompress_type_info = { { { "last_cs", NG_VJC_TSTATE_PTR_TYPE }, { "last_recv", &ng_parse_uint8_type }, { "last_xmit", &ng_parse_uint8_type }, { "flags", &ng_parse_hint16_type }, #ifndef SL_NO_STATS { "sls_packets", &ng_parse_uint32_type }, { "sls_compressed", &ng_parse_uint32_type }, { "sls_searches", &ng_parse_uint32_type }, { "sls_misses", &ng_parse_uint32_type }, { "sls_uncompressedin", &ng_parse_uint32_type }, { "sls_compressedin", &ng_parse_uint32_type }, { "sls_errorin", &ng_parse_uint32_type }, { "sls_tossed", &ng_parse_uint32_type }, #endif { "tstate", &ng_vjc_cstatearray_type }, { "rstate", &ng_vjc_cstatearray_type }, { NULL }, } }; static const struct ng_parse_type ng_vjc_slcompress_type = { &ng_parse_struct_type, &ng_vjc_slcompress_type_info }; /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_vjc_cmds[] = { { NGM_VJC_COOKIE, NGM_VJC_SET_CONFIG, "setconfig", &ng_vjc_config_type, NULL }, { NGM_VJC_COOKIE, NGM_VJC_GET_CONFIG, "getconfig", NULL, &ng_vjc_config_type, }, { NGM_VJC_COOKIE, NGM_VJC_GET_STATE, "getstate", NULL, &ng_vjc_slcompress_type, }, { NGM_VJC_COOKIE, NGM_VJC_CLR_STATS, "clrstats", NULL, NULL, }, { NGM_VJC_COOKIE, NGM_VJC_RECV_ERROR, "recverror", NULL, NULL, }, { 0 } }; /* Node type descriptor */ static struct ng_type ng_vjc_typestruct = { NG_VERSION, NG_VJC_NODE_TYPE, NULL, ng_vjc_constructor, ng_vjc_rcvmsg, ng_vjc_rmnode, ng_vjc_newhook, NULL, NULL, ng_vjc_rcvdata, - ng_vjc_rcvdata, ng_vjc_disconnect, ng_vjc_cmds }; NETGRAPH_INIT(vjc, &ng_vjc_typestruct); /************************************************************************ NETGRAPH NODE METHODS ************************************************************************/ /* * Create a new node */ static int ng_vjc_constructor(node_p *nodep) { priv_p priv; int error; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); if (priv == NULL) return (ENOMEM); /* Call generic node constructor */ if ((error = ng_make_node_common(&ng_vjc_typestruct, nodep))) { FREE(priv, M_NETGRAPH); return (error); } (*nodep)->private = priv; /* Done */ return (0); } /* * Add a new hook */ static int ng_vjc_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = (priv_p) node->private; hook_p *hookp; /* Get hook */ if (strcmp(name, NG_VJC_HOOK_IP) == 0) hookp = &priv->ip; else if (strcmp(name, NG_VJC_HOOK_VJCOMP) == 0) hookp = &priv->vjcomp; else if (strcmp(name, NG_VJC_HOOK_VJUNCOMP) == 0) hookp = &priv->vjuncomp; else if (strcmp(name, NG_VJC_HOOK_VJIP) == 0) hookp = &priv->vjip; else return (EINVAL); /* See if already connected */ if (*hookp) return (EISCONN); /* OK */ *hookp = hook; return (0); } /* * Receive a control message */ static int ng_vjc_rcvmsg(node_p node, struct ng_mesg *msg, const char *raddr, struct ng_mesg **rptr, hook_p lasthook) { const priv_p priv = (priv_p) node->private; struct ng_mesg *resp = NULL; int error = 0; /* Check type cookie */ switch (msg->header.typecookie) { case NGM_VJC_COOKIE: switch (msg->header.cmd) { case NGM_VJC_SET_CONFIG: { struct ngm_vjc_config *const c = (struct ngm_vjc_config *) msg->data; if (msg->header.arglen != sizeof(*c)) ERROUT(EINVAL); if ((priv->conf.enableComp || priv->conf.enableDecomp) && (c->enableComp || c->enableDecomp)) ERROUT(EALREADY); if (c->enableComp) { if (c->maxChannel > NG_VJC_MAX_CHANNELS - 1 || c->maxChannel < NG_VJC_MIN_CHANNELS - 1) ERROUT(EINVAL); } else c->maxChannel = NG_VJC_MAX_CHANNELS - 1; if (c->enableComp != 0 || c->enableDecomp != 0) { bzero(&priv->slc, sizeof(priv->slc)); sl_compress_init(&priv->slc, c->maxChannel); } priv->conf = *c; break; } case NGM_VJC_GET_CONFIG: { struct ngm_vjc_config *conf; NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); conf = (struct ngm_vjc_config *)resp->data; *conf = priv->conf; break; } case NGM_VJC_GET_STATE: { const struct slcompress *const sl0 = &priv->slc; struct slcompress *sl; u_int16_t index; int i; /* Get response structure */ NG_MKRESPONSE(resp, msg, sizeof(*sl), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); sl = (struct slcompress *)resp->data; *sl = *sl0; /* Replace pointers with integer indicies */ if (sl->last_cs != NULL) { index = sl0->last_cs - sl0->tstate; bzero(&sl->last_cs, sizeof(sl->last_cs)); *((u_int16_t *)&sl->last_cs) = index; } for (i = 0; i < MAX_STATES; i++) { struct cstate *const cs = &sl->tstate[i]; index = sl0->tstate[i].cs_next - sl0->tstate; bzero(&cs->cs_next, sizeof(cs->cs_next)); *((u_int16_t *)&cs->cs_next) = index; } break; } case NGM_VJC_CLR_STATS: priv->slc.sls_packets = 0; priv->slc.sls_compressed = 0; priv->slc.sls_searches = 0; priv->slc.sls_misses = 0; priv->slc.sls_uncompressedin = 0; priv->slc.sls_compressedin = 0; priv->slc.sls_errorin = 0; priv->slc.sls_tossed = 0; break; case NGM_VJC_RECV_ERROR: sl_uncompress_tcp(NULL, 0, TYPE_ERROR, &priv->slc); break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (rptr) *rptr = resp; else if (resp) FREE(resp, M_NETGRAPH); done: FREE(msg, M_NETGRAPH); return (error); } /* * Receive data */ static int ng_vjc_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { const node_p node = hook->node; const priv_p priv = (priv_p) node->private; int error = 0; if (hook == priv->ip) { /* outgoing packet */ u_int type = TYPE_IP; /* Compress packet if enabled and proto is TCP */ if (priv->conf.enableComp) { struct ip *ip; if ((m = ng_vjc_pulluphdrs(m, 0)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } ip = mtod(m, struct ip *); if (ip->ip_p == IPPROTO_TCP) { const int origLen = m->m_len; type = sl_compress_tcp(m, ip, &priv->slc, priv->conf.compressCID); m->m_pkthdr.len += m->m_len - origLen; } } /* Dispatch to the appropriate outgoing hook */ switch (type) { case TYPE_IP: hook = priv->vjip; break; case TYPE_UNCOMPRESSED_TCP: hook = priv->vjuncomp; break; case TYPE_COMPRESSED_TCP: hook = priv->vjcomp; break; default: panic("%s: type=%d", __FUNCTION__, type); } } else if (hook == priv->vjcomp) { /* incoming compressed packet */ int vjlen, need2pullup; struct mbuf *hm; u_char *hdr; u_int hlen; /* Are we decompressing? */ if (!priv->conf.enableDecomp) { NG_FREE_DATA(m, meta); return (ENXIO); } /* Pull up the necessary amount from the mbuf */ need2pullup = MAX_VJHEADER; if (need2pullup > m->m_pkthdr.len) need2pullup = m->m_pkthdr.len; if (m->m_len < need2pullup && (m = m_pullup(m, need2pullup)) == NULL) { priv->slc.sls_errorin++; NG_FREE_META(meta); return (ENOBUFS); } /* Uncompress packet to reconstruct TCP/IP header */ vjlen = sl_uncompress_tcp_core(mtod(m, u_char *), m->m_len, m->m_pkthdr.len, TYPE_COMPRESSED_TCP, &priv->slc, &hdr, &hlen); if (vjlen <= 0) { NG_FREE_DATA(m, meta); return (EINVAL); } m_adj(m, vjlen); /* Copy the reconstructed TCP/IP headers into a new mbuf */ MGETHDR(hm, M_DONTWAIT, MT_DATA); if (hm == NULL) { priv->slc.sls_errorin++; NG_FREE_DATA(m, meta); return (ENOBUFS); } hm->m_len = 0; hm->m_pkthdr.rcvif = NULL; if (hlen > MHLEN) { /* unlikely, but can happen */ MCLGET(hm, M_DONTWAIT); if ((hm->m_flags & M_EXT) == 0) { m_freem(hm); priv->slc.sls_errorin++; NG_FREE_DATA(m, meta); return (ENOBUFS); } } bcopy(hdr, mtod(hm, u_char *), hlen); hm->m_len = hlen; /* Glue TCP/IP headers and rest of packet together */ hm->m_next = m; hm->m_pkthdr.len = hlen + m->m_pkthdr.len; m = hm; hook = priv->ip; } else if (hook == priv->vjuncomp) { /* incoming uncompressed pkt */ u_char *hdr; u_int hlen; /* Are we decompressing? */ if (!priv->conf.enableDecomp) { NG_FREE_DATA(m, meta); return (ENXIO); } /* Pull up IP+TCP headers */ if ((m = ng_vjc_pulluphdrs(m, 1)) == NULL) { NG_FREE_META(meta); return (ENOBUFS); } /* Run packet through uncompressor */ if (sl_uncompress_tcp_core(mtod(m, u_char *), m->m_len, m->m_pkthdr.len, TYPE_UNCOMPRESSED_TCP, &priv->slc, &hdr, &hlen) < 0) { NG_FREE_DATA(m, meta); return (EINVAL); } hook = priv->ip; } else if (hook == priv->vjip) /* incoming regular packet (bypass) */ hook = priv->ip; else panic("%s: unknown hook", __FUNCTION__); /* Send result back out */ NG_SEND_DATA(error, hook, m, meta); return (error); } /* * Shutdown node */ static int ng_vjc_rmnode(node_p node) { const priv_p priv = (priv_p) node->private; node->flags |= NG_INVALID; ng_cutlinks(node); ng_unname(node); bzero(priv, sizeof(*priv)); FREE(priv, M_NETGRAPH); node->private = NULL; ng_unref(node); return (0); } /* * Hook disconnection */ static int ng_vjc_disconnect(hook_p hook) { const node_p node = hook->node; const priv_p priv = node->private; /* Zero out hook pointer */ if (hook == priv->ip) priv->ip = NULL; else if (hook == priv->vjcomp) priv->vjcomp = NULL; else if (hook == priv->vjuncomp) priv->vjuncomp = NULL; else if (hook == priv->vjip) priv->vjip = NULL; else panic("%s: unknown hook", __FUNCTION__); /* Go away if no hooks left */ if (node->numhooks == 0) ng_rmnode(node); return (0); } /************************************************************************ HELPER STUFF ************************************************************************/ /* * Pull up the full IP and TCP headers of a packet. If packet is not * a TCP packet, just pull up the IP header. */ static struct mbuf * ng_vjc_pulluphdrs(struct mbuf *m, int knownTCP) { struct ip *ip; struct tcphdr *tcp; int ihlen, thlen; if (m->m_len < sizeof(*ip) && (m = m_pullup(m, sizeof(*ip))) == NULL) return (NULL); ip = mtod(m, struct ip *); if (!knownTCP && ip->ip_p != IPPROTO_TCP) return (m); ihlen = ip->ip_hl << 2; if (m->m_len < ihlen + sizeof(*tcp)) { if ((m = m_pullup(m, ihlen + sizeof(*tcp))) == NULL) return (NULL); ip = mtod(m, struct ip *); } tcp = (struct tcphdr *)((u_char *)ip + ihlen); thlen = tcp->th_off << 2; if (m->m_len < ihlen + thlen) m = m_pullup(m, ihlen + thlen); return (m); } Index: head/sys/pci/if_mn.c =================================================================== --- head/sys/pci/if_mn.c (revision 69921) +++ head/sys/pci/if_mn.c (revision 69922) @@ -1,1346 +1,1348 @@ /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $Id: if_mn.c,v 1.1 1999/02/01 13:06:40 phk Exp $ * * Driver for Siemens reference design card "Easy321-R1". * * This card contains a FALC54 E1/T1 framer and a MUNICH32X 32-channel HDLC * controller. * * The driver supports E1 mode with up to 31 channels. We send CRC4 but don't * check it coming in. * * The FALC54 and MUNICH32X have far too many registers and weird modes for * comfort, so I have not bothered typing it all into a "fooreg.h" file, * you will (badly!) need the documentation anyway if you want to mess with * this gadget. * * $FreeBSD$ */ /* * Stuff to describe the MUNIC32X and FALC54 chips. */ #define M32_CHAN 32 /* We have 32 channels */ #define M32_TS 32 /* We have 32 timeslots */ #define NG_MN_NODE_TYPE "mn" #include #include #include #include #include #include #include #include #include #include "pci_if.h" #include #include #include #include #include #include #include static int mn_maxlatency = 1000; SYSCTL_INT(_debug, OID_AUTO, mn_maxlatency, CTLFLAG_RW, &mn_maxlatency, 0, "The number of milliseconds a packet is allowed to spend in the output queue. " "If the output queue is longer than this number of milliseconds when the packet " "arrives for output, the packet will be dropped." ); #ifndef NMN /* Most machines don't support more than 4 busmaster PCI slots, if even that many */ #define NMN 4 #endif /* From: PEB 20321 data sheet, p187, table 22 */ struct m32xreg { u_int32_t conf, cmd, stat, imask; u_int32_t fill10, piqba, piql, fill1c; u_int32_t mode1, mode2, ccba, txpoll; u_int32_t tiqba, tiql, riqba, riql; u_int32_t lconf, lccba, fill48, ltran; u_int32_t ltiqba, ltiql, lriqba, lriql; u_int32_t lreg0, lreg1, lreg2, lreg3; u_int32_t lreg4, lreg5, lre6, lstat; u_int32_t gpdir, gpdata, gpod, fill8c; u_int32_t ssccon, sscbr, ssctb, sscrb; u_int32_t ssccse, sscim, fillab, fillac; u_int32_t iomcon1, iomcon2, iomstat, fillbc; u_int32_t iomcit0, iomcit1, iomcir0, iomcir1; u_int32_t iomtmo, iomrmo, filld8, filldc; u_int32_t mbcmd, mbdata1, mbdata2, mbdata3; u_int32_t mbdata4, mbdata5, mbdata6, mbdata7; }; /* From: PEB 2254 data sheet, p80, table 10 */ struct f54wreg { u_int16_t xfifo; u_int8_t cmdr, mode, rah1, rah2, ral1, ral2; u_int8_t ipc, ccr1, ccr3, pre, rtr1, rtr2, rtr3, rtr4; u_int8_t ttr1, ttr2, ttr3, ttr4, imr0, imr1, imr2, imr3; u_int8_t imr4, fill19, fmr0, fmr1, fmr2, loop, xsw, xsp; u_int8_t xc0, xc1, rc0, rc1, xpm0, xpm1, xpm2, tswm; u_int8_t test1, idle, xsa4, xsa5, xsa6, xsa7, xsa8, fmr3; u_int8_t icb1, icb2, icb3, icb4, lim0, lim1, pcd, pcr; u_int8_t lim2, fill39[7]; u_int8_t fill40[8]; u_int8_t fill48[8]; u_int8_t fill50[8]; u_int8_t fill58[8]; u_int8_t dec, fill61, test2, fill63[5]; u_int8_t fill68[8]; u_int8_t xs[16]; }; /* From: PEB 2254 data sheet, p117, table 10 */ struct f54rreg { u_int16_t rfifo; u_int8_t fill2, mode, rah1, rah2, ral1, ral2; u_int8_t ipc, ccr1, ccr3, pre, rtr1, rtr2, rtr3, rtr4; u_int8_t ttr1, ttr2, ttr3, ttr4, imr0, imr1, imr2, imr3; u_int8_t imr4, fill19, fmr0, fmr1, fmr2, loop, xsw, xsp; u_int8_t xc0, xc1, rc0, rc1, xpm0, xpm1, xpm2, tswm; u_int8_t test, idle, xsa4, xsa5, xsa6, xsa7, xsa8, fmr13; u_int8_t icb1, icb2, icb3, icb4, lim0, lim1, pcd, pcr; u_int8_t lim2, fill39[7]; u_int8_t fill40[8]; u_int8_t fill48[4], frs0, frs1, rsw, rsp; u_int16_t fec, cvc, cec1, ebc; u_int16_t cec2, cec3; u_int8_t rsa4, rsa5, rsa6, rsa7; u_int8_t rsa8, rsa6s, tsr0, tsr1, sis, rsis; u_int16_t rbc; u_int8_t isr0, isr1, isr2, isr3, fill6c, fill6d, gis, vstr; u_int8_t rs[16]; }; /* Transmit & receive descriptors */ struct trxd { u_int32_t flags; vm_offset_t next; vm_offset_t data; u_int32_t status; /* only used for receive */ struct mbuf *m; /* software use only */ struct trxd *vnext; /* software use only */ }; /* Channel specification */ struct cspec { u_int32_t flags; vm_offset_t rdesc; vm_offset_t tdesc; u_int32_t itbs; }; struct m32_mem { vm_offset_t csa; u_int32_t ccb; u_int32_t reserve1[2]; u_int32_t ts[M32_TS]; struct cspec cs[M32_CHAN]; vm_offset_t crxd[M32_CHAN]; vm_offset_t ctxd[M32_CHAN]; }; struct softc; struct sockaddr; struct rtentry; static int mn_probe (device_t self); static int mn_attach (device_t self); static void mn_create_channel(struct softc *sc, int chan); static int mn_reset(struct softc *sc); static struct trxd * mn_alloc_desc(void); static void mn_free_desc(struct trxd *dp); static void mn_intr(void *xsc); static u_int32_t mn_parse_ts(const char *s, int *nbit); #ifdef notyet static void m32_dump(struct softc *sc); static void f54_dump(struct softc *sc); static void mn_fmt_ts(char *p, u_int32_t ts); #endif /* notyet */ static ng_constructor_t ngmn_constructor; static ng_rcvmsg_t ngmn_rcvmsg; static ng_shutdown_t ngmn_shutdown; static ng_newhook_t ngmn_newhook; static ng_connect_t ngmn_connect; static ng_rcvdata_t ngmn_rcvdata; static ng_disconnect_t ngmn_disconnect; static struct ng_type mntypestruct = { NG_VERSION, NG_MN_NODE_TYPE, NULL, ngmn_constructor, ngmn_rcvmsg, ngmn_shutdown, ngmn_newhook, NULL, ngmn_connect, ngmn_rcvdata, ngmn_rcvdata, ngmn_disconnect, NULL }; static MALLOC_DEFINE(M_MN, "mn", "Mx driver related"); #define NIQB 64 struct schan { enum {DOWN, UP} state; struct softc *sc; int chan; u_int32_t ts; char name[8]; struct trxd *r1, *rl; struct trxd *x1, *xl; hook_p hook; time_t last_recv; time_t last_rxerr; time_t last_xmit; u_long rx_error; u_long short_error; u_long crc_error; u_long dribble_error; u_long long_error; u_long abort_error; u_long overflow_error; int last_error; int prev_error; u_long tx_pending; u_long tx_limit; }; struct softc { int unit; device_t dev; struct resource *irq; void *intrhand; void *m0v, *m1v; vm_offset_t m0p, m1p; struct m32xreg *m32x; struct f54wreg *f54w; struct f54rreg *f54r; struct m32_mem m32_mem; u_int32_t tiqb[NIQB]; u_int32_t riqb[NIQB]; u_int32_t piqb[NIQB]; u_int32_t ltiqb[NIQB]; u_int32_t lriqb[NIQB]; char name[8]; u_int32_t falc_irq, falc_state, framer_state; struct schan *ch[M32_CHAN]; char nodename[NG_NODELEN + 1]; node_p node; u_long cnt_fec; u_long cnt_cvc; u_long cnt_cec1; u_long cnt_ebc; u_long cnt_cec2; u_long cnt_cec3; u_long cnt_rbc; }; static int ngmn_constructor(node_p *nodep) { return (EINVAL); } static int ngmn_shutdown(node_p nodep) { return (EINVAL); } static int ngmn_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, struct ng_mesg **resp, hook_p lasthook) { struct softc *sc; struct schan *sch; char *arg; int pos, i; sc = node->private; if (msg->header.typecookie != NGM_GENERIC_COOKIE || msg->header.cmd != NGM_TEXT_STATUS) { if (resp) *resp = NULL; FREE(msg, M_NETGRAPH); return (EINVAL); } NG_MKRESPONSE(*resp, msg, sizeof(struct ng_mesg) + NG_TEXTRESPONSE, M_NOWAIT); if (*resp == NULL) { FREE(msg, M_NETGRAPH); return (ENOMEM); } arg = (char *)(*resp)->data; pos = 0; pos += sprintf(pos + arg,"Framer status %b;\n", sc->framer_state, "\20" "\40LOS\37AIS\36LFA\35RRA" "\34AUXP\33NMF\32LMFA\31frs0.0" "\30frs1.7\27TS16RA\26TS16LOS\25TS16AIS" "\24TS16LFA\23frs1.2\22XLS\21XLO" "\20RS1\17rsw.6\16RRA\15RY0" "\14RY1\13RY2\12RY3\11RY4" "\10SI1\7SI2\6rsp.5\5rsp.4" "\4rsp.3\3RSIF\2RS13\1RS15"); pos += sprintf(pos + arg," Framing errors: %lu", sc->cnt_fec); pos += sprintf(pos + arg," Code Violations: %lu\n", sc->cnt_cvc); pos += sprintf(pos + arg," Falc State %b;\n", sc->falc_state, "\20" "\40LOS\37AIS\36LFA\35RRA" "\34AUXP\33NMF\32LMFA\31frs0.0" "\30frs1.7\27TS16RA\26TS16LOS\25TS16AIS" "\24TS16LFA\23frs1.2\22XLS\21XLO" "\20RS1\17rsw.6\16RRA\15RY0" "\14RY1\13RY2\12RY3\11RY4" "\10SI1\7SI2\6rsp.5\5rsp.4" "\4rsp.3\3RSIF\2RS13\1RS15"); pos += sprintf(pos + arg, " Falc IRQ %b\n", sc->falc_irq, "\20" "\40RME\37RFS\36T8MS\35RMB\34CASC\33CRC4\32SA6SC\31RPF" "\30b27\27RDO\26ALLS\25XDU\24XMB\23b22\22XLSC\21XPR" "\20FAR\17LFA\16MFAR\15T400MS\14AIS\13LOS\12RAR\11RA" "\10ES\7SEC\6LMFA16\5AIS16\4RA16\3API\2SLN\1SLP"); for (i = 0; i < M32_CHAN; i++) { if (!sc->ch[i]) continue; sch = sc->ch[i]; pos += sprintf(arg + pos, " Chan %d <%s> ", i, sch->hook->name); pos += sprintf(arg + pos, " Last Rx: "); if (sch->last_recv) pos += sprintf(arg + pos, "%lu s", time_second - sch->last_recv); else pos += sprintf(arg + pos, "never"); pos += sprintf(arg + pos, ", last RxErr: "); if (sch->last_rxerr) pos += sprintf(arg + pos, "%lu s", time_second - sch->last_rxerr); else pos += sprintf(arg + pos, "never"); pos += sprintf(arg + pos, ", last Tx: "); if (sch->last_xmit) pos += sprintf(arg + pos, "%lu s\n", time_second - sch->last_xmit); else pos += sprintf(arg + pos, "never\n"); pos += sprintf(arg + pos, " RX error(s) %lu", sch->rx_error); pos += sprintf(arg + pos, " Short: %lu", sch->short_error); pos += sprintf(arg + pos, " CRC: %lu", sch->crc_error); pos += sprintf(arg + pos, " Mod8: %lu", sch->dribble_error); pos += sprintf(arg + pos, " Long: %lu", sch->long_error); pos += sprintf(arg + pos, " Abort: %lu", sch->abort_error); pos += sprintf(arg + pos, " Overflow: %lu\n", sch->overflow_error); pos += sprintf(arg + pos, " Last error: %b Prev error: %b\n", sch->last_error, "\20\7SHORT\5CRC\4MOD8\3LONG\2ABORT\1OVERRUN", sch->prev_error, "\20\7SHORT\5CRC\4MOD8\3LONG\2ABORT\1OVERRUN"); pos += sprintf(arg + pos, " Xmit bytes pending %ld\n", sch->tx_pending); } (*resp)->header.arglen = pos + 1; FREE(msg, M_NETGRAPH); return (0); } static int ngmn_newhook(node_p node, hook_p hook, const char *name) { u_int32_t ts, chan; struct softc *sc; int nbit; sc = node->private; if (name[0] != 't' || name[1] != 's') return (EINVAL); ts = mn_parse_ts(name + 2, &nbit); if (ts == 0) return (EINVAL); chan = ffs(ts) - 1; if (!sc->ch[chan]) mn_create_channel(sc, chan); else if (sc->ch[chan]->state == UP) return (EBUSY); sc->ch[chan]->ts = ts; sc->ch[chan]->hook = hook; sc->ch[chan]->tx_limit = nbit * 8; hook->private = sc->ch[chan]; return(0); } static struct trxd *mn_desc_free; static struct trxd * mn_alloc_desc(void) { struct trxd *dp; dp = mn_desc_free; if (dp) mn_desc_free = dp->vnext; else dp = (struct trxd *)malloc(sizeof *dp, M_MN, M_NOWAIT); return (dp); } static void mn_free_desc(struct trxd *dp) { dp->vnext = mn_desc_free; mn_desc_free = dp; } static u_int32_t mn_parse_ts(const char *s, int *nbit) { unsigned r; int i, j; char *p; r = 0; j = 0; *nbit = 0; while(*s) { i = strtol(s, &p, 0); if (i < 1 || i > 31) return (0); while (j && j < i) { r |= 1 << j++; (*nbit)++; } j = 0; r |= 1 << i; (*nbit)++; if (*p == ',') { s = p + 1; continue; } else if (*p == '-') { j = i; s = p + 1; continue; } else if (!*p) { break; } else { return (0); } } return (r); } #ifdef notyet static void mn_fmt_ts(char *p, u_int32_t ts) { char *s; int j; s = ""; ts &= 0xfffffffe; for (j = 1; j < 32; j++) { if (!(ts & (1 << j))) continue; sprintf(p, "%s%d", s, j); p += strlen(p); s = ","; if (!(ts & (1 << (j+1)))) continue; for (; j < 32; j++) if (!(ts & (1 << (j+1)))) break; sprintf(p, "-%d", j); p += strlen(p); s = ","; } } #endif /* notyet */ /* * OUTPUT */ static int ngmn_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, - struct mbuf **ret_m, meta_p *ret_meta) + struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) { struct mbuf *m2; struct trxd *dp, *dp2; struct schan *sch; struct softc *sc; int chan, pitch, len; sch = hook->private; sc = sch->sc; chan = sch->chan; if (sch->state != UP) { NG_FREE_DATA(m, meta); return (0); } if (sch->tx_pending + m->m_pkthdr.len > sch->tx_limit * mn_maxlatency) { NG_FREE_DATA(m, meta); return (0); } NG_FREE_META(meta); pitch = 0; m2 = m; dp2 = sc->ch[chan]->xl; len = m->m_pkthdr.len; while (len) { dp = mn_alloc_desc(); if (!dp) { pitch++; m_freem(m); sc->ch[chan]->xl = dp2; dp = dp2->vnext; while (dp) { dp2 = dp->vnext; mn_free_desc(dp); dp = dp2; } sc->ch[chan]->xl->vnext = 0; break; } dp->data = vtophys(m2->m_data); dp->flags = m2->m_len << 16; dp->flags += 1; len -= m2->m_len; dp->next = vtophys(dp); dp->vnext = 0; sc->ch[chan]->xl->next = vtophys(dp); sc->ch[chan]->xl->vnext = dp; sc->ch[chan]->xl = dp; if (!len) { dp->m = m; dp->flags |= 0xc0000000; dp2->flags &= ~0x40000000; } else { dp->m = 0; m2 = m2->m_next; } } if (pitch) printf("%s%d: Short on mem, pitched %d packets\n", sc->name, chan, pitch); else { #if 0 printf("%d = %d + %d (%p)\n", sch->tx_pending + m->m_pkthdr.len, sch->tx_pending , m->m_pkthdr.len, m); #endif sch->tx_pending += m->m_pkthdr.len; } return (0); } /* * OPEN */ static int ngmn_connect(hook_p hook) { int i, nts, chan; struct trxd *dp, *dp2; struct mbuf *m; struct softc *sc; struct schan *sch; u_int32_t u; sch = hook->private; chan = sch->chan; sc = sch->sc; if (sch->state == UP) return (0); sch->state = UP; /* Count and configure the timeslots for this channel */ for (nts = i = 0; i < 32; i++) if (sch->ts & (1 << i)) { sc->m32_mem.ts[i] = 0x00ff00ff | (chan << 24) | (chan << 8); nts++; } /* Init the receiver & xmitter to HDLC */ sc->m32_mem.cs[chan].flags = 0x80e90006; /* Allocate two buffers per timeslot */ sc->m32_mem.cs[chan].itbs = nts * 2; /* Setup a transmit chain with one descriptor */ /* XXX: we actually send a 1 byte packet */ dp = mn_alloc_desc(); MGETHDR(m, M_WAIT, MT_DATA); m->m_pkthdr.len = 0; dp->m = m; dp->flags = 0xc0000000 + (1 << 16); dp->next = vtophys(dp); dp->vnext = 0; dp->data = vtophys(sc->name); sc->m32_mem.cs[chan].tdesc = vtophys(dp); sc->ch[chan]->x1 = dp; sc->ch[chan]->xl = dp; /* Setup a receive chain with 5 + NTS descriptors */ dp = mn_alloc_desc(); MGETHDR(m, M_WAIT, MT_DATA); MCLGET(m, M_WAIT); dp->m = m; dp->data = vtophys(m->m_data); dp->flags = 0x40000000; dp->flags += 1600 << 16; dp->next = vtophys(dp); dp->vnext = 0; sc->ch[chan]->rl = dp; for (i = 0; i < (nts + 10); i++) { dp2 = dp; dp = mn_alloc_desc(); MGETHDR(m, M_WAIT, MT_DATA); MCLGET(m, M_WAIT); dp->m = m; dp->data = vtophys(m->m_data); dp->flags = 0x00000000; dp->flags += 1600 << 16; dp->next = vtophys(dp2); dp->vnext = dp2; } sc->m32_mem.cs[chan].rdesc = vtophys(dp); sc->ch[chan]->r1 = dp; /* Initialize this channel */ sc->m32_mem.ccb = 0x00008000 + (chan << 8); sc->m32x->cmd = 0x1; DELAY(1000); u = sc->m32x->stat; if (!(u & 1)) printf("%s: init chan %d stat %08x\n", sc->name, chan, u); sc->m32x->stat = 1; + /* probably not at splnet, force outward queueing */ + hook->peer->flags |= HK_QUEUE; return (0); } /* * CLOSE */ static int ngmn_disconnect(hook_p hook) { int chan, i; struct softc *sc; struct schan *sch; struct trxd *dp, *dp2; u_int32_t u; sch = hook->private; chan = sch->chan; sc = sch->sc; if (sch->state == DOWN) return (0); sch->state = DOWN; /* Set receiver & transmitter off */ sc->m32_mem.cs[chan].flags = 0x80920006; sc->m32_mem.cs[chan].itbs = 0; /* free the timeslots */ for (i = 0; i < 32; i++) if (sc->ch[chan]->ts & (1 << i)) sc->m32_mem.ts[i] = 0x20002000; /* Initialize this channel */ sc->m32_mem.ccb = 0x00008000 + (chan << 8); sc->m32x->cmd = 0x1; DELAY(30); u = sc->m32x->stat; if (!(u & 1)) printf("%s: zap chan %d stat %08x\n", sc->name, chan, u); sc->m32x->stat = 1; /* Free all receive descriptors and mbufs */ for (dp = sc->ch[chan]->r1; dp ; dp = dp2) { if (dp->m) m_freem(dp->m); sc->ch[chan]->r1 = dp2 = dp->vnext; mn_free_desc(dp); } /* Free all transmit descriptors and mbufs */ for (dp = sc->ch[chan]->x1; dp ; dp = dp2) { if (dp->m) { sc->ch[chan]->tx_pending -= dp->m->m_pkthdr.len; m_freem(dp->m); } sc->ch[chan]->x1 = dp2 = dp->vnext; mn_free_desc(dp); } return(0); } /* * Create a new channel. */ static void mn_create_channel(struct softc *sc, int chan) { struct schan *sch; sch = sc->ch[chan] = (struct schan *)malloc(sizeof *sc->ch[chan], M_MN, M_WAITOK | M_ZERO); sch->sc = sc; sch->state = DOWN; sch->chan = chan; sprintf(sch->name, "%s%d", sc->name, chan); return; } #ifdef notyet /* * Dump Munich32x state */ static void m32_dump(struct softc *sc) { u_int32_t *tp4; int i, j; printf("mn%d: MUNICH32X dump\n", sc->unit); tp4 = (u_int32_t *)sc->m0v; for(j = 0; j < 64; j += 8) { printf("%02x", j * sizeof *tp4); for(i = 0; i < 8; i++) printf(" %08x", tp4[i+j]); printf("\n"); } for(j = 0; j < M32_CHAN; j++) { if (!sc->ch[j]) continue; printf("CH%d: state %d ts %08x", j, sc->ch[j]->state, sc->ch[j]->ts); printf(" %08x %08x %08x %08x %08x %08x\n", sc->m32_mem.cs[j].flags, sc->m32_mem.cs[j].rdesc, sc->m32_mem.cs[j].tdesc, sc->m32_mem.cs[j].itbs, sc->m32_mem.crxd[j], sc->m32_mem.ctxd[j] ); } } /* * Dump Falch54 state */ static void f54_dump(struct softc *sc) { u_int8_t *tp1; int i, j; printf("%s: FALC54 dump\n", sc->name); tp1 = (u_int8_t *)sc->m1v; for(j = 0; j < 128; j += 16) { printf("%s: %02x |", sc->name, j * sizeof *tp1); for(i = 0; i < 16; i++) printf(" %02x", tp1[i+j]); printf("\n"); } } #endif /* notyet */ /* * Init Munich32x */ static void m32_init(struct softc *sc) { sc->m32x->conf = 0x00000000; sc->m32x->mode1 = 0x81048000 + 1600; /* XXX: temp */ #if 1 sc->m32x->mode2 = 0x00000081; sc->m32x->txpoll = 0xffffffff; #else sc->m32x->mode2 = 0x00000101; #endif sc->m32x->lconf = 0x6060009B; sc->m32x->imask = 0x00000000; } /* * Init the Falc54 */ static void f54_init(struct softc *sc) { sc->f54w->ipc = 0x07; sc->f54w->xpm0 = 0xbd; sc->f54w->xpm1 = 0x03; sc->f54w->xpm2 = 0x00; sc->f54w->imr0 = 0x18; /* RMB, CASC */ sc->f54w->imr1 = 0x08; /* XMB */ sc->f54w->imr2 = 0x00; sc->f54w->imr3 = 0x38; /* LMFA16, AIS16, RA16 */ sc->f54w->imr4 = 0x00; sc->f54w->fmr0 = 0xf0; /* X: HDB3, R: HDB3 */ sc->f54w->fmr1 = 0x0e; /* Send CRC4, 2Mbit, ECM */ sc->f54w->fmr2 = 0x03; /* Auto Rem-Alarm, Auto resync */ sc->f54w->lim1 = 0xb0; /* XCLK=8kHz, .62V threshold */ sc->f54w->pcd = 0x0a; sc->f54w->pcr = 0x15; sc->f54w->xsw = 0x9f; /* fmr4 */ sc->f54w->xsp = 0x1c; /* fmr5 */ sc->f54w->xc0 = 0x07; sc->f54w->xc1 = 0x3d; sc->f54w->rc0 = 0x05; sc->f54w->rc1 = 0x00; sc->f54w->cmdr = 0x51; } static int mn_reset(struct softc *sc) { u_int32_t u; int i; sc->m32x->ccba = vtophys(&sc->m32_mem.csa); sc->m32_mem.csa = vtophys(&sc->m32_mem.ccb); bzero(sc->tiqb, sizeof sc->tiqb); sc->m32x->tiqba = vtophys(&sc->tiqb); sc->m32x->tiql = NIQB / 16 - 1; bzero(sc->riqb, sizeof sc->riqb); sc->m32x->riqba = vtophys(&sc->riqb); sc->m32x->riql = NIQB / 16 - 1; bzero(sc->ltiqb, sizeof sc->ltiqb); sc->m32x->ltiqba = vtophys(&sc->ltiqb); sc->m32x->ltiql = NIQB / 16 - 1; bzero(sc->lriqb, sizeof sc->lriqb); sc->m32x->lriqba = vtophys(&sc->lriqb); sc->m32x->lriql = NIQB / 16 - 1; bzero(sc->piqb, sizeof sc->piqb); sc->m32x->piqba = vtophys(&sc->piqb); sc->m32x->piql = NIQB / 16 - 1; m32_init(sc); f54_init(sc); u = sc->m32x->stat; sc->m32x->stat = u; sc->m32_mem.ccb = 0x4; sc->m32x->cmd = 0x1; DELAY(1000); u = sc->m32x->stat; sc->m32x->stat = u; /* set all timeslots to known state */ for (i = 0; i < 32; i++) sc->m32_mem.ts[i] = 0x20002000; if (!(u & 1)) { printf( "mn%d: WARNING: Controller failed the PCI bus-master test.\n" "mn%d: WARNING: Use a PCI slot which can support bus-master cards.\n", sc->unit, sc->unit); return (0); } return (1); } /* * FALC54 interrupt handling */ static void f54_intr(struct softc *sc) { unsigned g, u, s; g = sc->f54r->gis; u = sc->f54r->isr0 << 24; u |= sc->f54r->isr1 << 16; u |= sc->f54r->isr2 << 8; u |= sc->f54r->isr3; sc->falc_irq = u; /* don't chat about the 1 sec heart beat */ if (u & ~0x40) { #if 0 printf("%s*: FALC54 IRQ GIS:%02x %b\n", sc->name, g, u, "\20" "\40RME\37RFS\36T8MS\35RMB\34CASC\33CRC4\32SA6SC\31RPF" "\30b27\27RDO\26ALLS\25XDU\24XMB\23b22\22XLSC\21XPR" "\20FAR\17LFA\16MFAR\15T400MS\14AIS\13LOS\12RAR\11RA" "\10ES\7SEC\6LMFA16\5AIS16\4RA16\3API\2SLN\1SLP"); #endif s = sc->f54r->frs0 << 24; s |= sc->f54r->frs1 << 16; s |= sc->f54r->rsw << 8; s |= sc->f54r->rsp; sc->falc_state = s; s &= ~0x01844038; /* undefined or static bits */ s &= ~0x00009fc7; /* bits we don't care about */ s &= ~0x00780000; /* XXX: TS16 related */ s &= ~0x06000000; /* XXX: Multiframe related */ #if 0 printf("%s*: FALC54 Status %b\n", sc->name, s, "\20" "\40LOS\37AIS\36LFA\35RRA\34AUXP\33NMF\32LMFA\31frs0.0" "\30frs1.7\27TS16RA\26TS16LOS\25TS16AIS\24TS16LFA\23frs1.2\22XLS\21XLO" "\20RS1\17rsw.6\16RRA\15RY0\14RY1\13RY2\12RY3\11RY4" "\10SI1\7SI2\6rsp.5\5rsp.4\4rsp.3\3RSIF\2RS13\1RS15"); #endif if (s != sc->framer_state) { #if 0 for (i = 0; i < M32_CHAN; i++) { if (!sc->ch[i]) continue; sp = &sc->ch[i]->ifsppp; if (!(sp->pp_if.if_flags & IFF_UP)) continue; if (s) timeout((timeout_t *)sp->pp_down, sp, 1 * hz); else timeout((timeout_t *)sp->pp_up, sp, 1 * hz); } #endif sc->framer_state = s; } } /* Once per second check error counters */ /* XXX: not clear if this is actually ok */ if (!(u & 0x40)) return; sc->cnt_fec += sc->f54r->fec; sc->cnt_cvc += sc->f54r->cvc; sc->cnt_cec1 += sc->f54r->cec1; sc->cnt_ebc += sc->f54r->ebc; sc->cnt_cec2 += sc->f54r->cec2; sc->cnt_cec3 += sc->f54r->cec3; sc->cnt_rbc += sc->f54r->rbc; } /* * Transmit interrupt for one channel */ static void mn_tx_intr(struct softc *sc, u_int32_t vector) { u_int32_t chan; struct trxd *dp; struct mbuf *m; chan = vector & 0x1f; if (!sc->ch[chan]) return; if (sc->ch[chan]->state != UP) { printf("%s: tx_intr when not UP\n", sc->name); return; } for (;;) { dp = sc->ch[chan]->x1; if (vtophys(dp) == sc->m32_mem.ctxd[chan]) return; m = dp->m; if (m) { #if 0 printf("%d = %d - %d (%p)\n", sc->ch[chan]->tx_pending - m->m_pkthdr.len, sc->ch[chan]->tx_pending , m->m_pkthdr.len, m); #endif sc->ch[chan]->tx_pending -= m->m_pkthdr.len; m_freem(m); } sc->ch[chan]->last_xmit = time_second; sc->ch[chan]->x1 = dp->vnext; mn_free_desc(dp); } } /* * Receive interrupt for one channel */ static void mn_rx_intr(struct softc *sc, u_int32_t vector) { u_int32_t chan, err; struct trxd *dp; struct mbuf *m; struct schan *sch; chan = vector & 0x1f; if (!sc->ch[chan]) return; sch = sc->ch[chan]; if (sch->state != UP) { printf("%s: rx_intr when not UP\n", sc->name); return; } vector &= ~0x1f; if (vector == 0x30000b00) sch->rx_error++; for (;;) { dp = sch->r1; if (vtophys(dp) == sc->m32_mem.crxd[chan]) return; m = dp->m; dp->m = 0; m->m_pkthdr.len = m->m_len = (dp->status >> 16) & 0x1fff; err = (dp->status >> 8) & 0xff; if (!err) { - ng_queue_data(sch->hook, m, NULL); + int error; + NG_SEND_DATA_ONLY(error, sch->hook, m); sch->last_recv = time_second; - m = 0; /* we could be down by now... */ if (sch->state != UP) return; } else if (err & 0x40) { sch->short_error++; } else if (err & 0x10) { sch->crc_error++; } else if (err & 0x08) { sch->dribble_error++; } else if (err & 0x04) { sch->long_error++; } else if (err & 0x02) { sch->abort_error++; } else if (err & 0x01) { sch->overflow_error++; } if (err) { sch->last_rxerr = time_second; sch->prev_error = sch->last_error; sch->last_error = err; } sc->ch[chan]->r1 = dp->vnext; /* Replenish desc + mbuf supplies */ if (!m) { MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { mn_free_desc(dp); return; } MCLGET(m, M_DONTWAIT); if((m->m_flags & M_EXT) == 0) { mn_free_desc(dp); return; } } dp->m = m; dp->data = vtophys(m->m_data); dp->flags = 0x40000000; dp->flags += 1600 << 16; dp->next = vtophys(dp); dp->vnext = 0; sc->ch[chan]->rl->next = vtophys(dp); sc->ch[chan]->rl->vnext = dp; sc->ch[chan]->rl->flags &= ~0x40000000; sc->ch[chan]->rl = dp; } } /* * Interupt handler */ static void mn_intr(void *xsc) { struct softc *sc; u_int32_t stat, lstat, u; int i, j; sc = xsc; stat = sc->m32x->stat; lstat = sc->m32x->lstat; #if 0 if (!stat && !(lstat & 2)) return; #endif if (stat & ~0xc200) { printf("%s: I stat=%08x lstat=%08x\n", sc->name, stat, lstat); } if ((stat & 0x200) || (lstat & 2)) f54_intr(sc); for (j = i = 0; i < 64; i ++) { u = sc->riqb[i]; if (u) { sc->riqb[i] = 0; mn_rx_intr(sc, u); if ((u & ~0x1f) == 0x30000800 || (u & ~0x1f) == 0x30000b00) continue; u &= ~0x30000400; /* bits we don't care about */ if ((u & ~0x1f) == 0x00000900) continue; if (!(u & ~0x1f)) continue; if (!j) printf("%s*: RIQB:", sc->name); printf(" [%d]=%08x", i, u); j++; } } if (j) printf("\n"); for (j = i = 0; i < 64; i ++) { u = sc->tiqb[i]; if (u) { sc->tiqb[i] = 0; mn_tx_intr(sc, u); if ((u & ~0x1f) == 0x20000800) continue; u &= ~0x20000000; /* bits we don't care about */ if (!u) continue; if (!j) printf("%s*: TIQB:", sc->name); printf(" [%d]=%08x", i, u); j++; } } if (j) printf("\n"); sc->m32x->stat = stat; } static void mn_timeout(void *xsc) { static int round = 0; struct softc *sc; mn_intr(xsc); sc = xsc; timeout(mn_timeout, xsc, 10 * hz); round++; if (round == 2) { sc->m32_mem.ccb = 0x00008004; sc->m32x->cmd = 0x1; } else if (round > 2) { printf("%s: timeout\n", sc->name); } } /* * PCI initialization stuff */ static int mn_probe (device_t self) { u_int id = pci_get_devid(self); if (sizeof (struct m32xreg) != 256) { printf("MN: sizeof(struct m32xreg) = %d, should have been 256\n", sizeof (struct m32xreg)); return (ENXIO); } if (sizeof (struct f54rreg) != 128) { printf("MN: sizeof(struct f54rreg) = %d, should have been 128\n", sizeof (struct f54rreg)); return (ENXIO); } if (sizeof (struct f54wreg) != 128) { printf("MN: sizeof(struct f54wreg) = %d, should have been 128\n", sizeof (struct f54wreg)); return (ENXIO); } if (id != 0x2101110a) return (ENXIO); device_set_desc_copy(self, "Munich32X E1/T1 HDLC Controller"); return (0); } static int mn_attach (device_t self) { struct softc *sc; u_int32_t u; u_int32_t ver; static int once; int rid, error; struct resource *res; if (!once) { if (ng_newtype(&mntypestruct)) printf("ng_newtype failed\n"); once++; } sc = (struct softc *)malloc(sizeof *sc, M_MN, M_WAITOK | M_ZERO); device_set_softc(self, sc); sc->dev = self; sc->unit = device_get_unit(self); sprintf(sc->name, "mn%d", sc->unit); rid = PCIR_MAPS; res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid, 0, ~0, 1, RF_ACTIVE); if (res == NULL) { device_printf(self, "Could not map memory\n"); return ENXIO; } sc->m0v = rman_get_virtual(res); sc->m0p = rman_get_start(res); rid = PCIR_MAPS + 4; res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid, 0, ~0, 1, RF_ACTIVE); if (res == NULL) { device_printf(self, "Could not map memory\n"); return ENXIO; } sc->m1v = rman_get_virtual(res); sc->m1p = rman_get_start(res); /* Allocate interrupt */ rid = 0; sc->irq = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (sc->irq == NULL) { printf("couldn't map interrupt\n"); return(ENXIO); } error = bus_setup_intr(self, sc->irq, INTR_TYPE_NET, mn_intr, sc, &sc->intrhand); if (error) { printf("couldn't set up irq\n"); return(ENXIO); } u = pci_read_config(self, PCIR_COMMAND, 1); printf("%x\n", u); pci_write_config(self, PCIR_COMMAND, u | PCIM_CMD_PERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN, 1); #if 0 pci_write_config(self, PCIR_COMMAND, 0x02800046, 4); #endif u = pci_read_config(self, PCIR_COMMAND, 1); printf("%x\n", u); ver = pci_read_config(self, PCI_CLASS_REG, 4); sc->m32x = (struct m32xreg *) sc->m0v; sc->f54w = (struct f54wreg *) sc->m1v; sc->f54r = (struct f54rreg *) sc->m1v; /* We must reset before poking at FALC54 registers */ u = mn_reset(sc); if (!u) return (0); printf("mn%d: Munich32X", sc->unit); switch (ver & 0xff) { case 0x13: printf(" Rev 2.2"); break; default: printf(" Rev 0x%x\n", ver & 0xff); } printf(", Falc54"); switch (sc->f54r->vstr) { case 0: printf(" Rev < 1.3\n"); break; case 1: printf(" Rev 1.3\n"); break; case 2: printf(" Rev 1.4\n"); break; case 0x10: printf("-LH Rev 1.1\n"); break; case 0x13: printf("-LH Rev 1.3\n"); break; default: printf(" Rev 0x%x\n", sc->f54r->vstr); } if (ng_make_node_common(&mntypestruct, &sc->node) != 0) { printf("ng_make_node_common failed\n"); return (0); } sc->node->private = sc; sprintf(sc->nodename, "%s%d", NG_MN_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { ng_rmnode(sc->node); ng_unref(sc->node); return (0); } return (0); } static device_method_t mn_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mn_probe), DEVMETHOD(device_attach, mn_attach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), {0, 0} }; static driver_t mn_driver = { "mn", mn_methods, 0 }; static devclass_t mn_devclass; DRIVER_MODULE(mn, pci, mn_driver, mn_devclass, 0, 0);