README 444 540 24 13305 4535335470 5014 README: changes -- pst 7/19/89 -- test release recently tested under: Ultrix 3.0 and 4.3tahoe note: the man pages have been updated; please read them for more info. [1] This version of the driver has had changes made to data structures shared between acpconfig and the kernel resident software. It is imperative that installation of this software is done in sequence. (a) move the driver files to their proper locations: cp if_dda* if_x29* if_pi* /sys/vaxif (b) compile acpconfig and generate the new kernel (see note [2]) (c) install the new acpconfig in /etc or wherever finer system management tools belong and install the new kernel. The new version of acpconfig has a "generation" date which is displayed if you simply type "acpconfig." The date on the acpconfig shipped with this driver should be 19-Jul-1989. [2] The driver is no longer auto-configuring, as there are just too many different systems available now. You must tell it what type of system you are running. This is done by adding the appropriate "options" line to your kernel configuration file in the /sys/conf directory. For example: options ACC_BSD=43 for 4.3bsd or 4.3-tahoe system options ACC_BSD=42 for 4.2bsd options ACC_ULTRIX=30 for Ultrix 3.0 options ACC_ULTRIX=22 for Ultrix 2.2 options ACC_ULTRIX=20 for Ultrix 2.0 options ACC_ULTRIX=12 for Ultrix 1.2 options ACC_VMS=4n for VAX/VMS 4.n (Multinet or WIN/TCP) options ACC_VMS=5n for VAX/VMS 5.n (Multinet or WIN/TCP) In addition to the operating system definitons, if you are using either the X.29 option or the programmers interface option, you may enable these options by adding options statements. options DDA_RAWOPT enables the raw "programmer's interface" options DDA_PADOPT enables the X.29 pad/host option Debugging code is normally unused in a customer environment, but some sites may find it useful when using the programmer's interface or when working on site dependant modifications to the driver. If you wish to enable the debugging code, add the line: options DDADEBUG [3] The debug facility for the driver has been completely rewritten. Debug messages are now controlled via the acpconfig "-c" command and may be selectively enabled or disabled. Messages 0 - 127 are error messages. By default, they are on. To toggle on or off the display of these messages, simply type acpconfig -c <msgnum>. The following error messages have been added for the X29 module: [96] dda%d:(x29) xxstart: unit offline [97] dda%d:(x29) xxstart: could not get mbuf [98] dda%d:(x29) x29_supr: unexpected message type [100] dda%d:(x29) Bad decode, call REJECTED VC 0x%x [101] dda%d:(x29) Call cleared LCN %d (%x %x) [102] dda%d:(x29) X25 RESET on LCN %d (%x %x) [104] dda%d:(x29) supervisor error (%x %x %x %x) [105] dda%d:(x29) x29_dhandle: null mbuf [106] dda%d:(x29) couldn't get mbuf for QBIT message [107] dda%d:(x29) x29_supr: answer: line was -1, VC 0x%x [107] dda%d:(x29) x29_supr: ring: line was -1, VC 0x%x [107] dda%d:(x29) x29_supr: break: line was -1, VC 0x%x [107] dda%d:(x29) xx_tp_hangup: line was -1 [108] dda%d:(x29) x29_supr: unexpected answer on LCN %d Messages 128 - 255 are debug assistance messages. They are not compiled in unless DDADEBUG is defined. If the code has been compiled in, they may be enabled (they are OFF by default) by using acpconfig. The current manual page for dda(4) has a list of debug messages on the last page. Add 128 to the number in the square bracket and use acpconfig -c to toggle the printing of the message. Example: [0] dda%d: ddainit() Type "acpconfig -c 128" to toggle this message. * Do not attempt to use the old acpconfig commands "-v debug" and "-v dbg_unit", * as they are nonfunctional with this driver. The following messages are debug messages, they are off by default, I have already added 128 to the number. [224] dda%d:(x29) open line %d flag %o in %s mode [225] dda%d:(x29) closing line %d [225] dda%d:(x29) close: tp->t_state = %x [226] dda%d:(x29) ioctl qbit msg: cmd=%x ACC=%x [227] dda%d:(x29) xxstart: port %d t_state = %x [228] dda%d:(x29) xxstart: asked for %d got %d chars [229] dda%d:(x29) xxstart: mbuf %x len %d [230] dda%d:(x29) select() [231] dda%d:(x29) x29_supr() [231] dda%d:(x29) supr_msg: got call from %X [232] dda%d:(x29) x29_data: chan=%x cc=%x cnt=%x subcc=%x [233] dda%d:(x29) received data: <stream> [234] dda%d:(x29) x29_data: read complete mbuf %x %x [235] dda%d:(x29) x29_data: chan=%x DDAIOCOK [236] dda%d:(x29) qbit: <stream> [237] dda%d:(x29) flow restart [%d] in %x [237] dda%d:(x29) flow on [%d] in %x of %d [238] dda%d:(x29) xx_qbit_msg: %d %d %d [239] dda%d:(x29) xxcntl() [239] dda%d:(x29) xxcntl: close state: %s [239] dda%d:(x29) xxcntl: warning: state not data_idle [240] dda%d:(x29) xxclear: line=%d pgrp=%d state=%d [4] 4.3-tahoe support has been added. However, due to a change in the cpp (they finally fixed it!), ioctl command argument definitions have changed. In releases prior to 4.3-tahoe, argument definitions looked like: #define XIOWRITE _IOWR(t, 1, struct pi_dblock) The newer systems require the ioctl defines to have the command surrounded by apostrophe marks, like the following line: #define XIOWRITE _IOWR('t', 1, struct pi_dblock) Since most of our customers are running Ultrix or versions of BSD UNIX older than 4.3-tahoe, we ship the PI module with the apostrophes removed. If you are running 4.3-tahoe or a newer BSD release, you should rename the current if_pivar.h file to if_pivar.h.pretahoe and rename the if_pivar.h.tahoe file to if_pivar.h. The only difference in the files is the addition of the apostrophe as the CPP parameter delimiter. You will also need to modify the three ioctl definitions that were previously placed in /usr/include/sys/ioctl.h if you haven't done so already. sages 0 - 127 are error messages. By default, they are on. To toggle on or off the display of these messages, simply type acpconfig -c <msgnum>. The following error messages have been added for the X29 module: [96] dda%d:(x29) xxstart: unit offline [97] dda%d:(x29) xxstart: could not get mbuf [98] dda%d:(x2acpconfig/ 755 540 24 0 4535335666 5767 acpconfig/acpconfig.c 444 540 24 213456 4535335476 10221 #define VERSION "13-Nov-1989\n" /*************************************************************************/ /* */ /* */ /* ________________________________________________________ */ /* / \ */ /* | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* \________________________________________________________/ */ /* */ /* Copyright (c) 1988 by Advanced Computer Communications */ /* 720 Santa Barbara Street, Santa Barbara, California 93101 */ /* (805) 963-9431 */ /* */ /* */ /* File: acpconfig.c */ /* */ /* Author: Clare E. Russ */ /* */ /* Project: Installation verification program for ACC */ /* ACP 5100/6100 and ACP 5250/6250 network */ /* interface drivers. Acpconfig provides a user */ /* interface which supports the configuration of a */ /* network interface. */ /* */ /* Function: */ /* Based on socket ioctls, provide user interface to Network */ /* Interface Drivers for the following ACC ACP devices: */ /* */ /* ACP 5100/6100 'acp' interface */ /* ACP 5250/6250 'dda' interface */ /* ACP 625 'ddn' interface */ /* */ /* The ioctl indicates what type of message is to be sent to the */ /* front end device (ie, bring up the HDLC line in external */ /* loopback mode). */ /* */ /* Components: acpconfig.c, ioctl.h */ /* */ /* */ /* Usage Notes: */ /* */ /* -A filename */ /* Add the contents of the named file to the address */ /* translation table for PDN service */ /* */ /* -a ipaddr x25addr */ /* Add the specified address pair to the address */ /* translation table for PDN service */ /* */ /* -b baud */ /* Note that the baud rate values are different for the */ /* ACP 625. Table 1 contains the baud rates which apply */ /* to the ACP 6100, ACP 5250/6250 products. Table 2 */ /* contains the baud rates which apply to the ACP 625. */ /* */ /* Table 1: Nominal baud rates for ACP 5100/6100, ACP 5250/6250 */ /* */ /* 2.00M 2000K 2.0M 1.33M 1333K 1.3M */ /* 1.00M 1000K 1.0M 500K 250K 100K */ /* 64K 64000 56K 56000 30K 19.2K */ /* 9.6K 9600 4.8K 4800 2.4K 2400 */ /* 1.2K 1200 */ /* */ /* Table 2: Nominal baud rates for ACP 625 */ /* */ /* 316000 153600 115200 76800 76.8K */ /* 57600 57.6K 38400 38.4K 28800 */ /* 28.8K 19200 19.2K 9600 9.6K */ /* 4800 4.8K 2400 2.4K 2150 */ /* 1760 1200 */ /* */ /* An argument of 0 or "external" to the -b option */ /* specifies external clocking for the ACP 5100/6100 */ /* and ACP 5250/6250 interfaces. (The ACP 625 does not */ /* support this feature.) All other values imply */ /* internal clocking. */ /* The M for megabits/second and K for kilobits/second */ /* are optional. Note that Table 1 allows for the entry */ /* of 9.6 (with the assumed unit of Kilobits/second), but */ /* also allows for the entry of 9600 (bits/second). Thus */ /* there is more than one entry for some of the baud rate */ /* values. */ /* */ /* -c msgnum */ /* Toggle the enable status of driver message number msgnum */ /* */ /* -d ipaddr */ /* Delete the specified address entry from the address */ /* translation table for PDN service */ /* */ /* -D */ /* Delete all address entries form the address translation */ /* table for PDN service */ /* */ /* -e size */ /* set the firmware buffer size to size. This also resets */ /* the board. size comes from a table of valid sizes and */ /* may also be "default" to get the default size. */ /* */ /* -h mode */ /* Read driver histogram and print on standard output. Each */ /* entry is of the form sec.usec. The entries are as */ /* follows: */ /* 0 : number of seconds the link was up */ /* 1 : starting time */ /* 2 : ending time */ /* 3 : current value for tmo_data_idle */ /* 4-69 : seconds n logical channels were open for */ /* n = 0 to 64 (126) */ /* */ /* If mode is 0 then data is read. If mode is 1 then data */ /* is read and the histogram is reinitialized. */ /* */ /* -f parameter status */ /* Control flow control parameter negotiation where */ /* parameter is either "packet" or "window" and */ /* status is either "on" or "off". */ /* Note: incoming flow control parameter negotiation */ /* if not effected by this option. */ /* */ /* -l */ /* -ln */ /* List the currently active lcns. The 'n' suffix */ /* disables address to name lookups. */ /* */ /* -m message */ /* Pass ``message'' to the FEP as a supervisory channel */ /* message. Message is a sequence of numbers separated */ /* by white space. Numbers with leading ``0'' are taken */ /* as octal, other numbers taken as HEX, decimal is not */ /* supported. Hex and octal may be intermixed, as in */ /* ``0140 0 0 2 89 017''. The message is terminated by the */ /* end of the argument list or by an argument beginning */ /* with a dash ``-''. Absolutely no checking is performed; */ /* the bytes are written to the FEP as a supervisor */ /* message. The message is limited to MLEN = 112 bytes. */ /* */ /* -N network */ /* Set network type */ /* transpac - french transpac network addressing */ /* default - normal x25 network */ /* net15 - normal network but with 15 digit addrs */ /* */ /* -n circuits */ /* Set the number of logical channels to ``circuits'', which*/ /* must be less than some implementation defined maximum. */ /* */ /* -q query-type */ /* Query the board or driver and place results on stdout. */ /* */ /* -q 0 Statistics query to FEP */ /* -q 1 Driver operating mode query */ /* -q 2 Reserved for ACC debugging */ /* */ /* -r count */ /* Read the specified number of entries from the address */ /* translation table for PDN service */ /* */ /* -r 0 read all entries from the address translation table */ /* */ /* -s X.25 service */ /* Select DDN standard X.25 service or basic X.25 service */ /* or Public Data Network X.25 service */ /* */ /* -s standard select standard X.25 service */ /* -s basic select basic X.25 service */ /* -s pdn select X.25 Public Data Network service */ /* */ /* -t sec Set idle circuit timeout */ /* */ /* -u down bring down the link */ /* -u dte bring up link, no loopback, DTE */ /* -u dce bring up link, no loopback, DCE */ /* -u ext bring up link, external loopback mode */ /* -u int bring up link, internal loopback mode */ /* */ /* -v variable value */ /* Set the value of a driver variable symbolized by */ /* "variable" to "value" decimal. "Variables" understood */ /* are "window" and "packet" to set the driver's notion of */ /* negotiable window size, and negotiable packet size. */ /* */ /* -z reset the specifed front-end device */ /* */ /* Compile Notes: */ /* */ /* The associated makefile builds the acpconfig executable image */ /* for UNIX or the specified emulation/simulation such as */ /* The Wollongong Group (TWG) software which runs under VAX/VMS. */ /* The makefile must be invoked with the argument which specifies */ /* the target host operating system, otherwise the makefile */ /* defaults to creating an executable image for UNIX. */ /* */ /* make (compile for UNIX 4.nBSD) */ /* make clean */ /* make print */ /* */ /* */ /* System Notes: */ /* */ /* Create a socket of the AF_INET address family and of type */ /* datagram, SOCK_DGRAM. Use the socket to send a socket ioctl */ /* to the network driver of the specified interface ('acp0' in */ /* the usage notes). Depending on the type of message specified */ /* in the command line, send the appropriate socket ioctl to */ /* specify what kind of action is to be taken. */ /* */ /* For those drivers which support the Control Interface (CIF) */ /* the message exchange conforms to the CIF which defines paired */ /* command/response Control Interface Messages (CIMs) between the */ /* host and the front end. Otherwise, the exchange of messages */ /* is the pre-CIF protocol (i.e., that used by the ddn interface). */ /* */ /* Assignment of subcodes for the SIOCACPCONFIG ioctl: */ /* */ /* 1..14 -bRATE for RATE != 0 */ /* `0'..`4' -u N (line control) */ /* `a' -A and -a commands (PDN) */ /* `b' -b0 (set external clock) */ /* `c' -c msgnum */ /* `d' -d command (PDN) */ /* `e' -e size */ /* `h' -h 0 */ /* `H' -h 1 */ /* `m' -m (message command) */ /* `n' -n circuits (limit lcns) */ /* `p' -q 0 (statistics query) */ /* `q' -q 1 (driver query) */ /* `r' -r command (PDN) */ /* `t' -t sec (timeout set) */ /* `S' -s 0 */ /* `T' -s 1 */ /* `U' -s 2 */ /* `V' -v command (set variable) */ /* `z' -z command (reset) */ /* */ /*************************************************************************/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% INCLUDE FILES %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* There are alternate path names for The Wollongong Group (TWG) */ /* IPTCP and Eunice software which runs under VMS, */ /* and for TGV, Incorporated's MultiNet software which also runs */ /* under VMS. */ #ifdef VAXVMS # ifdef eunice # define WINS # else # define MULTINET # endif #endif #ifdef vax11c # define EXIT_ERR 0 # define EXIT_OK 1 #else # define EXIT_ERR 1 # define EXIT_OK 0 #endif #include <sys/param.h> #include <stdio.h> #include <errno.h> #include <ctype.h> #include <netdb.h> #include <nlist.h> #include <sys/mbuf.h> #include <sys/socket.h> #include <sys/ioctl.h> #ifdef MULTINET #include <vaxif/if_ddaioctl.h> /* multinet doesn't fix ioctl.h */ #endif #ifdef VAXVMS /* TWG or TGV */ #include <netinet/in.h> #include <net/if.h> #else #include "/sys/netinet/in.h" #include "/sys/net/if.h" #endif #ifdef MULTINET #ifdef errno #undef errno #endif errno #define errno socket_errno /* MultiNet runtime errors */ #define Perror(s) XPerror(s) /* Keep different from perror() */ #define ioctl(s,c,d) socket_ioctl(s,c,d)/* MultiNet ioctl() routine */ #endif MULTINET #define NDDA 1 #define NONE 0 /* used by the -o option */ #define EXTENDED 1 /* used by -o option */ /* 'iface' interface definitions */ #define ACP_INTERFACE 0x01 /* for acp interface */ #define DDN_INTERFACE 0x02 /* for ddn interface */ #define BAUD_VAL 50 /* for baud rate flag, -b */ #define SERVICE_VAL 35 /* for X.25 service flag, -s */ #define ACTUAL_VAL 83 /* for X.25 service flag 'S' */ /* network values */ #define NET_STANDARD 0 /* on standard network */ #define NET_TRANSPAC 1 /* on transpac style network */ /* delay macro from /sys/vax/param.h */ #ifdef vax11c #define NEW_DELAY(n) sleep(n / 100000) #else #define NEW_DELAY(n) { register int N = (n); while (--N > 0); } #endif /* The baud_rate structure maps the user-supplied baud rate into the */ /* parameter used in the socket ioctl to the driver. In some cases, */ /* more than one representation of a value is present to ease the */ /* interface. The manual page for acpconfig recommends that the */ /* user give the baud rate with the assumed unit of Kilobits/second. */ struct baud { char *b_rate; char parameter; } baud_rate[] = { { "2.00", 1 }, /* these are nominal baud rates */ { "2.00M", 1 }, { "2000K", 1 }, { "2000k", 1 }, { "2.0", 1 }, { "2.0M", 1 }, { "1.33", 2 }, { "1.33M", 2 }, { "1333K", 2 }, { "1333k", 2 }, { "1.3", 2 }, { "1.3M", 2 }, { "1.00", 3 }, { "1.00M", 3 }, { "1000K", 3 }, { "1000k", 3 }, { "1.0M", 3 }, { "1.0", 3 }, { "500", 4 }, { "500K", 4 }, { "500k", 4 }, { "250", 5 }, { "250K", 5 }, { "250k", 5 }, { "100", 6 }, { "100K", 6 }, { "100k", 6 }, { "64", 7 }, { "64K", 7 }, { "64k", 7 }, { "64000", 7 }, { "56", 8 }, { "56K", 8 }, { "56k", 8 }, { "56000", 8 }, { "30", 9 }, { "30K", 9 }, { "30k", 9 }, { "19.2", 10 }, { "19.2K", 10 }, { "19.2k", 10 }, { "9.6", 11 }, { "9.6K", 11 }, { "9.6k", 11 }, { "9600", 11 }, { "4.8", 12 }, { "4.8K", 12 }, { "4.8k", 12 }, { "4800", 12 }, { "2.4", 13 }, { "2.4K", 13 }, { "2.4k", 13 }, { "2400", 13 }, { "1.2", 14 }, { "1.2K", 14 }, { "1.2k", 14 }, { "1200", 14 }, { 0, 0 }, }; /* Table of baud rate values and the associated parameter for the Set */ /* System Parameters message, ddninit_msg. The 'parameter1' is nonzero */ /* for valid baud rate divisors. The user's manual gives both the */ /* actual and nominal baud rates, either one is accepted from the user, */ /* but the nominal baud rate is the figure which is closest to the rate */ /* set on the front end. */ struct baud ddnbaud_rate[] = { { "333333", 1 }, /* actual baud rate */ { "316000", 1 }, /* nominal baud rate */ { "153846", 2 }, /* actual baud rate */ { "153600", 2 }, /* nominal baud rate */ { "114285", 3 }, /* actual baud rate */ { "115200", 3 }, /* nominal baud rate */ { "76923", 4 }, /* actual baud rate */ { "76800", 4 }, /* nominal baud rate */ { "76.8K", 4 }, /* nominal baud rate */ { "76.8k", 4 }, /* nominal baud rate */ { "57971", 5 }, /* actual baud rate */ { "57600", 5 }, /* nominal baud rate */ { "57.6K", 5 }, /* nominal baud rate */ { "57.6k", 5 }, /* nominal baud rate */ { "38461", 6 }, /* actual baud rate */ { "38400", 6 }, /* nominal baud rate */ { "38.4K", 6 }, /* nominal baud rate */ { "38.4k", 6 }, /* nominal baud rate */ { "28776", 7 }, /* actual baud rate */ { "28800", 7 }, /* nominal baud rate */ { "28.8K", 7 }, /* nominal baud rate */ { "28.8k", 7 }, /* nominal baud rate */ { "19230", 8 }, /* actual baud rate */ { "19200", 8 }, /* nominal baud rate */ { "19.2K", 8 }, /* nominal baud rate */ { "19.2k", 8 }, /* nominal baud rate */ { "9592", 9 }, /* actual baud rate */ { "9600", 9 }, /* nominal baud rate */ { "9.6K", 9 }, /* nominal baud rate */ { "9.6k", 9 }, /* nominal baud rate */ { "4801", 10 }, /* actual baud rate */ { "4800", 10 }, /* nominal baud rate */ { "4.8K", 10 }, /* nominal baud rate */ { "4.8k", 10 }, /* nominal baud rate */ { "2400", 11 }, /* actual baud rate */ { "2.4K", 11 }, /* actual baud rate */ { "2.4k", 11 }, /* actual baud rate */ { "2150", 12 }, /* nominal baud rate */ { "1760", 13 }, /* actual baud rate */ { "1760", 13 }, /* actual baud rate */ { "1200", 14 }, /* nominal baud rate */ { "1.2K", 14 }, /* nominal baud rate */ { "1.2k", 14 }, /* nominal baud rate */ { 0, 0 }, }; #ifndef MULTINET char *kmemf = "/dev/kmem"; int kmem; #endif MULTINET #include <strings.h> #include <sys/time.h> #ifdef VAXVMS # ifdef WINS # include <vaxif/if_ddareg.h> # include <vaxif/if_ddavar.h> # endif # ifdef MULTINET # include "[-.kernel.vaxif]if_ddareg.h" # include "[-.kernel.vaxif]if_ddavar.h" # endif #else VAXVMS /* must be unix or ultrix */ # include "/sys/vaxif/if_ddareg.h" # include "/sys/vaxif/if_ddavar.h" #endif VAXVMS /* EXTERNAL FUNCTIONS */ extern char *routename(); extern off_t lseek(); extern u_long inet_addr(); extern char *inet_ntoa(); extern u_long inet_network(); /* * Weirdness for test-jig, re-map all ioctl and socket calls to go * through driver simulator. */ #ifdef SIMULATION #define ioctl fake_unix_ioctl #define socket fake_unix_socket #define main cf_main #endif /* * Print contents of queues and various data structures */ char *state_tab[] = { "Down", "Rstrt", "Idle", "Call", "Open", "Clear" }; #define NSTATES 5 /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% MAIN() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* For the specified interface, create a socket for a socket */ /* ioctl to set the Internet address and configuration. The */ /* ioctl kicks the appropriate driver, the value in ifr_data */ /* indicates the type of action to be taken. ('b' indicates */ /* external clock, 1-14 indicates baud rate from the table of */ /* possible values, '0' - '4' indicates -u options, and 's','t', */ /* 'u' indicate -s options.) */ /* Enhancement: 'a' indicates addition of an address table */ /* entry, 'd' indicates deletion of an address table entry, */ /* and 'r' is a request to read address table entries. */ /* Etc, etc (see comment at top for complete list) */ /* */ /* Call: main( argc, argv) */ /* Arguments: argc: argument count */ /* argv: argument value */ /* Returns: nothing */ /* Called by: invoked by privileged user */ /* Calls to: socket() */ /* perror() */ /* strcpy() */ /* strncpy() */ /* sizeof() */ /* usage() */ /* long_usage() */ /* get_bfrsize_index() */ /* ioctl() */ /* exit() */ /* Perror() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ struct ifreq ifr; struct sockaddr_in sin = { AF_INET }; char name[30]; int s; /* socket descriptor */ int nflag; /* control host name lookup (symbolic) */ struct ddactl ddactl; struct trtab trtab; struct lg2tab { /* For use with packet sizes, so just the */ int base2log; /* legal packet sizes for X.25 are here */ int binval; } lg2tab[] = { 4, 16, 5, 32, 6, 64, 7, 128, 8, 256, 9, 512, 10, 1024, 11, 2048, 12, 4096, 0, 0, }; char *fe_bfr_sizes[] = { "default", /* entry 0: Default Size */ "256", /* entry 1: 256 bytes */ "512", /* entry 2: 512 bytes */ "1024", /* entry 3: 1K bytes (currently same as default) */ "2048", /* entry 4: 2K bytes */ "4096", /* entry 5: 4K bytes */ "8192", /* entry 6: 8K bytes */ "16384", /* entry 7: 16K bytes */ NULL }; extern int errno; /* * Structure used to hold the results of an nlist library * function call. */ struct nlist nl[] = { { "_dda_softc" }, #define SOFTC 0 "", }; #ifndef VAXVMS /* * Seek into the kernel for a value. * Used by the -l option of acpconfig. */ klseek(fd, base, off) int fd; off_t base; int off; { if (lseek(fd, base, off) == (off_t)-1 ){ fprintf(stderr,"acpconfig: Bad lseek fd=%d,bas=%x,off=%d ",fd,base,off); perror(""); exit(1); } } klread(fd,buf,size) int fd; char *buf; int size; { if ( read(fd,buf,size) < 0 ){ perror("Read"); exit(2); } } #endif VAXVMS /* * Routine used in conjunction with the -l option. * This function displays the status of each active * lcn for the given unit. */ display(addr,nddach) off_t addr; int nddach; /* number of circuits currently available */ { struct dda_softc dda_softc; register struct dda_cb *dc; struct sockaddr_in sin; char *p; int i; int header; if (addr == 0) { printf("acpconfig: nlist--symbol not defined\n"); return; } #ifdef MULTINET klseek(addr); klread((char *)&dda_softc, sizeof(dda_softc)); #else MULTINET klseek(kmem, addr, 0); klread(kmem, (char *)&dda_softc, sizeof(dda_softc)); #endif MULTINET header = 0; for ( i=0; i<= nddach ; i++) { dc = &dda_softc.dda_cb[i]; if ((dc->dc_state != LC_IDLE) && (dc->dc_state != LC_DOWN)) { if (!header) { printf("\n\t\t\tACP 6250 / 5250 (dda%d) Status\n", dda_softc.dda_if.if_unit); printf("lcn Qlen Dropped Flags Timer State Pin Pout Win Wout IP Addr\n"); } header++; printf("%3d ",i); printf("%4d ",dc->dc_oq.ifq_len); printf("%6d ",dc->dc_oq.ifq_drops); switch (dc->dc_flags & DC_CLIENTS) { case DC_X29: p="Pad "; break; case DC_X29W: p="Host"; break; case DC_RAW: p="PI "; break; case DC_IP: p="IP "; break; default: p="bug!"; break; } printf("%c %s", (dc->dc_flags & DC_OBUSY ? 'B' : ' '), p); printf(" %3d ", dc->dc_timer); if (dc->dc_state > NSTATES) printf ("%4x? ", dc->dc_state); else printf("%-5s ", state_tab [dc->dc_state]); printf(dc->dc_pktsizein ? "%4d ":" ??? ",1<<dc->dc_pktsizein); printf(dc->dc_pktsizeout ?"%4d ":" ??? ",1<<dc->dc_pktsizeout); printf(dc->dc_wsizein ? "%3d ":"??? ",dc->dc_wsizein); printf(dc->dc_wsizeout ?"%3d ":"??? ",dc->dc_wsizeout); if ((dc->dc_flags & DC_CLIENTS) == DC_IP) printf(" %s",routename(dc->dc_inaddr)); putchar('\n'); } } if ((!header) && (i == nddach + 1)) printf("lcns 0 through %d inactive\n",nddach); sin.sin_addr.s_addr = dda_softc.dda_ipaddr.s_addr; printf("Our addr: %s", routename(sin.sin_addr)); (void)putchar('\n'); } char * routename(in) struct in_addr in; { char *cp = 0; static char line[50]; int lna, net; net = inet_netof(in); lna = inet_lnaof(in); if (!nflag) { if (lna == INADDR_ANY) { struct netent *np = getnetbyaddr(net, AF_INET); if (np) cp = np->n_name; } else { struct hostent *hp; hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), AF_INET); if (hp) cp = hp->h_name; } } if (cp) (void)strcpy(line, cp); else { u_char *ucp = (u_char *)∈ if (lna == INADDR_ANY) (void)sprintf(line, "%u.%u.%u", ucp[0], ucp[1], ucp[2]); else (void)sprintf(line, "%u.%u.%u.%u", ucp[0], ucp[1], ucp[2], ucp[3]); } return (line); } get_bfrsize_index(str) char *str; { register int i; for(i = 0; fe_bfr_sizes[i]; i++) if(strcmp(str,fe_bfr_sizes[i]) == 0) return(i); fprintf(stderr,"Invalid size: %s\nBuffer sizes available are: "); for(i = 0; fe_bfr_sizes[i]; i++) fprintf(stderr,"%s ",fe_bfr_sizes[i]); fprintf(stderr,"\n"); return(-1); } main(argc, argv) int argc; char *argv[]; { register struct baud *p; /* baud rates */ int iface = 0; /* indicate interface, i.e. acp */ int tmp, top, offset; int unit; FILE *fp; char line[80], arg1[40], arg2[40]; char *ap; char option_byte1 = 0; int o_flag = 0; if (argc == 1) { long_usage(); } if (argc < 3) { fprintf(stderr,"\n acpconfig: invalid number of arguments\n"); usage(); /* display proper syntax for user */ } s = socket(AF_INET, SOCK_DGRAM, 0); /* try create socket */ if (s < 0) { perror("acpconfig: socket"); exit(1); } argc--, argv++; /* interface name */ (void)strcpy(name, *argv); (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if ((*argv)[2] == 'n') /* check for 'ddn' interface */ iface |= DDN_INTERFACE; if ((*argv)[0] == 'a') /* check for 'acp' interface */ iface |= ACP_INTERFACE; if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCGIFFLAGS)"); exit(1); } unit = (*argv)[3] & 0x0F; while (--argc > 0) { argv++; if(**argv != '-') setifaddr(*argv); /* set interface address */ else { ifr.ifr_data = 0; switch((*argv)[1]) /* process flag(s) */ { case 'b': /* set baud rate or ext clocking */ ap = *argv; if (ap[2] == '\0') { /* -b N case */ argc--, argv++, ap = *argv; if (ap == 0) ap = "???"; } else ap += 2; /* -bN case, skip over the "-b" */ /* ap points to purported number, regardless -bN or -b N */ if ((*ap == '0') || (strcmp(ap, "external") == 0)) { if (iface&DDN_INTERFACE) { fprintf (stderr, "\nacpconfig %s: -b 0 invalid\n", name); break; } arg1[0] = 'b'; ifr.ifr_data = arg1; } else /* set baud rate, if N is valid */ { if (iface&DDN_INTERFACE) p = ddnbaud_rate; else p = baud_rate; while (p->b_rate) { if (strcmp (ap, p->b_rate) == 0) break; p++; } if (p->parameter) ifr.ifr_data = &(p->parameter); } if (setconfig(ifr.ifr_data, s)) { fprintf(stderr,"\nacpconfig: -b %s invalid\n", ap); usage(); } break; case 'l': /* monitor status of lcns */ if ((*argv)[2] == 'n' && (*argv)[3] == '\0') nflag = 1; /* "-ln" - numeric */ else if ((*argv)[2] == '\0') nflag = 0; /* "-l" - symbolic */ else usage(); /* other - wrong. */ if (strlen(*argv) < 3) { #ifdef VAXVMS # ifdef MULTINET if(multinet_kernel_nlist("MULTINET_NETWORK_IMAGE:",nl) ==-1) # else MULTINET /* must be TWG instead */ char *vmsnlist(); kmemf = vmsnlist(); if(nlist(kmemf, nl) == -1) # endif MULTINET #else VAXVMS if(nlist("/vmunix", nl) == -1) #endif VAXVMS { fprintf(stderr,"acpconfig: could not open /vmunix\n"); exit(1); } if (nl[SOFTC].n_value == 0) { fprintf(stderr, "acpconfig: No namelist\n"); exit(1); } #ifndef VAXVMS kmem = open (kmemf, 0); if (kmem < 0) { fprintf(stderr, "acpconfig: cannot open "); perror(kmemf); exit(1); } #endif ddactl.func = 'n'; ddactl.drval = 0; ifr.ifr_data = (caddr_t) &ddactl; if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } display((off_t)(nl[SOFTC].n_value+unit * sizeof(struct dda_softc)),*(int *)ifr.ifr_data); } else usage(); (void)close(kmem); break; case 'o': /* collect X.25 1984 options */ { char *cp = *argv; if (strlen (cp) > 2) /* handle -oNAME and -o NAME */ cp += 2; else { argc--; argv++; cp = *argv; if (cp == 0) { fprintf (stderr, "acpconfig: -o what?\n"); exit(1); } } if (strcmp(cp,"none") == 0) option_byte1 = NONE; else if (strcmp(cp,"extended") == 0) option_byte1 |= EXTENDED; else { fprintf(stderr,"acpconfig: -o %s invalid\n",cp); exit(1); } o_flag++; } break; case 's': /* select DDN standard X.25 service */ /* or basic X.25 service, accept -sn */ /* or -s n */ if (iface & ACP_INTERFACE) { /* error if 'acp' device */ fprintf(stderr,"\n acpconfig: '-s' flag invalid for"); fprintf(stderr," specified interface\n"); break; } ifr.ifr_data = 0; /* clear value */ if (!(*argv)[2]) { offset = 0; argc--; argv++; } else offset = 2; if (((*argv)[offset] >='0' && (*argv)[offset]< '3') && (*argv)[offset + 1] == 0) { (*argv)[offset] += SERVICE_VAL; /* match 'S','T','U' */ ifr.ifr_data = &((*argv)[offset]); } else if ((strncmp(&(*argv)[offset],"standard",8)==0) && (*argv)[offset + 8] == 0) { (*argv)[offset] = ACTUAL_VAL; /* match 'S' */ ifr.ifr_data = &((*argv)[offset]); } else if ((strncmp(&(*argv)[offset],"basic",5)==0) && (*argv)[offset + 5] == 0) { (*argv)[offset] = ACTUAL_VAL + 1; /* match 'T' */ ifr.ifr_data = &((*argv)[offset]); } else if ((strncmp(&(*argv)[offset],"pdn",3)==0) && (*argv)[offset + 3]==0) { (*argv)[offset] = ACTUAL_VAL + 2; /* 'U' */ ifr.ifr_data = &((*argv)[offset]); } else { fprintf(stderr, "\nacpconfig: invalid X.25 service\n"); usage();/* display proper syntax for user */ } if ((ifr.ifr_data == 0) || setconfig(ifr.ifr_data, s)) { if (errno == EALREADY) { fprintf(stderr,"acpconfig: must shut down interface to\ change modes between DDN and PDN\n"); exit(1); } } break; case 'u': { char *vp = *argv; /* value to set it to. */ char *cp; ifr.ifr_data = NULL; if (vp[2] == '\0') { argc--; /* value is next arg; fix pointers */ argv++; vp = *argv; } else vp += 2; /* value at 3rd char of this arg */ if (argc > 1) { fprintf(stderr, "-u command must be last on line\n"); exit(1); } if (strcmp(vp, "down") == 0 || *vp == '0') ifr.ifr_data = "0"; if (strcmp(vp, "dte") == 0 || *vp == '1') ifr.ifr_data = "1"; if (strcmp(vp, "dce") == 0 || *vp == '2') ifr.ifr_data = "2"; if (strcmp(vp, "ext") == 0 || *vp == '3') ifr.ifr_data = "3"; if (strcmp(vp, "int") == 0 || *vp == '4') ifr.ifr_data = "4"; if (ifr.ifr_data == 0) { fprintf(stderr,"acpconfig: -u invalid mode '%s'\n", *vp); usage(); /* display proper syntax for user */ } else { if ( setconfig(ifr.ifr_data, s) ) { if (errno == EADDRNOTAVAIL) { fprintf(stderr, "acpconfig: no local X.25 address \ translation in table; cannot start up PDN mode\n"); exit(1); } else { usage(); /* display proper syntax for user */ } } } } break; case 'f': /* control initiation of flow control */ /* parameters netgotiation. */ { char *cp; /* keyword determining which parameter */ /* to control*/ char *vp; /* value to set it to. */ cp = *argv; if (strlen (cp) > 2) /* handle -fNAME VAL and -f NAME VAL */ cp += 2; else { argc--; argv++; cp = *argv; if (cp == 0) { fprintf (stderr, "acpconfig: -f what?\n"); exit(1); } } argc--; argv++; vp = *argv; if (vp == 0) { fprintf (stderr, "acpconfig: -f %s what?\n", cp); exit(1); } if (strcmp(cp,"packet") == 0) ddactl.msg[0] = 0; else if (strcmp(cp,"window") == 0) ddactl.msg[0] = 1; else { fprintf(stderr,"acpconfig: -f %s invalid\n",cp); exit(1); } if (strcmp(vp,"on") == 0) ddactl.drval = 1; else if (strcmp(vp,"off") == 0) ddactl.drval = 0; else { fprintf(stderr,"acpconfig: -f %s invalid\n",cp); exit(1); } ddactl.func = 'f'; (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_data = (caddr_t) &ddactl; if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } break; case 'h': /* histogram information */ { char *cp = *argv; struct timeval databuf[HISTSIZE]; register int i; if (strlen (cp) > 2) cp += 2; else { argc--; argv++; cp = *argv; if (cp == 0) { fprintf(stderr, "acpconfig: -h what?\n"); exit(1); } } *((char *)databuf) = (*cp == '1') ? 'H' : 'h'; ifr.ifr_data = (caddr_t) databuf; (void)strncpy (ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } if(*(cp+1) == 'r') { printf("%ld.%06ld\t",databuf[H_LINK_UP].tv_sec,databuf[H_LINK_UP].tv_usec); printf("%ld.%06ld",databuf[H_START].tv_sec,databuf[H_START].tv_usec); printf("\t%ld.%06ld",databuf[H_END].tv_sec,databuf[H_END].tv_usec); printf("\t%ld.%06ld",databuf[H_TMO].tv_sec,databuf[H_TMO].tv_usec); for(i = 0; i < H_LINK_UP; i++) printf("\t%ld.%06ld",databuf[i].tv_sec,databuf[i].tv_usec); printf("\n"); } else hist_data_out(databuf); } break; case 'm': /* Send supervisory message to FEP */ { char *mp = ddactl.msg; int i; static char *xfmt = "%x"; static char *ofmt = "%o"; bzero (ddactl.msg, sizeof (ddactl.msg)); while (--argc > 0) { argv++; if (*argv[0] == '-') { /* hit a non-number */ argc++; /* fix these guys for */ argv--; /* the main loop */ break; } if (mp > &ddactl.msg[sizeof (ddactl.msg)]) { fprintf (stderr,"\nacpconfig: -m message too long\n"); exit(1); } (void)sscanf (*argv, (**argv == '0') ? ofmt : xfmt, &i); *mp++ = i; } /* while */ ddactl.func = 'm'; (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_data = (caddr_t) &ddactl; if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } break; case 'n': /* set SVC limit */ { char *ap = *argv; if (ap[2] == '\0') { argc--; /* value is next arg; fix pointers */ argv++; ap = *argv; } else ap += 2; /* value at 3rd char of this arg */ ddactl.func = 'n'; ddactl.drval = atoi(ap); (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_data = (caddr_t) &ddactl; if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } else if (*(int *)ifr.ifr_data != 0) printf("acpconfig: %s: %d circuits currently available.\n",name,*(int *)ifr.ifr_data); } break; case 'q': /* query board or driver for status */ { char *cp = *argv; int mode; if (strlen (cp) > 2) cp += 2; else { argc--; argv++; cp = *argv; if (cp == 0) { fprintf(stderr, "acpconfig: -q what?\n"); exit(1); } } mode = atoi(cp); ddactl.func = 'q'; ddactl.msg[0] = mode; ifr.ifr_data = (caddr_t) &ddactl; (void)strncpy (ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } switch (mode) { case 0: fmtstats(ddactl.msg); break; case 1: fmtmode(ddactl.msg); break; case 2: fmtsilo(ddactl.msg); break; } } break; case 't': /* set idle circuit timeout limit */ { char *ap = *argv; if (ap[2] == '\0') { argc--; /* value is next arg; fix pointers */ argv++; ap = *argv; } else ap += 2; /* value at 3rd char of this arg */ ddactl.func = 't'; ddactl.drval = atoi(ap); (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_data = (caddr_t) &ddactl; if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } break; case 'c': /* disable/enable driver console message */ { char *ap = *argv; int val; if (ap[2] == '\0') { argc--; /* value is next arg; fix pointers */ argv++; ap = *argv; } else ap += 2; /* value at 3rd char of this arg */ if ((ap == NULL) || (*ap == '-') || (*ap == '\0')) { fprintf(stderr, "\nacpconfig: no message number specified for -c\n"); exit(1); } ddactl.func = 'c'; val = ddactl.drval = atoi(ap); (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_data = (caddr_t) &ddactl; if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } else { printf("acpconfig: %s: Driver message #%d is now %s\n",name,val,*(u_char *)ifr.ifr_data ? "disabled" : "enabled"); } } break; case 'N': /* set net id */ { char *vp = *argv; /* value to set it to. */ char *cp; int net_type = -1; if (vp[2] == '\0') { argc--; /* value is next arg; fix pointers */ argv++; vp = *argv; } else vp += 2; /* value at 3rd char of this arg */ if (strcmp(vp, "transpac") == 0) net_type = NET_TRANSPAC; if (strcmp(vp, "0") == 0 || strcmp(vp, "standard") == 0) net_type = NET_STANDARD; if (net_type != -1) { ddactl.func = 'N'; ddactl.drval = net_type; ifr.ifr_data = (caddr_t) &ddactl; if (setconfig(&ifr.ifr_data, s)) exit(1); } if (net_type == NET_TRANSPAC || strcmp(vp, "net15") == 0) { /* allow 15 digit addrs */ ddactl.func = 'm'; bzero(ddactl.msg, sizeof (ddactl.msg)); bcopy("\140\0\0\2\177\17", ddactl.msg, 6); ifr.ifr_data = (caddr_t) &ddactl; if (setconfig(&ifr.ifr_data, s)) exit(1); } } break; case 'v': /* set variable */ { char *cp; /* keyword determining what to set */ char *vp; /* value to set it to. */ int i, val; /* * The ORDERING of this table must match the * cases in the processing for this ioctl * inside the driver (kludgy interface!) */ /* debug, dbgunit, and log are obsolete */ static char *nametab[] = { "log", "debug", "dbgunit", "packet", "window", 0 }; cp = *argv; if (strlen (cp) > 2) /* handle -vNAME VAL and -v NAME VAL */ cp += 2; else { argc--; argv++; cp = *argv; if (cp == 0) { fprintf (stderr, "acpconfig: -v what?\n"); exit(1); } } argc--; argv++; vp = *argv; if (vp == 0) { fprintf (stderr, "acpconfig: -v %s what?\n", cp); exit(1); } for (i = 0; nametab[i] != 0; i++) if (0 == strcmp (nametab[i], cp)) goto strmatch; fprintf (stderr, "acpconfig: -v: \"%s\" unknown\n", cp); exit(1); strmatch: /* hit the keyword, and "i" is its index */ if (i < 3) /* log, debug: masks: read in hexadecimal */ (void)sscanf (vp, "%x", &val); else /* window, packet: counts: read in decimal */ (void)sscanf (vp, "%d", &val); if (i == 3) { /* packet size: take base 2 log */ struct lg2tab *lp; /* * The sad thing is that after putting in all this * code to take the base 2 log, I realized that the * FEP takes this argument in BYTES and does the * log internally. Left the code in, though, since * it's the easiest way to check the argument. * (The driver converts to bytes with a shift). */ for (lp = lg2tab; lp->base2log; ++lp) if (lp->binval == val) { val = lp->base2log; goto goodsize; } fprintf (stderr, "acpconfig: -v: bad packet size\n"); exit(1); } goodsize: ddactl.drval = val; ddactl.msg[0] = i; ddactl.func = 'v'; (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_data = (caddr_t) &ddactl; if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } break; case 'e': { char *cp = *argv; char databuf[10]; register int i; if (strlen (cp) > 2) cp += 2; else { argc--; argv++; cp = *argv; } databuf[0] = 'e'; i = get_bfrsize_index(cp); if(i == -1) exit(1); /* get_bfrsize_index will print error message */ else databuf[1] = i; ifr.ifr_data = (caddr_t) databuf; if (!setconfig(ifr.ifr_data, s)) fprintf(stderr,"\nacpconfig: buffer reset in progress, wait 1 minute\n"); break; } case 'z': /* reset the specified front-end device*/ ifr.ifr_data = &((*argv)[1]); if (!setconfig(ifr.ifr_data, s)) fprintf(stderr,"\nacpconfig: reset in progress, wait 1 minute\n"); break; case 'x': ifr.ifr_data = &((*argv)[1]); if (setconfig(ifr.ifr_data, s)) fprintf(stderr,"acpconfig: Could not create UCBs\n"); break; case 'A': /* add a file of address translation table entries */ /* accept "-A filename OR "-Afilename" */ if (iface & ACP_INTERFACE) { /* error if 'acp' device */ fprintf(stderr,"acpconfig: '-A' flag invalid for specified \ interface\n"); exit(1); } ifr.ifr_data = 0; /* clear value */ if (strlen(*argv) > 2) fp = fopen(&((*argv)[2]),"r"); else { argc--, argv++; fp = fopen(*argv,"r"); } if (fp==NULL) { fprintf(stderr,"acpconfig: cannot open file '%s'\n", ((*argv)[0] == '-') ? &((*argv)[2]) : *argv); exit(1); } fprintf(stdout, "acpconfig: processing file '%s'; please wait...\n", ((*argv)[0] == '-') ? &((*argv)[2]) : *argv); while (fgets(line, 80, fp) != NULL) { if (line[0] == '#' || line[0] == '\n') continue; (void)sscanf (line, "%s %s", arg1, arg2); /* fprintf(stdout, "acpconfig: processing translation %s ==> %s\n", arg1, arg2); */ if ((top=strlen(arg2)) > (MAXADDRLEN-1) || top < (MINADDRLEN-1)) { fprintf(stderr, "acpconfig: bad X.25 address length: '%s'\n", arg2); exit(1); } for (tmp=0; tmp<top; tmp++) if (arg2[tmp] > '9' || arg2[tmp] < '0') { fprintf(stderr, "acpconfig: invalid X.25 address '%s'\n", arg2); exit(1); } getaddr(arg1,&sin); trtab.func = 'a'; trtab.ipaddr = sin.sin_addr.s_addr; trtab.x25addr[0]=strlen(arg2); (void)strncpy((char *)&trtab.x25addr[1], arg2, MAXADDRLEN-1); ifr.ifr_data = (caddr_t)&trtab; (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } (void)fclose(fp); break; case 'a': /* add an address translation table entry */ /* accept "-a inet_addr x25_addr" */ /* OR "-ainet_addr x25_addr" */ if (iface & ACP_INTERFACE) { /* error if 'acp' device */ fprintf(stderr,"acpconfig: '-a' flag invalid for specified \ interface\n"); exit(1); } ifr.ifr_data = 0; /* clear value */ if (strlen(*argv) > 2) getaddr(&((*argv)[2]),&sin); else { argc--, argv++; getaddr(*argv,&sin); } trtab.func = 'a'; trtab.ipaddr = sin.sin_addr.s_addr; argc--, argv++; if ((top=strlen(*argv)) > (MAXADDRLEN-1) || top < (MINADDRLEN-1)) { fprintf(stderr, "acpconfig: bad X.25 address length: '%s'\n", *argv); exit(1); } for (tmp=0; tmp<top; tmp++) if ((*argv)[tmp] > '9' || (*argv)[tmp] < '0') { fprintf(stderr, "acpconfig: invalid X.25 address '%s'\n", *argv); exit(1); } trtab.x25addr[0]=strlen(*argv); (void)strncpy((char *)&trtab.x25addr[1], *argv, MAXADDRLEN-1); ifr.ifr_data = (caddr_t)&trtab; (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } break; case 'd': /* delete an address translation table entry */ /* accept "-d inet_addr" OR "-dinet_addr" */ if (iface & ACP_INTERFACE) { /* error if 'acp' device */ fprintf(stderr,"acpconfig: '-d' flag invalid for specified \ interface\n"); exit(1); } ifr.ifr_data = 0; /* clear value */ if (strlen(*argv) > 2) getaddr(&((*argv)[2]),&sin); else { argc--, argv++; getaddr(*argv,&sin); } trtab.func = 'd'; trtab.ipaddr = sin.sin_addr.s_addr; ifr.ifr_data = (caddr_t)&trtab; (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } break; case 'D': if (iface & ACP_INTERFACE) { /* error if 'acp' device */ fprintf(stderr,"acpconfig: '-d' flag invalid for specified \ interface\n"); exit(1); } trtab.func = 'D'; ifr.ifr_data = (caddr_t)&trtab; (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } break; case 'r': /* read an address translation table entry */ /* accept "-r length" OR "-rlength" */ if (iface & ACP_INTERFACE) { /* error if 'acp' device */ fprintf(stderr,"acpconfig: '-r' flag invalid for specified \ interface\n"); exit(1); } ifr.ifr_data = 0; /* clear value */ if (strlen(*argv) > 2) top = atoi(&((*argv)[2])); else { argc--, argv++; top = atoi(*argv); } if (top==0) top = 30000; /* (a big number) */ for (tmp=0; tmp<top; tmp++) { trtab.ipaddr = (u_long) tmp; trtab.func = 'r'; ifr.ifr_data = (caddr_t)&trtab; (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { if (errno == EFAULT) { fprintf(stdout,"acpconfig: end of address table \ (%d entries max)\n", tmp); break; } else { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } /* * The byte ordering "works" on the inet_ntoa() call. * Probably only on VAXen, though (fine for us). */ if (trtab.x25addr[0]) { printf("acpconfig: address table entry %d: ", tmp); printf ("%s ==> %01.16s\n", inet_ntoa (trtab.ipaddr), &trtab.x25addr[1]); } } break; default: usage(); /* display proper syntax for user */ } } } /* * Send ioctl for gathered -o options. * Presently (08-31-87) the -o option supports only * one byte of options. If the number of firmware * supported options reaches 9, then a second byte * will be used. The driver must be modified if a * second byte is used. The value of PKT_OPTIONS * must be changed from 0x77 to 0xB7 to notify the * FE that PKT_OPTIONS is a 2-Byte Valued ID. */ if (o_flag) { /* send ioctl for gathered options */ ddactl.func = 'o'; ddactl.msg[0] = option_byte1; (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_data = (caddr_t) &ddactl; if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); exit(1); } } /* * if simulation, simply return to main loop of test jig */ #ifndef SIMULATION exit(0); #endif } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% SETIFADDR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Set the interface address. */ /* */ /* Call: setifaddr(addr) */ /* Arguments: addr: internet address */ /* Returns: nothing */ /* Called by: main() */ /* Calls to: getaddr() */ /* strncpy() */ /* ioctl() */ /* Perror() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ setifaddr(addr) char *addr; { getaddr(addr, (struct sockaddr_in *)&ifr.ifr_addr); (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCSIFADDR, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCSIFADDR)"); exit(1); } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% GETADDR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* THIS ROUTINE IS SLOW AS MOLASSES - - FIX IT SOONER OR LATER !!! */ /* Purpose: */ /* */ /* Get the interface address and stick it in the appropriate */ /* data structure. This routine has four different methods for */ /* deriving the address. If successful return nothing, else */ /* print error message and exit. */ /* */ /* */ /* Call: getaddr(s, sin) */ /* Arguments: s: string for address */ /* sin: socket address struct */ /* Returns: nothing for success, else error message exit */ /* Called by: setifaddr() */ /* Calls to: gethostbyname() */ /* bcopy() */ /* getnetbyname() */ /* inet_makeaddr() */ /* inet_addr() */ /* inet_network() */ /* fprintf() */ /* exit() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ struct in_addr inet_makeaddr(); getaddr(s, sin) char *s; struct sockaddr_in *sin; { struct hostent *hp, #ifdef VAXVMS *_gethtbyname(); #else VAXVMS *gethostbyname(); #endif VAXVMS struct netent *np; int val; sin->sin_family = AF_INET; val = inet_addr(s); if (val != -1) { sin->sin_addr.s_addr = val; return; } #ifdef VAXVMS hp = _gethtbyname(s); /* Use host table, not name server */ #else VAXVMS # ifdef SIMULATION hp = 0; /* don't find our address by host name */ # else SIMULATION hp = gethostbyname(s); # endif SIMULATION #endif VAXVMS if (hp) { sin->sin_family = hp->h_addrtype; bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length); return; } np = getnetbyname(s); if (np) { sin->sin_family = np->n_addrtype; sin->sin_addr = inet_makeaddr((int)np->n_net, INADDR_ANY); return; } sin->sin_family = AF_INET; val = inet_addr(s); if (val != -1) { sin->sin_addr.s_addr = val; return; } val = inet_network(s); if (val != -1) { sin->sin_addr = inet_makeaddr(val, INADDR_ANY); return; } fprintf(stderr, "acpconfig: invalid internet address '%s'\n", s); exit(1); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% SETCONFIG() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Set the configuration parameters by sending socket ioctl */ /* to the specified interface driver. */ /* */ /* */ /* Call: setconfig(action, s) */ /* Arguments: action indicates action to be taken */ /* s socket descriptor */ /* Returns: 0 for success, nonzero for failure */ /* NOTE: if no internet address, doesn't return, */ /* program exits */ /* Called by: main() */ /* Calls to: fprintf() */ /* strncpy() */ /* sizeof() */ /* ioctl() */ /* Perror() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ setconfig(action, s) char *action; int s; { extern int errno; int saves; if (action == 0) return(1); saves = s; (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { if (errno == EINTR) { fprintf(stderr,"\nacpconfig: command in progress\n"); NEW_DELAY(3000000); /* wait for device pwrup diags */ if (ioctl(saves, SIOCACPCONFIG, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCACPCONFIG) returns"); return(1); } else return(0); } else { Perror("ioctl (SIOCACPCONFIG) returns"); return(1); } } return(0); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% USAGE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Display the correct syntax for the acpconfig command line. */ /* */ /* Call: usage() */ /* Returns: nothing */ /* Called by: main() */ /* Calls to: fprintf() */ /* exit() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ static char *usemsg = "Usage: acpconfig interface [address] [-A filename] [-a ipaddr x25addr]\n\ \t[-b baud] [-c msgnum] [-d ipaddr] [-D] [-e size] [-f facility status]\n\ \t[-h mode[r]] [-l] [-m message] [-n circuits] [-o option]\n\ \t[-q type] [-r count] [-s X.25_service] [-t secs]\n\ \t[-u mode] [-v variable value] [-z]\n"; usage() { fprintf(stderr, usemsg); fprintf(stderr, "\nacpconfig generation %s\n", VERSION); exit(1); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% LONG_USAGE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Display the correct syntax for the acpconfig command line. */ /* This routine is called when no arguments are passed to */ /* acpconfig, this generally indicates that the user want to see */ /* the usage line as opposed to making a mistake in the */ /* arguments */ /* */ /* Call: usage() */ /* Returns: nothing */ /* Called by: main() */ /* Calls to: fprintf() */ /* exit() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ static char *longusemsg = "Usage: acpconfig interface [address] \n\ [-a ipaddr x25addr] : add a single IP to X.25 address translation (PDN only)\n\ [-A filename] : read IP to X.25 addr translations from file (PDN only)\n\ [-b baud] : set baud rate to baud. 0 designates external clocking\n\ [-c msgnum] : enable/disable driver console message\n\ [-d ipaddr] : delete an IP to X.25 translation (PDN only)\n\ [-D] : delete all IP to X.25 translations (PDN only)\n\ [-e size] : set front-end buffer size\n\ [-f facility status] : control initiation of flow control negotiation\n\ status is \"on\" or \"off\"; facility is \"packet\" or\n\ \"window\"\n\ [-h mode[r]] : print statistics on logical circuit usage\n\ mode is 0 (read) or 1 (read and reset); \"r\"\n\ specifies raw mode\n\ [-l[n]] : display status of each active logical channel\n\ the optional \"n\" argument will cause acpconfig\n\ [-m message] : send specified supervisor message\n\ [-n circuits] : set number of virtual circuits\n\ [-N net] : set network type; values for net are one of:\n\ transpac - the French Transpac network\n\ net15 - any network using 15 digit addresses\n\ default - standard network configuration\n\ [-o option] : set or disable extended clear handling; option is\n\ \"extended\" or \"none\"\n\ [-q type] : query driver or front end status; type is 0 for\n\ front end, 1 for driver\n\ [-r count] : read and print address translation table; count\n\ is number of translations to read; 0 means all\n\ [-s X.25_service] : specify DDN standard, DDN basic or PDN service;\n\ X.25_service is: \"standard\", \"basic\" or \"pdn\"\n\ [-t secs] : set the idle circuit timeout to secs seconds\n\ [-u mode] : set interface mode to:\n\ down - disable interface\n\ dte - online as DTE\n\ dce - online as DCE\n\ ext - external loopback\n\ int - internal loopback\n\ [-v variable value] : set driver variable; variable/values are one of:\n\ packet DDD - set X.25 packet size to DDD decimal\n\ window DDD - set X.25 window size to DDD decimal\n\ [-z] : reset the board\n"; long_usage() { fprintf(stderr, longusemsg); fprintf(stderr, "\nacpconfig generation %s\n", VERSION); exit(1); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PERROR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* print error message, based on errno */ /* */ /* Call: Perror(cmd) */ /* Arguments: cmd: error message */ /* Returns: nothing, exits if no internet address is set */ /* Called by: main() */ /* Calls to: fprintf() */ /* perror() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ Perror(cmd) char *cmd; { extern int errno; fprintf(stderr, "acpconfig: "); switch (errno) { case ENXIO: fprintf(stderr, "%s: ", cmd); fprintf(stderr, "no such interface\n"); break; case EPERM: fprintf(stderr, "permission denied\n"); break; case EDESTADDRREQ: fprintf(stderr, "%s: ", cmd); fprintf(stderr, "no internet address assigned to interface\n"); usage(); /* display proper syntax for user */ exit(1); break; case EINVAL: fprintf(stderr, "%s: ", cmd); fprintf(stderr, "invalid command\n"); break; case EINTR: fprintf(stderr, "%s: ", cmd); fprintf(stderr, "device not operational\n"); break; case EINPROGRESS: fprintf(stderr, "%s: ", cmd); fprintf (stderr, "Can't change parameters with link up\n"); fprintf (stderr, "Bring the link down and try again\n"); break; case ENOPROTOOPT: fprintf (stderr, "%s: ", cmd); fprintf (stderr, "Operation not supported by this version of the firmware.\n"); break; default: perror(cmd); } } /* * from SAGE::PROJECT:[X25.ACP5250W.HCOMMON]NCP.H on */ struct stat_rsp { u_char cmnd, path, var, count; /* message header */ u_long uptime; /* seconds since startup */ u_long elapsed; /* tenths since last statistics response */ u_long frames_sent; /* frames and... */ u_long bytes_sent; /* bytes transmitted */ u_long frames_rcvd; /* frames and... */ u_long bytes_rcvd; /* bytes received */ u_short SABMs_sent; /* similarly for SABMs, */ u_short SABMs_rcvd; u_short DISCs_sent; /* DISCs */ u_short DISCs_rcvd; u_short FRMRs_sent; /* FRMRs */ u_short FRMRs_rcvd; u_short RRs_sent; /* RRs */ u_short RRs_rcvd; u_short REJs_sent; /* REJs */ u_short REJs_rcvd; u_short RNRs_sent; /* RNRs */ u_short RNRs_rcvd; u_short crc_errors; /* CRC errors received */ u_short bad_frames; /* invalid frames received */ u_short rexmitted; /* I-frames RE-transmitted */ u_short UAs_sent; /* UAs transmitted, received */ u_short UAs_rcvd; u_short DMs_sent; /* DMs transmitted, received */ u_short DMs_rcvd; u_short I_frames_sent; /* I frames transmitted, received */ u_short I_frames_rcvd; }; /* * A long time ago in a UNIBUS far, far away, there was a Z80 based * front end processor happily talking to its PDP-11 host. They * exchanged many cheerful messages, some of which were statistics * queries. The statistics query was defined in the only logical * format, the format shared by both the Z80 and its PDP-11 host. * Well, thousands of computer generations passed, and the little * Z80 grew up into a big, strong 68000 based front end. His * friendly little PDP-11 host grew up too, into a big honkin' * VAX. They still liked to exchange cheerful messages, though, * and some of those were still the same well-worn statistics * queries, which are still in the only "logical" data format. * So don't ask again why a VAX and a 68000 exchange data in Z80 * byte ordering! */ #define z80toVAX(l) (((l) << 16) | (((l) >> 16) & 0xFFFF)) #ifdef __GNU__ /* gnu compiler prefers ANSII C */ #define pr(A) printf("%-10s%10d%10d\n", #A, sp->A##_sent, sp->A##_rcvd) #else /* idiot compiler */ #define pr(A) printf("%-10s%10d%10d\n", "A", sp->A/**/_sent, sp->A/**/_rcvd) #endif /* * internal routines for use by query commands */ fmtstats (buf) char *buf; { register struct stat_rsp *sp = (struct stat_rsp *) buf; struct timeval t; char *cp; sp->elapsed = z80toVAX (sp->elapsed); sp->uptime = z80toVAX (sp->uptime); sp->frames_sent = z80toVAX (sp->frames_sent); sp->frames_rcvd = z80toVAX (sp->frames_rcvd); sp->bytes_sent = z80toVAX (sp->bytes_sent); sp->bytes_rcvd = z80toVAX (sp->bytes_rcvd); (void)gettimeofday (&t, (struct timezone *)0); cp = ctime (&t.tv_sec); cp[24] = '\0'; /* get rid of the embedded newline */ printf ("\n----------------------------------------------\n"); printf ("Front End Processor statistics (host time: %s)\n", cp); t.tv_sec -= sp->uptime; cp = ctime (&t.tv_sec); cp[24] = '\0'; printf ("FEP up %d seconds (since %s)\n", sp->uptime, cp); t.tv_sec += sp->uptime; t.tv_sec -= sp->elapsed; cp = ctime (&t.tv_sec); cp[24] = '\0'; printf ("%d seconds since last statistics (at %s)\n", sp->elapsed, cp); /* ---- debugging stuff printf ("cmnd = %x path = %x var = %x count = %x\n", sp->cmnd, sp->path, sp->var, sp->count); ---- */ printf ("\n%20s%10s\n", "SENT", "RCVD"); pr (frames); pr (bytes); pr (I_frames); pr (SABMs); pr (DISCs); pr (FRMRs); pr (RRs); pr (REJs); pr (RNRs); pr (UAs); pr (DMs); printf ("\n%d CRC errors; %d bad frames; %d frames retransmitted\n", sp->crc_errors, sp->bad_frames, sp->rexmitted); printf ("--------------- END OF STATISTICS -----------------\n\n"); } /* * This is disgustingly ugly. The whole interface between acpconfig * and the driver has gotten completely out of hand. Has to do with * the fact that we want this program to be compatible with both * ULTRIX and BSD and they have different include file structures AND * historically many of the structures weren't even IN include files! * * buf: * dda_state, dda_init, dda_flags, dda_firmrev */ fmtmode (buf) char *buf; { static char *brd_modes[] = { "DISABLED", "COMING UP", "UP", "GOING DOWN" }; printf ("\n>>> Driver status for %s (Interface is %s) <<<\n", name, brd_modes[*buf & 3]); buf++; printf ("\tMode 0x%x ", (u_char)*buf); if (*buf & 0x1) printf ("<DDN_STANDARD>\n"); if (*buf & 0x2) printf ("<DDN_BASIC>\n"); if (*buf & 0x4) printf ("<PDN>\n"); if (*buf & 0x40) printf ("\t<Packet Size Facility Negotiation Initiation Enabled>\n"); if (*buf & 0x80) printf ("\t<Window Size Facility Negotiation Initiation Enabled>\n"); buf++; if (*buf == 0) printf ("\tWarning, DDAF_OK is OFF <--------------\n"); else printf ("\tInterface on-line (DDAF_OK is ON)\n"); buf++; printf ("\tFirmware revision %d.%d installed\n", (*buf >> 4) & 0xF, *buf & 0xF); buf++; printf ("*** End of driver status information ***\n\n"); } fmtsilo (buf) unsigned char *buf; { int i, j; for (j = 0; j < 256; j += 16) { for (i = 0; i < 16; i++) printf("%02x ", buf[j+i]); for (i = 0; i < 16; i++) putchar(isalpha(buf[j+i]) ? buf[j+i] : '.'); printf("\n");; } } /* print out data from the histogram request */ double tval_dbl(tv) struct timeval *tv; { return((double)tv->tv_sec+(double)tv->tv_usec / (double)1000000.0); } hist_data_out(times) struct timeval times[]; { register int i; double percents[HISTSIZE]; double totaltime; char *p, *p1; totaltime = tval_dbl(times+H_END) - tval_dbl(times+H_START); for(i = 0; i <= H_LINK_UP; i++) percents[i] = tval_dbl(times+i) / totaltime; p1 = p = ctime(×[H_START].tv_sec); while(*p) { if(*p == '\n') *p = ' '; *p++; } printf("START: %s",p1); p1 = p = ctime(×[H_END].tv_sec); while(*p) { if(*p == '\n') *p = ' '; *p++; } printf("\t\tEND: %s\n",p1); printf("total time: %6.2f seconds",totaltime); printf("\ttime up: %ld seconds (%3.2f%%)\n",times[H_LINK_UP].tv_sec, percents[H_LINK_UP]*100.0); printf("idle circuit timeout: %ld seconds\n",times[H_TMO].tv_sec); for(i = 0; i < H_LINK_UP; i++) { if(percents[i] <= 0.0) continue; printf("%3d %6.2f\n",i,percents[i]*100.0); } } /* Revision History: 9-AUG-1987 Steph Price V2.3 Add new -f option for controlling the initiation of flow control parameter negotiation. The default in the driver is no flow control parameter negotiation initiation. To turn on packet or window size negotiation initiation, issue the new acpconfig commands: -f packet on -f window on To turn off initiation: -f packet off -f window off Note that these options are valid only on a per call basis. If a circuit is already open for the requested destination, the status of flow control parameter negotiation initiation can't be altered until the circuit is cleared either by timing out or by a reset. Add "external" argument to the -b option. Add new arguments to the -s option. They are as follows: -s standard (equivalent to -s 0) -s basic (equivalent to -s 1) -s pdn (equivalent to -s 2) SERVICE_VAL was modified to accomodate the new arguments. Previously the driver expected arguments of s, t, and u to be passed for the -s 0, -s 1, and -s 2 options. Since an argument of v is already in use, use of the -s0/standard option will result in the use of 'S' as an argument to the driver's ioctl routine. Similarly, 'T', 'U', and 'V' will be passed for the -s1/basic, -s2/pdn, and -s3/class_b_c options. Add the -o option to enable root to select extended clear and extended clear confirmation packets as the first step toward X.25 1984 compatability. Add the -l option to enable root to display the status of each active lcn. Modify reset case to check for returned value before printing a message saying that a reset is in progress. Fix bug in -n case. The interface was not given. 25-OCT-87 Stephanie Price Modified -l option to used /dev/kmem instead of /dev/mem. Removed mask of high order bit in klseek() routine. 6-NOV-87 Brad Engstrom Add -D flag 11-NOV-87 Brad Engstrom Add -h and -H histogram flags 18-NOV-87 Brad Engstrom Changed -A option handling so that acpconfig does not exit upon seeing an address already in use error. 18-DEC-87 Brad Engstrom Added -t options to set the idle circuit timeout value to a certain number of seconds. 21-APR-88 Brad Engstrom Added the -v dbgunit option to the -v flag to set the debug unit variable in the driver. 22-APR-88 Brad Engstrom Added the -c command to enable or disable driver console messages. Each use of the command for a particular message will toggle the message status. 26-APR-88 Brad Engstrom Modified the -n command so that a command of the form "-n 0" will print the number of circuits currently available. Also the -l command uses this information to print the number of circuits free. This allows the new driver to work with old firmware easier. 10-MAY-88 Brad Engstrom Made all references to the length of an X.25 address refer to theo constants MAXADDRLEN and MINADDRLEN which are defined in if_ddavar.h. 26-JAN-89 Steve Johnson Added -N option to support TRANSPAC net. Merged in extra TWG support. 15-FEB-89 Paul Traina Fixed bug with -N command parsing. Implemented an improved form of Brad's changes for Transpac. 17-FEB-89 Paul Traina Installed Multinet support. 10-MAR-89 Paul Traina Installed simulation support. 13-MAR-89 Paul Traina Installed support for undocumented -q 2 command. 31-MAY-89 Paul Traina Updated command parsing on -u fixed documentation. 20-JUN-89 Paul Traina Obsoleted -v debug and -v dbg_unit commands. New driver is *NOT* compatible with the old crufty debugging format. 26-JUN-89 Paul Traina Removed the tolower()'s on -u and -N arguments. Reinstalled Charles' changes to 3.1 acpconfig for -ln Fixed things so -h w/o an argument doesn't core. 19-Jul-89 Paul Traina Changed version date id due to incompatible changes in the dc structure located in if_ddavar.h 13-Nov-89 Paul Traina Changed query ioctl interface. Incompatible with old drivers. End of Revision History */ idle circuit timeout: %ld seconds\n",times[H_TMO].tv_sec); for(i = 0; i < H_LINK_UP; i++) { if(percents[i] <= 0.0) continue; printf("%3d %6.2f\n",i,percents[i]*100.0); } } /* Revision History: 9-acpconfig/makefile 444 540 24 7104 4535335500 7537 ###################################################################### # # # # # Copyright (c) 1986 by Advanced Computer Communications # # 720 Santa Barbara Street, Santa Barbara, California 93101 # # (805) 963-9431 # # # # filename: makefile # # author: Clare E. Russ, ACC Santa Barbara # # purpose: create executable image of 'acpconfig' configuration # # program to run under UNIX (BSD, TWG, ULTRIX) # # # # use: # # By default, this makefile builds an executable image, # # acpconfig, for a BSD or ULTRIX system. If the TWG world # # is desired, it must be defined explicitly in the make # # command line. # # # # make (make acpconfig for BSD or ULTRIX) # # make bsd (make acpconfig for BSD or ULTRIX) # # make twg (make acpconfig for TWG under VAX/VMS) # # make ultrix (same as "make bsd", now) # # make clean (rm -f acpconfig) # # make print (print -nh acpconfig.c) # # # # date: V0.6 26 June 1986 Clare Russ # # add ULTRIX V1.2 and ULTRIX V2.0 support to account # # for differences in the internet address data structure # # in_addr (see /sys/netinet/in.h) # # # # V0.7 16 March 1987 Jeff Berkowitz # # Correct the V0.6 change; there is no reason to have an # # ifdef for ULTRIX versus BSD. # # V0.8 15 Sept 1987 Stephanie Price # # Give full pathname for in.h and if.h because 4.2BSD # # may not have the same symbolic links as 4.3BSD. # # # ###################################################################### # # # CFLAGS = -O NAME = acpconfig PRINT = print -nh RM = rm -f # # The COMMON_FILES are included regardless of the target environment. # COMMON_FILES = acpconfig.c \ /usr/include/sys/types.h \ /usr/include/sys/socket.h \ /usr/include/sys/ioctl.h \ /usr/include/stdio.h \ /usr/include/errno.h \ /usr/include/ctype.h \ /usr/include/netdb.h # # The BSD_FILES are included for the BSD and ULTRIX environments. # BSD_FILES = /sys/netinet/in.h \ /sys/net/if.h # # The TWG_FILES are only included for the TWG environment. # TWG_FILES = /sys/twgtcp/kernel/netinet/in.h \ /sys/twgtcp/kernel/net/if.h # # Compile an executable image for BSD (4.2 or 4.3) or ULTRIX (1.2 or 2.0) # bsd: ${COMMON_FILES} ${BSD_FILES} cc ${CFLAGS} -o ${NAME} ${NAME}.c # # This entry exists so we don't have to change the manual, which says # to type "make ultrix" # ultrix: bsd # # Compile an executable image for TWG. Note the -DTWG flag. # twg: ${COMMON_FILES} ${TWG_FILES} cc -DTWG ${CFLAGS} -o ${NAME} ${NAME}.c clean: ${RM} ${NAME} print: ${PRINT} ${NAME}.c rint (print -nh acpconfig.c) # # # # date: V0.6 26 June 1986 Clare Russ # # add ULTRIX V1.2 and ULTRIX V2.0 support to account # # for differences in the internet address data structure # # in_addr (see /sys/netinet/in.h) # # conf/ 755 540 24 0 4535574763 4766 conf/ddn 555 540 24 712 4535335503 5507 #! /bin/csh -f # configure ACP5250/6250 for connection to PSN set UNIT=0 # change to suit your unit number set INET_ADDR=X.X.X.X # change to your site's internet address alias acp /etc/acpconfig dda$UNIT acp $INET_ADDR acp -u down # required only if interface was enabled acp -b external # EXTERNAL clocking; change if necessary acp -s standard # DDN standard mode acp -f window on -f packet on # enable negotiation acp -u dte # normal (DTE) mode ######################################################conf/ddn_external 555 540 24 715 4535335504 7415 #! /bin/csh -f # configure ACP5250/6250 for external loopback (using loopback connector) set UNIT=0 # change to suit your unit number set INET_ADDR=X.X.X.X # change to your site's internet address alias acp /etc/acpconfig dda$UNIT acp $INET_ADDR acp -u 0 # required only if interface was enabled acp -b 9600 # set internal clocking acp -s standard # DDN standard mode acp -f window on -f packet on # enable negotiation acp -u ext # external loopback ###################################################conf/ddn_internal 555 540 24 724 4535335515 7411 #! /bin/csh -f # configure ACP5250/6250 for internal loopback (bypassing serial interface) set UNIT=0 # change to suit your unit number set INET_ADDR=X.X.X.X # change to your site's internet address alias acp /etc/acpconfig dda$UNIT acp $INET_ADDR acp -u 0 # required only if interface was enabled # acp -b 0 # clock source doesn't matter acp -s standard # DDN standard mode acp -f window on -f packet on # enable negotiation acp -u int # internal loopback ############################################conf/pdn 555 540 24 1443 4535335504 5546 #! /bin/csh -f # configure ACP5250/6250 for connection to public data network set UNIT=0 # if necessary, change to suit your system set INET_ADDR=X.X.X.X # change to your site's internet address set X25_ADDR=XXXXXXXXXXXXXX # (optionally) change to your X.25 address set ADDRFILE=/etc/addresses # set name of address translation table if # one exists alias acp /etc/acpconfig dda$UNIT acp $INET_ADDR acp -u down # required only if interface was enabled acp -b external # clock supplied by modem acp -o extended # enable extended clear format (Telenet) acp -a $INET_ADDR $X25_ADDR # enter ourselves in translation table if (-r $ADDRFILE) then acp -A $ADDRFILE # read addresses if file exists and readable endif acp -s pdn # PDN mode acp -u dte # DTE o.h \ /usr/include/errno.h \ /usr/include/ctype.h \ /usr/include/netdb.h # # The BSD_FILES are included for the BSD and ULTRIX environments. # BSD_FILES = /sys/netinet/in.h \ /sys/net/if.h # # The TWG_FILES conf/pdn_external 555 540 24 1043 4535335505 7445 #! /bin/csh -f # configure ACP5250/6250 for external loopback (using loopback connector) set UNIT=0 # if necessary, change to suit your system set INET_ADDR=X.X.X.X # change to your site's internet address set X25_ADDR=11223344556677 # (optionally) change to your X.25 address alias acp /etc/acpconfig dda$UNIT acp $INET_ADDR acp -u down # required only if interface was enabled acp -b 9600 # set internal clocking acp -a $INET_ADDR $X25_ADDR # enter ourselves in translation table acp -s pdn # PDN mode acp -u ext # external loopback 6250 for external loopback (using loopback connector) set UNIT=0 # if necessary, change to suit your system set INET_ADDR=X.X.X.X # change to your site's internet address set X25_ADDR=11223344556677 # (optionally) change to your X.25 address alias acp /etc/acpconfig dda$UNIT acp $INET_ADDR acp -u down # required only if interface was enabled acp -b 9600 # set internal clocking acp -a $INET_ADDR $X25_ADDR # enter ourselves in translation table acp -s pdn # PDN modconf/pdn_internal 555 540 24 777 4535335505 7434 #! /bin/csh -f # configure ACP5250/6250 for internal loopback (bypassing serial interface) set UNIT=0 # if necessary, change to suit your system set INET_ADDR=X.X.X.X # change to your site's internet address set X25_ADDR=11223344556677 # (optionally) change to your X.25 address alias acp /etc/acpconfig dda$UNIT acp $INET_ADDR acp -u down # required only if interface was enabled acp -a $INET_ADDR $X25_ADDR # enter ourselves in translation table acp -s pdn # PDN mode acp -u int # internal loopback dconf/MAKEDEV.acc 555 540 24 3032 4535335471 6565 #!/bin/sh - # # @(#)MAKEDEV.local 1.00 (ACC) 5/19/89 # # Terminal multiplexors: # xx* unibus acp5250/6250 with x29 option # Special purpose devices: # pi* unibus acp5250/6250 with programmers interface option # X29_MAJOR=60 XPI_MAJOR=61 umask 77 for i do case $i in local) echo "acceptable arguments are pi, xt, and pad" ;; xt*|pad*) case $i in pad*) name=pad ;; xt*) name=xt ;; esac unit=`expr $i : "$name\(.*\)"` if [ ! -f x29show ] ; then /etc/mknod x29show c $X29_MAJOR 255 fi case $unit in 0) ch=J ;; 1) ch=K ;; 2) ch=L ;; 3) ch=M ;; 4) ch=N ;; 5) ch=O ;; 6) ch=P ;; 7) ch=Q ;; *) echo bad unit for $name in: $i ;; esac count=16 ; eval `echo $ch $unit $X29_MAJOR $count $name | awk ' { ch = $1; u = $4 * $2; m = $3; cnt = $4; n=$5 } END { for (i = 0; i < cnt; i++) if (n == "pad") if (i < 10) printf("/etc/mknod pad%s%x c %d %d; ",ch,i,m,128+u+i); else printf("/etc/mknod pad%s%c c %d %d; ",ch,87+i,m,128+u+i); else if (i < 10) printf("/etc/mknod tty%s%x c %d %d; ",ch,i,m,u+i); else printf("/etc/mknod tty%s%c c %d %d; ",ch,87+i,m,u+i); }'` ;; pi*) class=`expr $i : 'pi\(.*\)'` case $class in 0) offset=0 name=a;; 1) offset=32 name=b;; 2) offset=64 name=c;; 3) offset=96 name=d;; *) echo bad unit for pi in: $i;; esac case $class in 0|1|2|3) umask 0 eval `echo $offset $name $XPI_MAJOR | awk ' { b=$1; n=$2; m=$3 } END { for (i = 0; i < 32; i++) printf("/etc/mknod pi%s%02d c %d %d; ", \ n, i, m, b+i); }'` umask 77 ;; esac ;; esac done ${RM} ${NAME} print: ${PRINT} ${NAME}.c rint (print -nh acpconfig.c) # # # # date: V0.6 26 June 1986 Clare Russ # # add ULTRIX V1.2 and ULTRIX V2.0 support to account # # for differences in the internet address data structure # # in_addr (see /sys/netinet/in.h) # # conf/pitest.c 444 540 24 23170 4535335503 6533 /* pitest.c V1.0 30 Aug 1988 */ /*************************************************************************/ /* */ /* */ /* ________________________________________________________ */ /* / \ */ /* | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* \________________________________________________________/ */ /* */ /* Copyright (c) 1988 by Advanced Computer Communications */ /* 720 Santa Barbara Street, Santa Barbara, California 93101 */ /* (805) 963-9431 */ /* */ /* */ /* File: pitest.c */ /* */ /* Author: Brad Engstrom */ /* */ /* Project: Installation verification program for ACC */ /* ACP 5250/6250 Programmers Interface. */ /* */ /* Function: */ /* This program fork a child process, establishes an X.25 connection*/ /* with it and sends data packets to the child. The child echoes */ /* the packets back to the sender. The sender verifies the packets. */ /* The program will print PASSED or FAILED to indicate the results */ /* of the test. */ /* */ /* Components: pitest.c, /sys/vaxif/if_pivar.h */ /* */ /* Revision History: */ /* */ /* 30-AUG-1988 Brad Engstrom: first generated. */ /*************************************************************************/ #include <stdio.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/file.h> #include <signal.h> #include <ctype.h> #include "/sys/vaxif/if_pivar.h" #define PIPROTOCOL 7 #define CALL 0x0 #define ANSWER 0x3 #define RING 0x1 #define CLEAR 0x4 /* PREDEFINED SUPERVISORY MESSAGES */ u_char callmsg[] = {CALL, 0x4, 0x0, 0x22, 0xe, 3, 1, 1, 0, 8, 0, 5, 0, 0, 1, 0, 1, 1, 1, 0xe, 3, 1, 1, 0, 8, 0, 5, 0, 0, 1, 0, 1, 1, 1, 1, PIPROTOCOL, 0, 0}; u_char clearmsg[] = {CLEAR, 0x0, 0, 1, 0}; #define DATABUFSIZE 1024 u_char databuffer[DATABUFSIZE]; u_char readbuffer[DATABUFSIZE]; #define PACKETSTOSEND 75 main (argc, argv) int argc; char *argv[]; { int fd1, fd2; if (argc != 3) { fprintf (stderr, "Usage: pitest xxdev1 xxdev2\n"); return (1); } if ((fd1 = open (argv[1], O_RDWR)) == -1) error (argv[0]); if ((fd2 = open (argv[2], O_RDWR)) == -1) error (argv[0]); if (fork ()) { caller (fd1); fprintf(stderr,"pitest: PASSED\n"); } else callee (fd2); return (0); } caller (fd) int fd; { int lcn, i; struct pi_dblock pd, rd; caddr_t cp; char c; sleep (5); /* give callee chance to set up */ /* * RESERVE THE LOGICAL CIRCUIT TO USE */ if (ioctl (fd, XIOGETLCN, &lcn) == -1) error ("caller: XIOGETLCN"); lcn &= 0xff; /* * MAKE THE CALL */ fprintf(stderr,"Making X.25 call...\n"); callmsg[1] = lcn * 2; pd.dataptr = (caddr_t) callmsg; pd.length = sizeof (callmsg); pd.lcn = 0; pd.subfunc = 0; pd.flags = 0; if (ioctl (fd, XIOWRITE, &pd) == -1) error ("caller: XIOWRITE"); /* * READ AND CHECK THAT WE GOT AN ANSWER MESSAGE */ pd.dataptr = (caddr_t) databuffer; pd.length = DATABUFSIZE; pd.flags = 0; if (ioctl (fd, XIOREAD, &pd) == -1) error ("caller: XIOREAD"); if (pd.lcn != 0 || pd.dataptr[0] != ANSWER) { fprintf (stderr, "caller: did not get answer message\n"); exit (1); } fprintf(stderr,"X.25 connection established\n"); /* * SEND 25 DATA PACKETS, EXPECT THEM TO BE ECHOED */ fprintf(stderr,"Sending and verifying data packets\n"); pd.dataptr = (caddr_t) databuffer; pd.subfunc = 0; pd.flags = 0; pd.lcn = lcn; /* fill buffer with data */ for (cp = pd.dataptr, c = '0'; cp < pd.dataptr + DATABUFSIZE; cp++) { *cp = c; if (++c > '9') c = '0'; } srandom (getpid ()); for (i = 0; i < PACKETSTOSEND; i++) { pd.length = random () & 0x3ff; if (pd.length == 0) pd.length = 1; if (ioctl (fd, XIOWRITE, &pd) == -1) error ("caller: XIOWRITE"); rd.dataptr = (caddr_t) readbuffer; rd.length = DATABUFSIZE; rd.flags = 0; if (ioctl (fd, XIOREAD, &rd) == -1) error ("caller: XIOREAD"); if (rd.lcn != lcn) { fprintf (stderr, "caller: mismatched lcn (%d != %d)\n",rd.lcn,lcn); exit (1); } if (rd.length != pd.length) { fprintf (stderr, "caller: mismatched length\n"); exit (1); } if (bcmp (pd.dataptr, rd.dataptr, rd.length)) { fprintf (stderr, "caller: data does not match\n"); exit (1); } fprintf(stderr,"+"); } fprintf(stderr,"\n"); /* * CLEAR THE CALL. */ clearmsg[1] = lcn * 2; pd.dataptr = (caddr_t) clearmsg; pd.length = sizeof (clearmsg); pd.lcn = 0; pd.subfunc = 0; pd.flags = 0; if (ioctl (fd, XIOWRITE, &pd) == -1) error ("caller: XIOWRITE"); /* * WAIT FOR CLEAR CONFIRMATION */ for (;;) { pd.dataptr = (caddr_t) databuffer; pd.length = DATABUFSIZE; pd.flags = 0; if (ioctl (fd, XIOREAD, &pd) == -1) error ("caller: XIOREAD"); if (pd.lcn == 0 && pd.dataptr[0] == CLEAR) break; else { fprintf (stderr, "caller: packet was not a clear\n"); exit(1); } } /* * FREE THE LOGICAL CHANNEL AND RESOURCES */ if (ioctl (fd, XIOFREELCN, &lcn) == -1) error("caller: XIOFREELCN"); /* * CLOSE THE DEVICE */ close (fd); } callee (fd) int fd; { struct pi_dblock pd, rd; proto_range pr; int lcn, i; /* * RESERVE THE LOGICAL CIRCUIT TO USE */ if (ioctl (fd, XIOGETLCN, &lcn) == -1) error ("callee: XIOGETLCN"); lcn &= 0xff; /* * TELL THE DRIVER WHICH PROTOCOLS THIS CHANNEL WILL ACCEPT */ pr.lower = PIPROTOCOL; pr.upper = PIPROTOCOL; if (ioctl (fd, XIOACCRING, &pr) == -1) error ("callee: XIOACCRING"); /* * WAIT FOR A RING TO COME IN AND THEN ANSWER IT. */ for (;;) { pd.dataptr = (caddr_t) databuffer; pd.length = DATABUFSIZE; pd.flags = 0; if (ioctl (fd, XIOREAD, &pd) == -1) error ("XIOREAD"); if (pd.lcn == 0 && pd.dataptr[0] == RING) { databuffer[0] = ANSWER; databuffer[1] = lcn * 2; /* databuffer[2] already has data */ databuffer[3] = 6; databuffer[4] = 0; /* length of called addr */ databuffer[5] = 0; /* length of calling addr */ databuffer[6] = 1; /* length of protocol */ databuffer[7] = PIPROTOCOL; /* use protocol 7 */ databuffer[8] = 0; /* length of facilities */ databuffer[9] = 0; /* length of user data */ pd.length = 10; pd.flags = 0; pd.lcn = 0; pd.subfunc = 0; if (ioctl (fd, XIOWRITE, &pd) == -1) error ("XIOWRITE"); break; } else fprintf (stderr, "callee: packet was not a ring\n"); } /* * ECHO 25 PACKETS */ for (i = 0; i < PACKETSTOSEND; i++) { rd.dataptr = (caddr_t) readbuffer; rd.length = DATABUFSIZE; rd.flags = 0; if (ioctl (fd, XIOREAD, &rd) == -1) error ("callee: XIOREAD"); if (rd.lcn == 0) { fprintf (stderr, "callee: unexpected supervisor message\n"); exit (1); } if (ioctl (fd, XIOWRITE, &rd) == -1) error ("callee: XIOWRITE"); } /* * WAIT FOR THE CLEAR */ for (;;) { pd.dataptr = (caddr_t) databuffer; pd.length = DATABUFSIZE; pd.flags = 0; if (ioctl (fd, XIOREAD, &pd) == -1) error ("callee: XIOREAD"); if (pd.lcn == 0 && pd.dataptr[0] == CLEAR) break; else { fprintf (stderr, "callee: packet was not a clear\n"); exit(1); } } /* * SEND A CLEAR CONFIRMATION */ pd.dataptr[1] = lcn * 2; /* patch up clear message and send it back */ if (ioctl (fd, XIOWRITE, &pd) == -1) error ("callee: XIOREAD"); /* * FREE THE LOGICAL CHANNEL AND RESOURCES */ if (ioctl (fd, XIOFREELCN, &lcn) == -1) error("callee: XIOFREELCN"); /* * CLOSE THE DEVICE */ close (fd); } error(s) char *s; { perror(s); fprintf("pitest: FAILED\n"); exit(1); } ength = 1; if (ioctl (fd, XIOWRITE, &pd) == -1) error ("caller: XIOWRITE"); rd.dataptr = (caddr_t) readbuffer; rd.length = DATABUFSIZE; rd.flags = 0; if (ioctl (fd, XIOREAD, &rd) == -1) error ("caller: XIOREAD"); if (rd.lcn != lcn) { fprintf (stderr, "caller: mismatched lcn (%d != %d)\n",rd.lcn,lcn); exit (1); } if (rd.length != pd.length) { fprintf (stdeconf/bi_data.diff 444 540 24 1667 4535573571 7274 *** bi_data.c.orig Thu Nov 30 15:54:32 1989 --- bi_data.c Thu Nov 30 15:54:31 1989 *************** *** 170,175 **** --- 170,189 ---- #define bvpsspinit binotconf #endif + #include "dda.h" + #if NDDA > 0 + int ddaprobe(); + #else + #define ddaprobe binotconf + #endif + + #include "acp.h" + #if NACP > 0 + int acpprobe(); + #else + #define acpprobe binotconf + #endif + int nNVAXBI = NVAXBI; #if NVAXBI > 0 *************** *** 186,191 **** --- 200,206 ---- int (*cibcaprobes[])() = { biciinit,0}; int (*lxprobes[])() = { lx_init,0}; int (*xbibprobes[])() = {xbibinit,0}; + int (*acp7kprobes[])() = { ddaprobe, acpprobe, 0}; struct bisw bisw [] = { *************** *** 235,240 **** --- 250,258 ---- { BI_XBI, "xbib", xbibprobes, nobireset, BIF_NOCONF|BIF_SET_HEIE}, + { BI_ACP7K, "acp7000", acp7kprobes, nobireset, + BIF_DEVICE}, + { 0 } }; int nbitypes = sizeof (bisw) / sizeof (bisw[0]); 25 PACKETS */ for (i = 0; i < PACKETSTOSEND; i++) { rd.daconf/bireg.diff 444 540 24 1014 4535573571 6763 *** bireg.h.orig Thu Nov 30 15:54:10 1989 --- bireg.h Thu Nov 30 15:54:10 1989 *************** *** 207,213 **** #define BI_BDA 0x0000010e #define BI_MEM1 0x00000001 ! /* BI control register */ #define BICTRL_BIICREV 0xff000000 /* BI interface chip revision */ --- 207,213 ---- #define BI_BDA 0x0000010e #define BI_MEM1 0x00000001 ! #define BI_ACP7K 0x00008127 /* ACC ACP7000 hardware platform */ /* BI control register */ #define BICTRL_BIICREV 0xff000000 /* BI interface chip revision */ xprobes[])() = { lx_init,0}; int (*xbibprobes[])() = {xbibinit,0}; + int (*acp7kprobes[])() = { ddaprobe, acpprobe, 0}; struct bisw bisw [] = { *************** *** 235,240 **** --- 250,258 ---- { BI_XBI, "xbib", xbibprobes, nobireset, BIF_NOCONF|BIF_SET_HEIE}, + { BI_ACP7K, "acp7000", acp7kprobes, nobireset, + BIF_DEVICE}, + { 0 } }; int nbitypes = sizeof (bisw) / sizeof (bisw[0]); 25 PACKETS */ for (i = 0; i < PACKETSTOSEND; i++) { rd.daconf/README 644 540 24 1170 4535575121 5717 Some of the files in this directory: ddn -- example acpconfig startup scripts for ddn or pdn ddn_external mode IP links ddn_internal pdn pdn_external pdn_internal CONFIG -- modifcations for your config file in /sys/conf files.vax -- modifications to /sys/conf/files.vax MAKEDEV.acc -- make X.29 and PI devices in /dev directory conf.c -- changes to /sys/machine/conf.c for X.29 and PI pitest.c -- test the programmer's interface These files are only needed with the ACP 7000 series product line: bi_data.diff -- patches to Ultrix 3.0 /sys/data/bi_data.c bireg.diff -- patches to Ultrix 3.0 /sys/vaxbi/bireg.h acpprobe, 0}; struct bisw bisw [] = { *************** *** 235,240 **** --- 250,258 ---- { BI_XBI, "xbib", xbibprobes, nobireset, BIF_NOCONF|BIF_SET_HEIE}, + { BI_ACP7K, "acp7000", acp7kprobes, nobireset, + BIF_DEVICE}, + { 0 } }; int nbitypes = sizeof (bisw) / sizeof (bisw[0]); 25 PACKETS */ for (i = 0; i < PACKETSTOSEND; i++) { rd.daconf/CONFIG 644 540 24 1001 4535573245 5725 For an Ultrix 3.0 system, add the appropriate lines to the configuration file before the "config" line. # We're running Ultrix 3.0 options ACC_ULTRIX=30 # Enable the PAD (X.29 interface) options DDA_PADOPT # Enable the RAW (programmers interface) options DDA_RAWOPT After the "config" line, near the end of the device entries, add the following line (assuming you have one 6250/5250 to be installed in the default location): device dda0 at uba0 csr 0167000 vector ddainta ddaintb For an Ultrix 3.0 system, add the appropriate lines to the configuration file before the "config" line. # We're running Ultrix 3.0 options ACC_ULTRIX=30 # Enable the PAD (X.29 interface) options DDA_PADOPT # Enable the RAW (programmers interface) options DDA_RAWOPT After the "config" line, near the end of the device entries, add the following line (assuming you have one 6250/5250 to be installed in the default location): device dda0 at uba0 csr 0167000 vector ddainta ddaintb conf/conf.c 644 540 24 3506 4535573245 6142 If you have either the PI or the X.29 (DDA_RAWOPT or DDA_PADOPT) defined, you will have to add new device driver entries into the file /sys/vax/conf.c. The following example is for an Ultrix 3.0 system, the similar changes are necessary for Ultrix 2.n and 4.3bsd systems. If you are not using DDA_RAWOPT and not using DDA_PADOPT, there is no need to modify conf.c. Someplace after the definiton of nulldev() and nodev() enter the following preamble: #include "dda.h" #if (NDDA > 0) && defined(DDA_PADOPT) int xxopen(), xxclose(), xxread(), xxwrite(); int xxioctl(), xxreset(), xxstop(), xxselect(); struct tty xx_tty[]; #else #define xxopen nodev /* ACP 5250/6250 x29 interface */ #define xxclose nodev #define xxread nodev #define xxwrite nodev #define xxioctl nodev #define xxreset nulldev #define xxstop nodev #define xxselect nodev #define xx_tty NULL #endif #if (NDDA > 0) && defined(DDA_RAWOPT) int piopen(), piclose(), piioctl(); #else #define piopen nodev /* ACP 5250/6250 pi interface */ #define piclose nodev #define piioctl nodev #endif At the end of the cdevsw table (character device dispatch table), add the following two entries: /* ACP5250/6250 x29 interface */ xxopen, xxclose, xxread, xxwrite, /*??*/ xxioctl, xxstop, xxreset, xx_tty, xxselect, nodev, 0, /* ACP5250/6250 programmers interface */ piopen, piclose, nodev, nodev, /*??*/ piioctl, nodev, nulldev, NULL, nodev, nodev, 0, For the sake of sanity, enter the major device numbers for these devices in the "??" spaces after each entry. For example (again, Ultrix 3.0), if the last device is 59, the X.29 interface is accessed via major # 60, and the X.29 interface is accessed via major # 61. You should also edit the file MAKEDEV.acc that accompanied this distribution so that it will contain the proper major numbers for each of these interfaces. error ("callee: XIOREAD"); /* * FREE THE LOGICAL CHANNEL AND RESOURCES */ if (ioctl (fd, XIOFREELCN, &lcn) == -1) error("callee: XIOFREELCN"); /* *conf/files.vax 644 540 24 416 4535573245 6650 For Ultrix (versions 2.0, 2.2, 3.0): Add the following line to the file /sys/conf/files.vax: vaxif/if_dda.c optional dda device-driver Notbinary For Berkeley UNIX, add the following line to the file /sys/conf/files/vax: vaxif/if_dda.c optional dda device-driver ems. If you are not using DDA_RAWOPT and not using DDA_PADOPT, there is no need to modify conf.c. Someplace after the definiton of nulldev() and nodev() enter the following preamble: #include "dda.h" #if (NDDA > 0) && defined(DDA_PADOPT) doc/ 755 540 24 0 4535335726 4600 doc/dda.4 444 540 24 56745 4535335500 5527 .TH DDA 4 "19 July 1989" .nh .UC 4 .ds ]W "4.3 BSD .SH NAME dda \- ACC ACP 5250/6250 Network Interface Driver for X.25 .SH SYNOPSIS .B "optional device-driver dda" .br .B "options ACC_BSD=43" .br .B "device dda0 at uba0 csr 0167000 flags 0 vector ddainta ddaintb" .SH DESCRIPTION The .I dda device supports virtual circuits between the ACP 5250/6250 and the DDN IMP for carrying IP datagrams. The dda driver may also be configured to support IP traffic on X.25 Public Data Networks. The ACP 5250/6250 is a microprocessor-based front end that provides X.25 capability. Via the ACC supplied ACPCONFIG(8C) program, the user can set the device's internet address, loopback state, baud rate, and service mode (DDN standard, DDN basic, or PDN X.25). By default, the driver supports DDN standard X.25 service. .SH CONFIGURATION In previous versions, the .I dda driver configured itself to operate with whatever version of Unix, Ultrix, or VMS it was compiled under. However, since these distinctions have become rather muddy, the driver requires an additional "options" command line in the system's kernel configuration file. Only one ACC_<operating system> option line is required. The following lines may be used: .PP .nf .ta .5i 2.5i options ACC_BSD=43 4.3bsd or 4.3-tahoe system options ACC_BSD=42 4.2bsd options ACC_ULTRIX=30 Ultrix 3.0 options ACC_ULTRIX=22 Ultrix 2.2 options ACC_ULTRIX=20 Ultrix 2.0 options ACC_ULTRIX=12 Ultrix 1.2 options ACC_VMS=4n VAX/VMS 4.n (Multinet or WIN/TCP) options ACC_VMS=5n VAX/VMS 5.n (Multinet or WIN/TCP) .fi .PP In addition to the operating system definitons, if either the X.29 or PI options are installed, they may be enabled using: .PP .nf .ta .5i 2.5i options DDA_RAWOPT enables the raw "programmer's interface" options DDA_PADOPT enables the X.29 pad/host option .fi .PP Debugging code is normally unused in a customer environment, but some sites may find it useful when using the programmer's interface or when working on site dependant modifications to the driver. If you wish to enable the debugging code, add the line: .PP .nf .ta .5i 2.5i options DDADEBUG .fi .SH DIAGNOSTICS The diagnostic messages below refer to concepts that are explained further in the ACP 5250/6250 Hardware Installation and User Guide. The content of the messages ranges from status information to notification of unrecoverable errors. The notation is based on the C Programming Language's formatted output conventions where %d represents decimal notation and %x represents unsigned hexadecimal notation. The messages preceded by numbers in square brackets may be disabled via the acpconfig -c option, specifying the message number; the numbers are not displayed as part of the message. These messages are normally enabled. .sp .PP The following messages are informational only; no action need be taken. .PP [0] .BR "dda%d: reset" .br This message indicates reset of the front end, in response to a UNIBUS/Qbus reset, or an ACPCONFIG command invoked by the user (acpconfig dda0 -z). After a bus reset, if the interface is on the specified bus adapter, its state is reset. There is a pause to allow the powerup diagnostics to run for a short period of time. If the buffer size has been altered via the acpconfig -e command, it reverts to the value set in the on-board switches. .PP [38] .BR "dda%d: buffer size reset" .br This message indicates that the front end is being reset to change its buffer size, in response to the acpconfig -e command. .PP [14] .BR "lcn=%d func=%x" .br This message provides the logical channel number and the function code for the errors reported by messages 7 through 13. .PP [29] .BR "dda%d: link disabled" .br This message indicates that the interface has been disabled, in response to the -u 0 option to ACPCONFIG. .PP [31] .BR "dda%d: Restart (%x %x) received" .br [31] .BR "dda%d: Restart Ack Received" .br These messages indicate receipt of an X.25 Restart Indication or Restart Confirmation packet; these packets are used to initialize the packet level protocol. The numbers in parentheses are the Cause and Diagnostic fields from the Restart Indication packet. See CCITT Recommendation X.25 for an explanation of cause and diagnostic codes. .PP [32] .BR "dda%d: (ACP6250 rev %d.%d) link up" .br [32] .BR "dda%d: (ACP5250 rev %d.%d) link up" .br This message indicates that the interface is ready to make and accept calls (X.25 frame level is up and Restart packets have been exchanged). The board type (ACP6250/ACP5250) and firmware revision level are indicated in the message. The front end will not attempt to bring the link up until instructed to do so via the -u option to ACPCONFIG. If this message is displayed when the link is already up, it indicates that the link has been reset. .PP [258] .BR "dda%d: Accepting call from %d.%d.%d.%d (%s) on lcn %d .br This message is disabled by default. The message reports the internet address and X.25 address of the host originating the call, and the logical channel number used by the front end, when the driver accepts an incoming call from the network. .PP [258] .BR "dda%d: Calling %d.%d.%d.%d (%s) on lcn %d .br [258] .BR "dda%d: lcn %d connected .br These messages are disabled by default. The first message reports the internet address (%d.%d.%d.%d) and X.25 address (%s) of the destination host, and the logical channel number used by the front end, when the driver initiates a call to the network. The second message indicates that the call has been successfully established. (If the call fails, message 33 is displayed, reporting the reason for the failure). .PP [258] .BR "dda%d: Cleared lcn %d .br This message is disabled by default. It indicates that the driver has closed a circuit after it has been idle for the period defined by the "idle timeout". .PP [258] .BR "dda%d: Cleared lcn %d to %d.%d.%d.%d (%x %x)" .br This message is disabled by default. A circuit was cleared by the remote host or the network; the Clearing Cause and Diagnostic fields are displayed in parentheses. See CCITT Recommendation X.25 for an explanation of cause and diagnostic codes. .PP [256] .BR "dda%d: abort completed on chan %d .br This message is disabled by default. An outstanding read or write to the indicated logical channel has been aborted; this occurs when the circuit is cleared or reset. .sp .PP The following messages are warnings of potential problems. .PP [27] .BR "dda%d: supervisory channel overflow (maxlen=%d)" br The output queue for the supervisory path has reached its maximum length. The queue length for the supervisory path is doubled; the supervisory message is not dropped. If this message is repeated, the front end may have stopped accepting commands from the host, in which case the front end should be reset; however, this message may also be displayed during error recovery if a large number of circuits is opened or closed. .PP [28] .BR "dda%d: truncated supervisor message" .br A message from the front end over the supervisor path exceeded the maximum anticipated length; the partial message is discarded. .PP [30] .BR "dda%d: link down" .br This message indicates that the front end has reported the link down, because it has not received any response to its messages, because a protocol error has occurred, or because the network interface has requested that the link be disconnected. If the link has not yet been reported up, this may indicate that the configuration parameters for the front end and the network interface are incompatible (i.e., both declared as DCE, or clock source is incorrectly specified). The front end will continue to attempt to bring the link up, unless the user disables the link with the -u 0 option to ACPCONFIG. .PP [33] .BR "dda%d: Call to %d.%d.%d.%d on lcn %d failed (%x %x)" The front end has placed a call to the X.25 address associated with the IP address displayed in the message; the network has refused the call with the Clearing Cause and Diagnostic reported in parentheses. See CCITT Recommendation X.25 for an explanation of cause and diagnostic codes. .PP [34] .BR "dda%d: X25 RESET (%x %x) on lcn %d: %d.%d.%d.%d" .br An X.25 RESET supervisor message was received from the front end. The Resetting Cause and Diagnostic fields are displayed, as well as the channel number and the internet address of the remote host. This message may indicate that the front end and network interface configurations are incompatible (incorrect packet size or window size, for instance). See CCITT Recommendation X.25 for an explanation of cause and diagnostic codes. .PP [35] .BR "dda%d: X25 INTERRUPT (%x) on lcn %d: %d.%d.%d.%d" .br An X.25 INTERRUPT supervisor message was received from the front end. The Interrupt User Data is displayed, as well as the channel number and the internet address of the remote host. .PP [36] .BR "dda%d: supervisor error (%x %x %x %x)" .br A message was received over the supervisor channel which is not recognized by the driver. The first four bytes of the message are displayed. .PP [37] .BR "dda%d: Clear request lost -- lcn %d" .br The driver has attempted to close an circuit, but the request was not acknowledged before the clear timeout expired. The lcn will remain inactive until the clear request is acknowledged, Restart packets are exchanged, or the interface is reset. .PP [38] .BR "dda%d: make_x25_call message too large for mbuf (%d bytes)" .br The driver attempted to make a call, but there was too much data to be passed into the calling mbuf. This usually occurs because of a .ul user data field that is too large. Attempt the call again with less information in the user data field. (Note: this should not occur except when an X.29 call is being placed). .PP [258] .BR "dda%d: Rejecting call from %s on VC 0x%x .br An incoming call from the X.25 address shown (%s) is being rejected because the front end does not have an available channel on which to accept it. .PP [258] .BR "dda%d: Network cleared VC %x (%x %x) .br It should follow the "Rejecting call" message, and indicates that the specified virtual circuit is again ready for use. .PP [257] .BR "dda%d: all circuits in use" .br While attempting to open a new circuit, the driver has discovered that all circuits are active. This can be prevented by increasing the number of circuits available (if possible), or by reducing the amount of time an idle circuit will be left open (via the -t option to ACPCONFIG). This message may be suppressed by clearing the LOG_BUSY bit in the dda_logger variable with the -v option to ACPCONFIG. .PP [257] .BR "dda%d: no circuits available" .br While attempting to open a new circuit, the driver has discovered that no circuits are available; this is usually because the link is being reset. This message may be suppressed by clearing the LOG_BUSY bit in the dda_logger variable with the -v option to ACPCONFIG. .sp .PP The following messages report errors affecting only a single message; the message is discarded. .PP [2] .BR "dda%d: can't handle af%d" .br The driver was requested to transmit a datagram with an address family other than AF_INET (internetwork: UDP, TCP, etc.). The datagram is discarded. .PP [10] .BR "dda%d: DMA completion error (%x) lcn=%d func=%x" .br An I/O interrupt from the ACP 5250/6250 has occurred; the I/O completion status indicates that its DMA controller detected an error. The value in parentheses is the contents of the channel error register, which indicates what type of error occurred. This message may result from a software error (invalid address passed to the front end), or from a hardware error (bus timeout or other bus error). The lcn and function codes are printed only if message 14 is enabled. .PP [22] .BR "dda%d: couldn't get mbuf for call command." .br The driver was given a datagram addressed to a destination to which no circuit is open, and was unable to allocate the mbuf needed to place a call. The datagram is discarded. .PP [26] .BR "dda: couldn't get buffer for ifp header" .br The driver received a datagram, but was unable to allocate the mbuf needed for the interface header. The datagram is discarded. .sp .PP The following messages report errors affecting the operation of the interface. .PP [1] .BR "dda%d: failed getting UBA resources" .br Insufficient UNIBUS resources existed to initialize the device. This is likely to be a shortage of UNIBUS mapping registers. The driver will try again if the front end is reset. .PP [15] .BR "dda%d: asynchronous restart, status = %d" .br A system interrupt occurred (as opposed to an I/O interrupt). The status indicates the status of the ACP 5250/6250 device. If the ACP 5250/6250 is operational, a system interrupt is unexpected. .PP [16] .BR "dda%d: Diagnostic failure = %d" .br A system interrupt occurred (as opposed to an I/O interrupt). The powerup diagnostics resident in the ACP 5250/6250 detected a hardware failure. .PP [17] .BR "dda%d: No Microcode Present!" .br The device status indicates that the front end is ready to be downloaded. Since the ACP 5250/6250 is not downloadable, this may indicate a system configuration error. .PP [18] .BR "dda%d: can't get bfr for acpconfig msg" .br While processing a configuration command (via the acpconfig -m option), the driver was unable to obtain an mbuf to hold the command to the front end. The configuration command is ignored. .PP [19] .BR "dda%d: supervisor message too long" .br The driver received a configuration command (via the acpconfig -m option) that is too long; the configuration command is ignored. .PP [23] .BR "dda%d: failed to get supr msg bfr!" .br While attempting to send a supervisory message, the driver was unable to allocate an mbuf to hold the message. The supervisor command is not sent. .PP [25] .BR "dda%d: couldn't get buffer for read" .br The attempt to allocate an mbuf for a read was unsuccessful. No read is issued for the channel, so no additional messages will be received from this circuit. .sp .PP The following messages report possible configuration errors. .PP [20] .BR "dda%d: illegal X25 address length!" .br In DDN Standard or Basic mode, an incoming call contained an X.25 address whose address length was found to be invalid. The call is refused. This should not happen if the front end is attached to a DDN X.25 network. .PP [21] .BR "dda%d: illegal X25 address format!" .br In DDN Standard or Basic mode, an incoming call contained an X.25 address which does not conform to the DDN X.25 address format. The call is refused. This should not happen if the front end is attached to a DDN X.25 network. .sp .PP The following messages "can't happen" -- they may indicate logic errors in the driver or the front end. .PP [4] .BR "dda%d: unknown transfer channel, lcn=%d" .br An I/O interrupt from the ACP 5250/6250 has occurred; the type of interrupt is a transfer request from the front end and the logical channel is invalid. .PP [5] .BR "dda%d: transfer request lcn %d: no mbuf" .br While servicing a transfer request, the driver discovered that it had no buffer to transmit on the listed channel. The driver aborts the transfer (by specifying a byte count of zero). .PP [6] .BR "dda%d: unknown completion channel, lcn=%d" .br An I/O interrupt from the ACP 5250/6250 has occurred; the type of interrupt is an I/O completion and the logical channel is invalid. .PP [7] .BR "dda%d: program error lcn=%d func=%x" .br An I/O interrupt from the ACP 5250/6250 has occurred; the I/O completion status indicates a program error. Resetting the board with the -z option to acpconfig(8C) may clear the error. The lcn and function codes are printed only if message 14 is enabled. .PP [8] .BR "dda%d: overrun error lcn=%d func=%x" .br An I/O interrupt from the ACP 5250/6250 has occurred; the I/O completion status indicates a data overrun. A matching pair of I/O requests specified a larger write request count than the read request count without specifying stream mode. The lcn and function codes are printed only if message 14 is enabled. .PP [9] .BR "dda%d: transfer count = 0 lcn=%d func=%x" .br An I/O interrupt from the ACP 5250/6250 has occurred; the I/O completion status indicates that the host specified a byte count of 0. Either the driver inadvertently passed a byte count of 0 on an I/O request or transfer grant, or the driver has intentionally aborted a transfer request by granting a byte count of 0 (because it no longer has a buffer). The lcn and function codes are printed only if message 14 is enabled. .PP [11] .BR "dda%d: listen collision lcn=%d func=%x" .br An I/O interrupt from the ACP 5250/6250 has occurred; the I/O completion status indicates that both sides of a data path in the same direction have listen requests pending. Both requests are terminated with this status code. The lcn and function codes are printed only if message 14 is enabled. .PP [12] .BR "dda%d: invalid function lcn=%d func=%x" .br An I/O interrupt from the ACP 5250/6250 has occurred; the I/O completion status indicates that the function specified in a request is invalid. The lcn and function codes are printed only if message 14 is enabled. .PP [13] .BR "dda%d: invalid dpn lcn=%d func=%x" .br An I/O interrupt from the ACP 5250/6250 has occurred; the I/O completion status indicates that the data path number (DPN) specified in a request is invalid (too large). The lcn and function codes are printed only if message 14 is enabled. .PP [24] .BR "dda%d: dequeued NULL mbuf in IP output chain!" .br .BR "RESET dda%d MANUALLY: use /etc/acpconfig dda%d -z" .br When attempting to start I/O on a channel, the driver discovered that no data was available to be started. The interface is disabled, and must be reset before it can be used again. .sp .PP The following error messages are only produced if the X.29 module is installed and enabled. .PP [96] .BR "dda%d:(x29) xxstart: unit offline" .br The unit has gone offline, data will be flushed and the connection will be dropped. .PP [97] .BR "dda%d:(x29) xxstart: could not get mbuf" .br A memory buffer could not be obtained. This should never happen unless the mbuf pool is too small. .PP [98] .BR "dda%d:(x29) x29_supr: unexpected message type .br A supervisor message came in that should normally never be processed by the X29 module. [100] .BR "dda%d:(x29) Bad decode, call REJECTED VC 0x%x" .br An invalid call request was received, either the remote PAD is configured improperly or the front end is not equipped to offer the X.28 options requested by the remote pad. .PP [101] .BR "dda%d:(x29) Call cleared LCN %d (%x %x)" .br A call was cleared unexpectedly. This could be due to network failure or remote PAD failure. No action required. .PP [102] .BR "dda%d:(x29) X25 RESET on LCN %d (%x %x)" .br A reset was received. The driver will take appropriate action. Possibly due to link hardware problems. No action usually required. .PP [104] .BR "dda%d:(x29) supervisor error (%x %x %x %x)" .br An invalid command was sent to the front-end supervisor. This could be due to the driver losing sync with the front-end. No action required. .PP [105] .BR "dda%d:(x29) x29_dhandle: null mbuf" .br Unable to obtain a memory buffer. If this happens often, the mbuf pool may need to be resized, otherwise no action required. .PP [106] .BR "dda%d:(x29) couldn't get mbuf for QBIT message" .br We're out of mbufs. Probably need to resize the mbuf pool. System configurations should be checked, and a netstat -m should be issued to attempt to determine what is being a mbuf hog. .PP [107] .BR "dda%d:(x29) x29_supr: answer: line was -1, VC 0x%x" .br [107] .BR "dda%d:(x29) x29_supr: ring: line was -1, VC 0x%x" .br [107] .BR "dda%d:(x29) x29_supr: break: line was -1, VC 0x%x" .br [107] .BR "dda%d:(x29) xx_tp_hangup: line was -1" .br This error should never happen. The driver attempted to perform an action on a terminal structure entry, but no link from the bottom of the stack to the top of the stack was found. If you see this error, please contact ACC customer service, as you've found a bug in the driver. .sp .PP The following messages are produced by the driver only if debugging is enabled (by defining the symbol DDADEBUG). By default, they are disabled, but they may be enabled using the acpconfig -c <msg> command. .PP [always enabled] .BR "dda%d: write completion timeout lcn %d" .br [128] .BR "dda%d: ddainit()" .br [129] .BR "dda%d: ddaoutput: dst = %d.%d.%d.%d" .br [130] .BR "dda%d: ddaoutput: dst = %d.%d.%d.%d" .br [130] .BR "dda%d: ddaoutput: lcn found = %d" .br [131] .BR "dda%d: ddatimer()" .br [132] .BR "dda%d: acpconfig_msg is %x %x %x" .br [132] .BR "dda%d: ioctl()" .br [133] .BR "dda%d: ddainta()" .br [134] .BR "dda%d: ddaintb()" .br [135] .BR "dda%d: send_config()" .br [136] .BR "dda%d: send_config" [followed by data] .br [137] .BR "dda%d: locate_x25_lcn()" .br [138] .BR "dda%d: locate_x25_lcn: made call to %d.%d.%d.%d" .br [139] .BR "dda%d: convert_ip_addr: %d.%d.%d.%d ==> %s" .br [140] .BR "dda%d: convert_x25_addr: %s ==> %d.%d.%d.%d" .br [141] .BR "dda%d: make_x25_call: call_bfr" [followed by data] .br [141] .BR "dda%d: make_x25_call: lcn used = %d" .br [142] .BR "dda%d: dda_start()" .br [143] .BR "dda%d: dda_wrq: chan=%d func=%x" .br [144] .BR "dda%d: dda_rrq()" .br [145] .BR "dda%d: start_chn()" .br [146] .BR "dda%d: start_chn: WRITE on lcn %d func %x" .br [147] .BR "dda%d: dda_data: chan=%d cc=%x cnt=%x" .br [147] .BR "dda%d: dda_data: chan=%d DDAIOCOK" .br [147] .BR "dda%d: received data" [followed by data] .br [148] .BR "dda%d: dda_supr: chan=%d cc=%x" .br [149] .BR "dda%d: supr_msg" [followed by data] .br [149] .BR "dda%d: supr_msg: CLEARVC VCN=%x" .br [149] .BR "dda%d: supr_msg: HDLC link up" .br [149] .BR "dda%d: Unexpected RESTART in state %x .br [149] .BR "dda%d: supr_msg: RESTART rcvd, no RESTART pending" .br [149] .BR "dda%d: supr_msg: got call from %d.%d.%d.%d" .br [150] .BR "dda: decode_ring()" .br [151] .BR "dda%d: clear_lcn(%d)" .br [152] .BR "dda%d: send_restart()" .br [153] .BR "dda%d: send_supr" [followed by data] .br [154] .BR "dda%d: start_supr" .br [155] .BR "dda%d: abort_io on lcn's %d - %d" .br [156] .BR "dda%d: abort_io--invalidating sioq lcn %d" .br [156] .BR "dda%d: abort_io--queueing abort: lcn %d" .br [156] .BR "dda%d: start_chn: aborting chan %d" .sp .PP The following debug messages are relevant only if the X.29 option has been installed. .PP [224] .BR "dda%d:(x29) open line %d flag %o in %s mode" .br [225] .BR "dda%d:(x29) closing line %d" .br [225] .BR "dda%d:(x29) close: tp->t_state = %x" .br [226] .BR "dda%d:(x29) ioctl qbit msg: cmd=%x ACC=%x" .br [227] .BR "dda%d:(x29) xxstart: port %d t_state = %x" .br [228] .BR "dda%d:(x29) xxstart: asked for %d got %d chars" .br [229] .BR "dda%d:(x29) xxstart: mbuf %x len %d" .br [230] .BR "dda%d:(x29) select()" .br [231] .BR "dda%d:(x29) x29_supr()" .br [231] .BR "dda%d:(x29) supr_msg: got call from %X" .br [232] .BR "dda%d:(x29) x29_data: chan=%x cc=%x cnt=%x subcc=%x" .br [233] .BR "dda%d:(x29) received data: <stream>" .br [234] .BR "dda%d:(x29) x29_data: read complete mbuf %x %x" .br [235] .BR "dda%d:(x29) x29_data: chan=%x DDAIOCOK" .br [236] .BR "dda%d:(x29) qbit: <stream>" .br [237] .BR "dda%d:(x29) flow restart [%d] in %x" .br [237] .BR "dda%d:(x29) flow on [%d] in %x of %d" .br [238] .BR "dda%d:(x29) xx_qbit_msg: %d %d %d" .br [239] .BR "dda%d:(x29) xxcntl()" .br [239] .BR "dda%d:(x29) xxcntl: close state: %s" .br [239] .BR "dda%d:(x29) xxcntl: warning: state not data_idle" .br [240] .BR "dda%d:(x29) xxclear: line=%d pgrp=%d state=%d" .br [241] .BR "dda%d:(x29) x29_init() active=%d" .br [242] .BR "dda%d:(x29) x29_supr: answer: line=%d" .br [242] .BR "dda%d:(x29) x29_supr: answer: line=%d" .br .SH "NOTES" The old logging and debug functionality has been merged in with the rest of the driver messages. Messages are simply enabled or disabled by using the acpconfig "-c" command to toggle the current state of the message. The acpconfig options "-v debug" and "-v dbgunit" and "-v log" are no longer valid. .SH "SEE ALSO" acpconfig(8C) If you see this error, pleadoc/acpconfig.8c 444 540 24 75163 4535335472 7102 .TH ACPCONFIG 8C "29 July 1989" .nh .UC 8C .ds ]W "4.3 BSD .SH NAME acpconfig \- configure ACC's ACP network interface .SH SYNOPSIS .B /etc/acpconfig .I interface [ .I address ] [ .I options ] .SH DESCRIPTION The .I acpconfig program is used to administer an ACC ACP 625, 5100, 5250, 6100, or 6250 network interface front-end processor. There are numerous options allowing interface configuration, maintenance of an address translation table, console debugging/logging control, and status inquiries. The options are discussed below. When bringing up an ACP device, the configuration parameters can be set for external or internal transmit clock and baud rate, internal or external loopback, and DTE or DCE mode. The configuration parameters are described further in the user manual that accompanies your ACP interface. For the ACP 5250 and ACP 6250, you may select DDN standard mode, DDN basic mode, or PDN (Public Data Network) X.25 service. When the system is brought up, .I acpconfig is normally called from /etc/rc.local to configure the ACP front end and to define its internet address; it can also be used later to redefine the interface's internet address or configuration. .I acpconfig is also used as part of an installation verification procedure when the ACP front end and the network interface driver are installed. Once a parameter is specified, it applies to subsequent commands unless explicitly changed. When PDN service is employed, .I acpconfig is used to add to, delete from, and read the contents of an internet-to-X.25 address translation table used by the driver. The .I interface parameter is a string of the form \*(lqname unit\*(rq (e.g., \*(lqdda0\*(rq) which corresponds to the interface as it is defined in your system configuration file. Note that .I acpconfig is always invoked with the .I interface parameter. The interface parameter name is \*(lqddn\*(rq for ACP 625, \*(lqdda\*(rq for ACP 5250/6250, and \*(lqacp\*(rq for ACP 5100/6100. The .I address is either a host name present in the host name database ( see hosts(5) ) or an internet address expressed in internet standard dot notation (e.g., 1.0.0.3). See INET(3N). For each of the following options, white space (ASCII SPACE or TAB characters) is .I optional between the \*(lqminus letter\*(rq flag and the following parameter. For example, \*(lq-b 0\*(rq and \*(lq-b0\*(rq are equivalent. All acpconfig options are restricted to the super-user except the -l option, which requires read access to /dev/kmem. As stated in the DDN X.25 specification, DDN X.25 provides two types of service: DDN basic X.25 service and DDN standard X.25 service. DDN standard X.25 service provides only local DTE to local DCE support of the X.25 connection. A reliable transport protocol (i.e., TCP) provides DDN standard X.25 service. DDN basic X.25 service provides end-to-end call management with significance as described in CCITT Recommendation X.25. The .B -s service flag is used only for the ACP 5250 and ACP 6250 devices to select DDN standard mode, DDN basic mode, or PDN X.25 service. The default for both devices is DDN standard mode. The use of PDN X.25 service requires that an address translation table be communicated to the driver, to enable it to convert between the internet address and the X.25 address of all sources and destinations. (A conversion algorithm is used for DDN service.) The address table must include entries for the local ACP front end and for all destinations, to establish connections. Creation and management of the address translation table is accomplished by the use of the -A, -a, -D, -d, and -r options. The .B -b baud_rate and .B -u mode options apply to all interface types (ddn, dda, and acp). All other options are specific to the dda (ACP 5250/6250) interface. .PP .TP 15 .B -A filename Add the contents of the named file to the driver's address translation table, used in PDN mode. The filename may be any absolute or relative pathname. The named file must contain lines which consist of an internet address followed by white space followed by an X.25 address. .sp .5v The internet address may either be in standard \*(lqdot\*(rq notation (of the form a.b.c.d; see INET(3N)) or the name of a host from the system host name database. For example, either \*(lq14.2.3.1\*(rq or \*(lqhost-name\*(rq is acceptable. The network portion of each internet address must be non-zero. .sp .5v The X.25 address must be a string of from twelve to fourteen ASCII decimal digits. Thus, an acceptable X.25 address might be \*(lq30003010007600\*(rq. The requirement for a minimum length of 12 may be changed by suitable alteration of the #define'd value PDNX25AMIN in the acpconfig.c source file, followed by recompilation. .sp .5v Blank lines, lines beginning with the character \*(lq#\*(rq, and any characters following white space after the X.25 address field are all ignored. .sp .5v Due to the necessity of reading each entry in the file and passing it to the driver for installation in its translation table, execution of the -A option may take a noticeable amount of time. A message is displayed indicating that a delay will be observed. .sp .5v An error message is displayed if insufficient space is available in the table to add all entries requested. All entries from the file which fit are entered. See the installation section of the User's Guide for a discussion on changing the size of the table. .sp .TP 15 .B -a ipaddr x25addr Add a single entry to the driver's address translation table, used in PDN mode. The .I ipaddr field must be an internet address of the format prescribed above for the \*(lq-A\*(rq option (e.g., \*(lq14.2.3.1\*(rq or \*(lqhost-name\*(rq). The .I x25addr field must be an X.25 address of the format prescribed above for the \*(lq-A\*(rq option (e.g., \*(lq30002130006578\*(rq). An error message is displayed if the specified internet address is already in the table, or if insufficient space is available in the table to add the requested entry. A single .I acpconfig command may include more than one -a option. .sp .TP 15 .B -b baud_rate Select baud rate (which sets internal clocking) or external clocking. The baud rate is set to control the speed of the internally generated communications circuit clock physically located on the ACP front-end hardware, the transmitter clock. (A modem's timing signal is an example of an externally generated clock source not physically on the ACP front end.) The -b flag is specified with a nonzero value for baud rate if internal clocking is desired. Specifying -b external or -b 0 implies external clocking. The tables below list valid baud rates used for internal clocking. .nf .sp .5v ACP 5100/6100, ACP 5250/6250 Baud Rate Parameters* .ta 1iR 2iR 3iR 4iR .sp .5v 1.33M 100K 19.2K 1200 1.00M 64K 9600 500K 56K 4800 250K 30K 2400 .sp .5v ACP 625 Baud Rate Parameters* .sp .5v 316000 57600 9600 1760 153600 38400 4800 1200 115200 28800 2400 76800 19200 2150 .sp .5v .fi * These are nominal baud rates. In some cases, the actual baud rate differs from the nominal baud rate by a few percent. Consult your user's manual for further information and descriptions of baud rates. The unit of measure is bps unless otherwise specified: M = megabits/second, and K = kilobits/second. The unit of measure does not need to be supplied with the -b flag, it is optional. To set the acp0 baud rate to 1.33 megabits/second, enter a command of the following form: \*(lq/etc/acpconfig acp0 -b 1.33M -u dte\*(rq. External clocking is the default for ACP 5100/6100 and ACP 5250/6250. To bring up the link with external clocking (the -b flag may be omitted), use a command of the following form: \*(lq/etc/acpconfig dda0 -u dte\*(rq. .sp .5v Note that for the ACP 625 device (ddn interface), the clocking is not settable by software. Only the baud rate can be set by software. For the ACP 625, the specification of external or internal clocking is a hardware configuration option. The ACP 625 is factory configured to transmit a clock signal on specified RS-449/422 or RS-232 pins that can be used if an external clock is not provided. Refer to the ACP 625 User's Manual (ACC P/N 1500015) for more details. .sp .TP 15 .B -c Control printing of driver error, log, and debug messages. All error messages are initially enabled; all debug messages are initially disabled, and the logging of call aborts is enabled. This command allows the user to disable (and re-enable) specific messages. This may be used if unusual situations cause the driver to flood the console with error messages or if extended debugging is required. The message number .I msgnum is listed in the .I dda (4) man page for each message produced by the driver. This command will display the new status of the message (i.e., whether it is now enabled or disabled). .sp .TP 15 .B -D Delete all address translation table entries. .sp .TP 15 .B -d ipaddr Delete a single entry from the driver's address translation table. The .I ipaddr field must be an internet address of the format prescribed above for the \*(lq-A\*(rq option (e.g., \*(lq14.2.3.1\*(rq or \*(lqhost-name\*(rq). An error message is displayed if the specified internet address is not in the table. A single .I acpconfig command may include more than one -d option. .sp .TP 15 .B -e size Set front-end buffer size. This allows the user to tune buffer sizes for the number of virtual circuits enabled. This option causes a reset of the front-end. The available sizes are powers of two between 256 and 16384. Specifying too large a value may cause the front-end to fail. .sp .TP 15 .B -f facility status Control the initiation of flow control parameter negotiation. The -f flag is specified with a .I facility value of "packet" or "window" and with a .I status of "on" or "off". The default is no flow control parameter negotiation initiation. To turn on packet and window size negotiation initiation for unit 0, issue the .I acpconfig commands as follows: .ti +.5i /etc/acpconfig dda0 -f packet on -f window on .br To turn off initiation: .ti +.5i /etc/acpconfig dda0 -f packet off -f window off Note that these options affect only calls made after the parameter is changed. If a circuit is already open, flow control parameters can't be altered until the circuit is cleared. When the driver initiates a call, it will request negotiation of packet size or window size if the corresponding facility has been enabled. For incoming calls, the driver will recognize requests to negotiate, and will reduce the requested values to the maximum value permitted if necessary. The maximum value for negotiation may be specified with the -v option to .I acpconfig. For versions of the firmware prior to v2.0, negotiation may not be enabled; the driver will respond to negotiation attempts by setting the packet size to 128 bytes and the packet window to 2. .sp .TP 15 .B -h mode Print statistics on logical circuit usage. The .I mode may be one of 0, 0r, 1 or 1r. If .I mode is 0 then a histogram is printed showing the percent of time that n logical channels were used. For example, .KS .nf START: Tue Nov 10 15:56:26 1987 END: Tue Nov 10 16:04:37 1987 total time: 490.31 seconds time up: 490.250000 seconds (99.99%) 0 13.79 1 74.67 2 2.18 3 9.36 .fi .KE This shows statistics recorded from 15:56:26 to 16:04:37. The total time data was recorded was 490.31 seconds. The link was up for 490.25 seconds. The column shows the number of logical channels and the percent of time that that many channels were open. In the above example there were two logical channels open 2.18 percent of the total time. When .I mode is 1, the histogram data is printed and the histogram is reset. If the .I r modifier is attached to either mode then the data is printed in raw form. The raw data can be piped into a user-supplied program to print out required statistics. Raw mode prints a single line, consisting of a set of floating point numbers each separated by a single space. The first field contains the number of seconds the link was up (cumulative since the last reset). The second and third fields contain timestamps, interpreted as seconds since Jan 1, 1970; the second field reports when the statistics were last cleared, and the third field reports the current time. The fourth field contains the current idle timer value, in seconds. The remaining fields report the number of seconds .I n circuits were open, where .I n is the field offset (i.e., the fifth field reports the number of seconds no circuits were open) Thus, for 64 circuits, 69 fields will be returned. .sp .TP 15 .B -l[n] Display the status of each active logical channel. To display for unit 0 use: .ti +.5i /etc/acpconfig dda0 -l .br or .ti +.5i /etc/acpconfig dda0 -ln .br For each active logical channel the following information is given: .nf .in +.5i Interface unit number Logical channel number Output queue length Number of dropped outgoing packets Timer value Client owning channel Logical channel state Source/Destination host name Host name of the interface (or address, if -ln is used) .in -.5i .fi .sp .TP 15 .B -m message Send an arbitrary supervisory message to the ACP 5250/6250. .I message is a sequence of arguments each of which represents one byte. Arguments beginning with \*(lq0\*(rq (zero) are interpreted as octal, others are interpreted as hexadecimal; decimal is not supported. The message is terminated by an argument which begins with a dash or by the end of .I acpconfig's argument list. This option is used primarily to change parameters in the ACP 5250/6250 for which a less cumbersome method has not yet been provided. For instance, the command \*(lqacpconfig dda0 -m 60 0 0 3 90 0 1\(rq changes the \*(lqdefault packet size\*(rq parameter to 256; the command \*(lqacpconfig dda0 -m 60 0 0 2 7f f\(rq changes the \*(lqmaximum address length\*(rq parameter to 15. This option should be used with caution; correct use of this option requires knowledge of ACP 5250/6250 supervisory message formats and parameters. These messages are detailed in chapter 9, and summarized in appendix A, of the ACP 5250/6250 Hardware Installation and User Guides. .sp .TP 15 .B -n circuits Limit the number of virtual circuits which may be used at any one time to .I circuits. This option requires version 2.1 or later of the firmware. The default value is 64 circuits. Version 2.1 supports up to 64 simultaneous circuits; version 2.2 supports up to 126 simultaneous circuits. The -n option should not be used once the link has been enabled. A reset with the -z option of .I acpconfig is necessary before changing the number of virtual circuits. After the reset completes, the number of circuits may be changed with the -n option and then the link must be enabled with the -u option of .I acpconfig. .sp .TP 15 .B -N network Configure unit to work with a nonstandard X.25 network. Current acceptable values for .I network are: .nf .ta .5i 2.0i -n standard standard network (default) -n transpac the French Transpac network -n net15 networks with 15 digit address fields .fi .sp .TP 15 .B -o option Select 1984 X.25 options. Currently the only supported 1984 X.25 option is extended clear and extended clear confirmation packet handling. This may be required for operation on Telenet. To enable this option for unit 0 enter: .ti +.5i /etc/acpconfig dda0 -o extended .br To disable this option for unit 0 enter: .ti +.5i /etc/acpconfig dda0 -o none .sp .TP 15 .B -q type Query the driver or Front End Processor for status. If .I type is 0, a Statistics Response message is solicited from the ACP 5250/6250. The results are written to the standard output in tabular form. With the exception of uptime, statistics are cleared when read. The Statistics Response is supported only in versions 2.1 and greater of the ACP 5250/6250 product. If .I type is 1, a driver status query is performed. This shows the driver's concept of the link state, driver flags, and the status of flow control parameter negotiation initiation. .sp .TP 15 .B -r count Read address translation table entries; the internet address and corresponding X.25 address obtained from the first .I count entries in the driver's address translation table will be displayed. If .I count is zero, or greater than the maximum size of the table, all entries are returned, along with an indication of the maximum possible size of the table. Internet addresses contained in the address table are displayed in normal a.b.c.d format. The order in which entries are displayed is dependent on the table maintenance algorithm in the driver. Currently, the table is sorted on internet address as stored internally (d.c.b.a). .sp .TP 15 .B -s X.25_service Specify DDN X.25 standard, DDN X.25 basic, or PDN X.25 service. The -s flag is specified with one of the following arguments: .DS .ta .5i 2i -s 0 DDN standard X.25 service -s standard DDN standard X.25 service -s 1 DDN basic X.25 service -s basic DDN basic X.25 service -s 2 PDN X.25 service -s pdn PDN X.25 service .DE .sp .TP 15 .B -t seconds Set the idle circuit timeout. This value is the amount of time a circuit may be idle before it is closed by the driver. The default value is 600 seconds (ten minutes). Reducing the idle circuit timeout will free idle circuits sooner and can be used to tune performance. Setting the timeout too low will cause circuits to close too soon and increase overhead by repeatedly clearing and reestablishing calls. .sp .TP 15 .B -u mode Bring up the interface (enable link level) in the specified configuration, or bring down the interface (disable link level). A .I mode For normal operation, the interface is configured for no loopback; external and internal loopback configurations are used only for installation verification. .nf .ta .5i 1i -u down bring down the interface -u dte bring up the interface for normal DTE operation without loopback -u dce bring up the interface for normal DCE operation without loopback -u ext bring up the interface in external loopback -u int bring up the interface in internal loopback .fi The DTE/DCE mode sets the DTE/DCE address used by the link-level protocol to reference the interface. When DTE mode is specified (-u dte), the address is 03. When DCE mode is specified (-u dte), the address is 01. With the exception of the address, no other changes are made by specifying DTE or DCE. .sp 1v Note that when external loopback is specified with \*(lq-u ext\*(rq, you should specify a baud rate if you are using the loopback connector supplied with the device, or should specify external clocking if the link is being looped back by the modem. To set external loopback, use a command of the following form: \*(lq/etc/acpconfig dda0 1.0.0.1 -b 9600 -u ext\*(rq. Use of \*(lq-u down\*(rq disables link level on the front end. Success is indicated by a \*(lqlink disabled\*(rq message on the console terminal. Two disable commands followed by one enable command is not recommended. If two disables have been sent (link level disable is aborted) then a delay (long enough for any outstanding frames to time out; 5 seconds should be sufficient) is required before issuing a link enable command. In the following sequence, note that the line is disabled each time the parameters are changed: .nf .in +.5i /etc/acpconfig dda0 -u int /etc/acpconfig dda0 -u down /etc/acpconfig dda0 -b 1.33 -u dte /etc/acpconfig dda0 -u down /etc/acpconfig dda0 -u int .in -.5i .fi If you try to bring up the interface without first setting the internet address, an error message is generated and no further processing occurs. When PDN X.25 service has been specified (by -s 2/pdn), a -u option to bring up the interface (-u dte/dce/int/ext) will not succeed unless an address translation table entry already exists for the ACP front end interface's internet address. If the address translation table entry does not exist, an error message will be displayed. An address table entry for the device must be added (via a -a or -A option) before the interface can be brought up. .sp .TP 15 .B -v key value Set a driver internal variable symbolized by .I key to .I value. Possible key values are .I log, debug, dbgunit, packet, and .I window. .nf .ta .5i 2i -v packet DDD Set X.25 packet size to DDD decimal -v window DDD Set X.25 window size to DDD decimal .fi The .I packet and .I window variables control the maximum negotiable packet and window size supported by the interface. These variables can only be set if the ACP 5250 or ACP 6250 contains firmware release 2.1 or above. The link must first be brought down (use the -u down flag of .I acpconfig ), the variables changed, and the link brought back up. .sp .TP 15 .B -z Reset the front end. To reset the dda0 device give a command of the following form: \*(lq/etc/acpconfig dda0 -z\*(rq. A message will appear on the system console telling you a device reset is in progress. There is a pause during the reset because the powerup diagnostics are run for a short period of time. .sp .SH EXAMPLE To specify internet address 1.0.0.1, external clock, normal DTE operation, and DDN standard X.25 service use a command of the following form: \*(lq/etc/acpconfig dda0 1.0.0.1 -u dte\*(rq. .SH DIAGNOSTICS Diagnostic messages appear on your terminal to indicate that the interface's configuration has been established as specified, or an error occurred during .I acpconfig program execution. .PP .BR "usage: acpconfig interface [address] [options]" .br The program was invoked with incorrect arguments. .PP .BR "acpconfig: invalid number of arguments". .br The .I acpconfig program must be invoked with a minimum of 2 arguments, the first of which is the interface name. .PP .BR "acpconfig: reset in progress" .br This command appears in response to an .I acpconfig command to reset a specified device. .PP .BR "acpconfig: command in progress". .br The .I acpconfig command is in progress, expect a small delay. Two passes are made with a delay of about 30 seconds between attempts. This message indicates that the second attempt is in progress. .PP .BR "acpconfig: device not operational" .br The front end device is not operational. Repeat the command. In response to the .I acpconfig command to initialize the front-end device, a check is made to verify that powerup diagnostics have completed. If the diagnostics have not successfully completed, then there is a 30 second delay followed by a second attempt to initialize the front end. This message indicates that the device was still not operational at the time of the second initialization attempt. .PP .BR "acpconfig: no internet address assigned to interface". .br The interface must be assigned an internet address before any of the configuration parameters will be accepted. You may assign an interface address in the following manner: \*(lqacpconfig dda0 1.0.0.1\*(rq. .PP .BR "acpconfig: -b %s invalid". .br The baud rate argument for the -b flag must be one of the values listed above. .PP .BR "acpconfig: '-b 0' invalid for specified interface". .br The '-b 0' and -b external commands (for specifying external clocking) do not apply to the specified interface (i.e., ddn?). The '-b 0' and -b external flags are only valid for ACC ACP devices that support software selection of internal or external clocking. The ACP 625 device does not. In the case of the ACP 625 ddn interface, the clocking is selected via strapping options, not software control. The use of '-b 0' or -b external is valid for the ACP 5100/6100 'acp' interfaces or the ACP 5250/6250 'dda' interfaces because they support software selection of internal or external clocking. For more detail consult the user's manuals. .PP .BR "acpconfig: ioctl (SIOCGIFFLAGS) returns no such interface" .br The specified interface does not exist. This message indicates that one of many possible errors exists: the ACP board itself is not installed in the QBUS or UNIBUS, the ACP board is in a UNIBUS slot which does not have NPR unwrapped, the driver is not installed, the ACP board is installed with a CSR address which does not match the one in the system configuration file, or the name of the interface was misspelled. .PP .BR "acpconfig: ioctl (SIOCACPCONFIG) returns permission denied". .br You must have sufficient (root) privilege for this operation. .PP .BR "acpconfig: socket: <error explanation>". .br This message indicates that a socket could not be created for the reason given. .PP .BR "acpconfig: invalid internet address '%s'". .br This message indicates that the value for the internet address is invalid. The internet address must be given in \*(lqdot notation\*(rq or be a valid host name. .PP .BR "acpconfig: invalid mode '%s'". .br The mode argument for the -u flag must be down, dte, dce, int, or ext. .PP .BR "acpconfig: '%s' flag invalid for specified interface". .br The flag identified does not apply to the specified interface (i.e., acp?). The -s, -A, -a, -d, and -r flags are only valid for ACC ACP devices which support DDN/PDN X.25 service. .PP .BR "acpconfig: invalid X.25 service". .br The X.25 service argument for the -s flag must be a value 0 - 3 or the strings: standard, basic, pdn, or class_b_c. .PP .BR "acpconfig: processing file '%s'; please wait...". .br Acpconfig is beginning to read address pairs from the named file, as requested by the -A flag; some delay will be observed while all addresses are passed to the driver for installation in its address translation table. .PP .BR "acpconfig: cannot open file '%s'". .br When attempting to read address translation table entries from a file using the -A flag, the specified file cannot be opened. .PP .BR "acpconfig: bad X.25 address length: '%s'". .br An X.25 address supplied with either the -A or -a flag has an invalid length, either greater than 14 or less than the minimum value PDNX25AMIN, #define'd in the acpconfig.c source code with the default value of 12. If necessary, the value of PDNX25AMIN may be changed and .I acpconfig recompiled. .PP .BR "acpconfig: invalid X.25 address '%s'". .br An X.25 address supplied with either the -A or -a flag includes characters that are not decimal digits, which is invalid. .PP .BR "acpconfig: end of address table (%d entries max)". .br A request for display of address translation table entries specified more entries than the table holds; the maximum table size is noted; all current entries have already been displayed. .PP .BR "acpconfig: address table entry %d: %d.%d.%d.%d ==> %s". .br This information is displayed for each address translation table entry requested via the -r flag. %d.%d.%d.%d is an internet address whose corresponding X.25 address is %s. .PP .BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Operation already in progress". .br .BR "acpconfig: must shut down interface to change modes between DDN and PDN". .br An attempt was made, while the interface was up, to change between DDN and PDN service via the -s flag. This is allowed only after bringing the interface down via -u down. .PP .BR "acpconfig: -u flag must be last". .br The -u flag must follow all others in any .I acpconfig command, so all requested options can be set before the interface is brought up (or down). .PP .BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Can't assign requested address". .br .BR "acpconfig: no local X.25 address translation in table; cannot start up PDN mode". .br These two messages indicate that an attempt was made to start up the interface for PDN service (via -s 2 then -u), but no address translation table entry exists for this host's internet address. Use -A or -a to add such a table entry then try again. .PP .BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Address already in use". .br An attempt was made to add an entry to the address translation table, but an entry was already present in the table for the identical internet address; no action was taken. Note that if the table is already full the following message will appear instead. .PP .BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Not enough core". .br An attempt was made to add an entry to the address translation table, but no space remained in the table. The maximum size of the table may be determined by using the -r 0 flag; increasing the table size may be accomplished only by rebuilding the kernel (See ACP 5250/6250 Hardware Installation and User Guide installation chapter for information on changing the table size). An alternative is to delete unused entries via the -d flag. .PP .BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Bad address". .br An attempt was made to delete an internet address from the address translation table; that internet address was not found in the table; no action was taken. .PP .BR "acpconfig: -m message too long" .br The bytes following the -m option exceeded .I acpconfig\c \'s internal buffering. An alternative is to break the long parameter command into two or more commands. .PP .BR "acpconfig: -v what?". .br An unknown key value or no key value was issued with the .I acpconfig -v command. .PP .BR "acpconfig: -v: bad packet size". .br The packet size argument for the -v flag must be one of the values listed above. .PP .BR "acpconfig: ioctl (SIOCACPCONFIG) returns: .BR "Can't change parameters with link up". .BR "Bring the link down and try again". .br The value of the target parameter cannot be changed until the link is brought down via -u down. .PP .BR "acpconfig: Operation not supported by this version of the firmware". .br Changing packet size, window size, or SVC limit requires firmware revision 2.1 or greater. Initiating flow control parameter negotiation, querying the FE for status, or changing packet options with -o also requires rev 2.1 or greater. .PP .BR "acpconfig: Bad lseek fd=%d,bas=%x,off=%d ". .br The -l option of .I acpconfig could not seek into the kernel for driver structure contents. .PP .BR "acpconfig: nlist--symbol not defined". .br The nlist program used by the -l option of .I acpconfig returned an invalid address. .PP .BR "acpconfig: No namelist" .br The nlist program used by the -l option of .I acpconfig returned an error indicating that the UNIX kernel's namelist is invalid. .PP .BR "acpconfig: cannot open /dev/kmem". .br The process issuing the -l command of .I acpconfig must be able to read /dev/kmem. .PP .BR "acpconfig: -o what?". .br A keyword was not specfified in the command text. .PP .BR "acpconfig: -o %s invalid". .br The option argument for the -o flag must be one of values listed above. .PP .BR "acpconfig: -f what?". .br A keyword was not specfified in the command text. .PP .BR "acpconfig: -f %s invalid". .br The facility or status argument for the -f flag must be one of values listed above. .PP .SH "SEE ALSO" rc(8), intro(4N), netstat(1), ioctl(2), socket(2), inet(3N), hosts(5), ACC ACP devices: acp(4), dda(4), ddn(4) .SH "NOTES" The -u flag is positional, it should be the last flag on the command line. .PP The most common user error is forgetting to disable the link (-u down) before re-enabling it with new configuration parameters. .PP The .BR "-v log, -v debug, " and .BR "-v dbgunit" commands have been obsoleted. Their functionality has been moved into the standard message facility. Use the .BR "-c" command to enable or disable the appropriate messagess. up (or down). .PP .BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Can't assign requested address". .br .BR "acpconfig: no local X.25 address translation in table; cannot start up PDN mode". .br These two messages indicate that an attempt was made to start up the interface for PDN service (via -s 2 then -u), but no address translation table entry exists for this host's internet address. Use -A odoc/pi.me 444 540 24 46677 4535335502 5652 .po 1i .he 'ACC'Preliminary'\*(td' .fo ''%'' .ba 1i .nr ii 1.25i .nr si 0.3i .ps +4 .sp 3 .ce \fB5250 Programmers Interface\fR .ps .sp 2 .sh 1 "Introduction" .lp This document describes the preliminary design for the ACC X.25 Programmers Interface for the 5250 and 6250 boards. .lp The Programmers Interface (PI) is an extension to the standard [5/6]250 Unix Driver. It allows user processes to access the front end (FE) at the same level as the driver. This can be used to implement new protocols over X.25. .lp Using the Programmer's Interface allows full access to the ACC X.25 Front End. All commands are available to the programmer. The PI and FE handle packet numbering and packet flow control thus greatly simplifying X.25 programming. .sh 1 "PI Devices" .lp Communication with the FE is done through a character device driver. Character special devices are opened by user programs. Packets to/from the FE are transmitted via \fIioctl(2)\fR calls to the open device. .lp Each open device is referred to as a \fIchannel\fR. The channel uses ioctl calls to reserve logical circuits. Each logical circuit can be used to establish a virtual circuit and transfer data with a remote host. The user is responsible for clearing any established call and freeing any logical circuits before closing the channel. Outstanding data queued within the driver will be aborted automatically. .lp Each channel has two queues associated with it: an input queue and one or more output queues. The input queue holds data that has come in from all logical circuits that have been associated with the channel as well as supervisory (logical circuit 0) data. The input queue will hold at most 1 message at a time from any given logical circuit. This does not apply to the supervisory circuit. Thus the length of this queue should be equal to the sum of 1) the maximum number of logical circuits a channel will have open simultaneously and 2) the maximum number of supervisory messages that will be presented to the channel at one time. .lp Channels may be shared between processes using standard UNIX open file inheritance or by multiple opens of the same minor device. Currently the driver does not support the non-blocking and block if in use flags to the open call. .lp There are 256 minor devices for each major device. The PI interface consists of a single major device regarless of the number of ACP boards in the system. Thus, the minor device numbers are split among the available boards. The constant PILINES in if_pi.c defines the number of minor devices per board. In general this number should simply be 256 divided by number of boards present. .lp The character device design allows the PI interface to be ported to many Unix drivers. This includes those that do not support a socket interface. This design also allows more different process structures for managing data traffic (See section 5). Access to the FE is controlled using the permission bits of the character device entries in the file system. .sh 1 "Programming" .lp The PI allows the user program to send any command supported by the FE. It is recommended that only those commands that affect a single logical channel be used. Global commands may adversely affect the IP and X.29 interfaces if they are being used simultaneously. .lp Each command or data packet is sent in a single \fIioctl\fR(2) call. All available ioctl calls are listed below. If an invalid command is sent to the PI ioctl handler then the ioctl call will return -1 and errno will be set to ENOTTY. .sp .ip "\fBXIOGETLCN\fR" This call will reserve a logical circuit and associate it with the channel that the ioctl is issued on. The byte of data used by the ioctl will contain the lcn of the channel reserved. A value of 0 indicates that no circuits are available. .ip "\fBXIOWRITE\fR" The write ioctl uses the following structure to describe the user buffer. .(l struct pi_dblock { caddr_t dataptr; /* pointer to user data to be written */ u_int length; /* length of data pointed to by dataptr */ u_short lcn; /* logical channel to send data out on */ u_char func; /* read status , write function value */ u_char subfunc; /* read substatus, write subfunction value */ u_short flags; /* special flags */ }; .)l The write ioctl is used to send data or supervisory messages to the FE. The data is pointed to by \fIdataptr\fR. The amount of data is given by \fIlength\fR. The \fIlcn\fR field gives the logical circuit that the data is to go out on. If this value is 0 then the data should be a supervisory message. A channel is permitted to write only on circuit 0 and those logical circuits that it has reserved for its use. (for more details see the XIOGETLCN description). .ip The \fIfunc\fR field is currently not used. It is reserved for future enhancements. It should always be set to 0. The \fIsubfunc\fR field is used to set the q-bit for packets. The value of the \fIsubfunc\fR field is or-ed into the subfunction byte in the FE's data request mailbox (See ACP [5/6]250 Hardware Installation Manual for more information). To set the q-bit for the packet the value of \fIsubfunc\fR should be 0x80 (hexadecimal 80) otherwise this field should always be 0. .ip The data is copied from the user buffer to an mbuf and then queued for output. If the data is less than or equal to (MLEN-1) (111) bytes then a small mbuf is used. If the data is 112 to 1K bytes then a page cluster is allocated to create a large mbuf. The maximum size for a packet is CLBYTES (1024 on most systems) bytes. .ip The mbuf allocated is queued for output. If the queue is full then the process will sleep waiting for it to empty. If the DB_NONBLOCK bit is set in \fIflags\fR then the write is non-blocking. The ioctl call will return -1 and errno will be set to EWOULDBLOCK. The length of the per logical circuit output queue is defined by DDA_OQMAX in if_ddavar.h. This limit applies to all interfaces: IP, x29, and PI. .ip The following table gives values returned in errno by the XIOWRITE ioctl and all possible reasons for the error. .TS box,tab(:); l l. EINVAL:lcn is <= 0 or greater than max lcns configured. EINVAL:lcn specified is not associated with this channel. EINVAL:length specified is <= 0 or greater than CLBYTES. ENOBUFS:An mbuf or cluster could not be allocated. EFAULT:dataptr address violation. EWOULDBLOCK: operation would block. .TE .ip "\fBXIOREAD\fR" The read ioctl uses the same pi_dblock structure as the XIOWRITE ioctl. The program must fill in the dataptr and length fields. The flags field can have the value of \fIDB_NONBLOCK\fR to specify a non-blocking read. .ip Upon return the length field will contain the number of bytes actually read. The lcn field will contain the logical circuit number that the data came in on. If the lcn field is 0 then the data should be interpreted as a supervisory message. Each read will return 1 complete packet. The buffer provided must be able to accept the largest possible packet. If supervisory or data packets come in when no user read has been issued then the packet will be queued on the channel input queue. Additional packets will be dropped and an error message printed on the console. This message indicates that the length of the input queue should probably be increased. The length of the input queue is set by PIQLEN in if_pi.c. .ip It should always be set to 0. The \fIsubfunc\fR field is used to set the q-bit for packets. The value of the \fIsubfunc\fR field is or-ed into the subfunction byte in the FE's data request mailbox (See ACP [5/6]250 Hardware Installation Manual for more information). To set the q-bit for the packet the value of \fIsubfunc\fR should be 0x80 (hexadecimal 80) otherwise this field should always be 0. .ip The \fIsubfunc\fR field will contain the C_SBSTAT (completion sub-status) data contained in the Host Completion Mailbox. This field indicates whether the q-bit was set for the packet. A value of 0x80 (hexadecimal 80) indicates the q-bit was set otherwise the value of \fIsubfunc\fR will be 0. .ip The \fIfunc\fR field will containing the C_STATUS (completion status) data from the Host Completion Mailbox. Since the driver always returns a complete, valid packet this value is uninteresting. .ip The following table gives values returned in errno by the XIOREAD ioctl and all possible reasons for the error. .TS box,tab(:); l l. EINVAL:the length specified is too small to hold the entire packet. EFAULT:dataptr address violation. EWOULDBLOCK:operation would block. .TE .ip "\fBXIORPEND\fR" This ioctl will return the number of messages queued on the channel's input queue. .ip "\fBXIORSIG\fR" This ioctl specifies a signal to be sent to the process when data is available to be read on this channel. The program passes an integer specifying the signal number as defined in the <sys/signal.h> include file. .ip "\fBXIOACCRING\fR" The lower and upper fields specify a range of protocol byte values that this channel will accept. The proto_range structure is used. .(l typedef proto_range { u_char lower; u_char upper; } proto_range; .)l The protocol byte of an incoming call is the first byte of the protocol field in the ring packet. Thus an incoming call with a protocol byte within the requested range may be routed to this PI device. If more than one PI device has requested rings of the same protocol type then the driver will arbitrarily (not necessarily fairly) give the ring to one of the devices. .ip "\fBXIONORING\fR" Inform the driver that the device will no longer accept incoming calls. This command will not purge any rings that have already been put in the data queue for the device. .ip "\fBXIOANYPROTO\fR" Issuing this ioctl will tell the driver that any calls that have protocol types not handled by any other channel may be routed to this channel. .ip "\fBXIOCLRCHAN\fR" This call will flush any input data queued in the driver for the channel that the ioctl is issued on. .ip "\fBXIOFREELCN\fR" This call is the complement of XIOGETLCN. It is used when the process is finished with a logical circuit. The process is responsible for clearing the call before freeing the circuit. This call will abort any outstanding writes. .ip The following table gives values returned in errno by the XIOFREELCN ioctl and all possible reasons for the error. .TS box,tab(:); l l. EINVAL:lcn is <= 0 or greater than max lcns configured. EINVAL:lcn specified is not associated with this channel. .TE .ip "\fBXIOABORT\fR" This call will abort any outstanding writes for the logical ciruit specified. This should be done before sending a CLEAR LOGICAL CIRCUIT supervisory message to ensure that no data is sent on the circuit after it has been cleared. .ip The following table gives values returned in errno by the XIOABORT ioctl and all possible reasons for the error. .TS box,tab(:); l l. EINVAL:lcn is <= 0 or greater than max lcns configured. EINVAL:lcn specified is not associated with this channel. .TE .sh 1 "Interaction with IP and X.29" .lp The PI interface can operate simultaneously with IP and X.29 interfaces. The \fIacpconfig\fR program supplied by ACC is used to configure the FE and bring up the link. .lp The main point of contention between the interfaces is allocation of virtual circuits. All interfaces share the same virtual circuit table. This means that there is a limit of 126 circuits among all types of traffic. A virtual circuit is reserved each time X.29 device is opened as well as each time a PI channel make an XIOGETLCN call. IP traffic must use whatever is left over. Virtual circuits between host pairs are never shared between traffic types. .lp It is possible for the PI interface to handle IP or X.29 traffic. The PI has precedence over the DDA and X.29 interfaces. .sh 1 "Usage Examples" .lp The following are several ways in which processes could be structured to process incoming supervisory and data traffic using the PI devices. .sh 2 "Single Process" .lp One process opens many PI devices. The process knows to open a new channel when all its channels are in use. Alternatively the process could open one PI device and make multiple XIOLCNREQ calls to have multiple circuits associated with it. .sh 2 "Multiple Process" .lp Each process opens a channel and manages the communication on that channel. Each process could open more than one channel if desired. .sh 2 "Master Process with Children" .lp A single master process sits on a channel with rings enabled. When a call comes in the process forks. The child process handles the channel. The parent process opens a new channel. Either the parent or child process can answer the call. The master process can also make outgoing calls and fork children to process the data. After the child process has been created the parent may close the channel to free the file descriptor. .sh 2 "Master Process with Peers" .lp A master process sets up connections and informs other (already running) processes of the name of the PI device to open to process the data. The peer processes handle the data. Communication between peers can be done using some combination of shared memory, sockets, pipes, files and signals. .sh 2 "Server Process" .lp One process acts as a server. It is responsible for all manipulation of PI devices. It can have one or many channels open at a time. Other processes use sockets to establish connections to the server and to send requests and receive replies. Fig. 3 shows this process structure. It is not required that there be the same number of client processes as there are open PI devices. The server can allocate data any way it wishes. .(b F .sp 2 .PS P: [ down A: circle "process" "1" B: circle "process" "2" C: circle "process" "3" ] move;move S: box "server" move right 0.75i D: box "PI driver" invis line <-> from 1/4 <S.ne, S.se> to D.l dashed "PI dev" above line <-> from 1/2 <S.ne, S.se> to D.l dashed line <-> from 3/4 <S.ne, S.se> to D.l dashed line <-> from P.A to S.nw chop circlerad chop 0 line <-> from P.B to S.l "socket" above chop circlerad chop 0 line <-> from P.C to S.sw chop circlerad chop 0 .PE \fBFigure 3. Process structure for a PI server. Dashed lines are PI devices. Solid lines are sockets.\fR .sp .)b .bp .sh 1 "Supervisory Command Formats" .lp This section describes the commands to be used in the \fBSupr\fR command types and their formats. .sh 2 "Call Command Header" .TS H box center; c c r|l. .sp .5v Byte Offset .sp .5v _ .TH 0: Command code (00) 1: LCN * 2 2: NA 3: Count 4-r: Common body .sp .5v .TE .sh 2 "Ring Command Header" .TS H box center; c c r|l. .sp .5v Byte Offset .sp .5v _ .TH 0: Command code (01) 1: NA 2: VCN 3: Count 4-r: Common body .sp .5v .TE .sh 2 "Clear Virtual Circuit Command Header" .TS H box center; c c r|l. .sp .5v Byte Offset .sp .5v _ .TH 0: Command code (02) 1: VCN 2: Cause code 3: Count 4: Diagnostic code 5-r: Common body .sp .5v .TE .sh 2 "Answer Command Header" .TS H box center; c c r|l. .sp .5v Byte Offset .sp .5v _ .TH 0: Command code (03) 1: LCN * 2 2: VCN 3: Count 4-r: Common body .sp .5v .TE .bp .sh 2 "Clear Logical Channel Command Header" .TS H box center; c c r|l. .sp .5v Byte Offset .sp .5v _ .TH 0: Command Code (04) 1: LCN * 2 2: Cause Code 3: Count 4: Diagnostic code 5-r: Common body .sp .5v .TE .sh 2 "Call, Ring, Answer, Clear Virtual Circuit, and Clear Logical Channel Commands Common Body" .TS H box center; c c r|l. .sp .5v Byte Offset .sp .5v _ .TH m: length of called address (0 to 14 decimal) m+1: called address in ASCII n: length of calling address (0 to 14 decimal) n+1: calling address in ASCII o: length of protocol (0 to 4) o+1: protocol p: length of facilities (0 to 63 decimal) p+1: facilities q: length of user data (0 to 124 decimal) q+1: user data .sp .5v .TE .ce NOTE Offset m begins at 4 for Answer, Call, and Ring. It begins at 5 for Clear Virtual Circuit and Clear Logical Channel. All length fields are binary values giving the number of bytes for the field to follow. .sh 2 "Reset Command" .TS H box center; c c r|l. .sp .5v Byte Offset .sp .5v _ .TH 0: command code (040) 1: LCN * 2 2: cause code 3: count (0 or 1) 4: diagnostic code (optional) .sp .25v .TE .sh 2 "Reset Acknowledge" .TS H box center; c c r|l. .sp .5v Byte Offset .sp .5v _ .TH 0: command code (041) 1: LCN * 2 2: NA 3: count (0) .sp .25v .TE .sh 2 "Interrupt Command" .TS H box center; c c r|l. .sp .5v Byte Offset .sp .5v _ .TH 0: command code (042) 1: LCN * 2 2: reason code 3: count (0) .sp .5v .TE .sh 2 "Ready Command" .TS H box center; c c r|l. .sp .5v Byte Offset .sp .5v _ .TH 0: command code (043) 1: LCN * 2 2: type code (zero=RNR, nonzero=RR) 3: count (0) .sp .5v .TE .sh 2 "Interrupt Acknowledge" .TS H box center; c c r|l. .sp .5v Byte Offset .sp .5v _ .TH 0: command code (044) 1: LCN * 2 2: NA 3: count (0) .sp .5v .TE .bp .sh 1 "Ioctl Header File and Structures" .nf /* * Ioctl's have the command encoded in the lower word, * and the size of any in or out parameters in the upper * word. The high 2 bits of the upper word are used * to encode the in/out status of the parameter; for now * we restrict parameters to at most 128 bytes. */ /* this structure is used when writing or reading data data */ struct pi_dblock { caddr_t dataptr; /* pointer to user data to be written */ u_int length; /* length of data pointed to by dataptr */ u_short lcn; /* logical channel to send data out on */ u_char func; /* read status , write function value */ u_char subfunc; /* read substatus, write subfunction value */ u_short flags; /* special flags */ }; /* pi_dblock.flags: The follwing values can be used to set bits */ #define DB_NONBLOCK 0x01 /* non-blocking read or write */ typedef struct proto_range { u_char lower; /* lower bound on protocol range (inclusive) */ u_char upper; /* upper bound on protocol range (inclusive) */ } proto_range; /* data write */ #define XIOWRITE _IOWR(t, 1, struct pi_dblock) /* data read */ #define XIOREAD _IOWR(t, 2, struct pi_dblock) /* see if data is ready to be read on a particular channel. * The field will return the number of pending data bytes. */ #define XIORPEND _IOR(t, 3, int) /* allow rings on this minor device. The proto_range structure specifies * the lower and upper bounds on the protocol byte of incomming calls */ #define XIOACCRING _IOW(t, 4, proto_range) /* specify that a protocol not requested by anyone else will be accepted on * this channel. */ #define XIOANYPROTO _IO(t, 5) /* reserve an lcn for use by this channel (minor device). The number of the * lcn is returned the the character passed */ #define XIOGETLCN _IOR(t, 6, u_char) /* free an lcn specified by the u_char parameter. This should be called for all * lcn's obtained with XIOGETLCN before the channel is closed. */ #define XIOFREELCN _IOW(t, 11, u_char) /* clear any data associated with a channel. The lcn is passed. Note that * this does not clear the circut. It only flushes queued data stored in the * driver */ #define XIOCLRCHAN _IO(t, 7) /* disallow incomming calls on this channel */ #define XIONORING _IO(t, 9) /* specify a signal to be sent to the process when there is data ready to * be read on a particular channel. The u_int specifies the signal to be * sent. */ #define XIORSIG _IOW(t, 10, u_int) /* abort all output for the given lcn. */ #define XIOABORT _IOW(t, 12, u_char) .fi ling address (0 to 14 decimal) n+1: calling address in ASCII driver/ 755 540 24 0 4535574376 5334 driver/if_dda.c 444 540 24 444612 4535335120 7022 /*************************************************************************/ /* */ /* */ /* ________________________________________________________ */ /* / \ */ /* | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* \________________________________________________________/ */ /* */ /* Copyright (c) 1986 by Advanced Computer Communications */ /* 720 Santa Barbara Street, Santa Barbara, California 93101 */ /* (805) 963-9431 */ /* */ /* */ /* File: if_dda.c */ /* */ /* Project: DDN-X.25 Network Interface Driver for ACP 5250 */ /* and ACP 6250 */ /* */ /* Function: This is a network interface driver supporting */ /* the ACP5250/6250 under UNIX versions 4.2, 4.3, */ /* 4.3-tahoe, Ultrix versions 1.2 and 2.0, and */ /* under VMS, TWG WIN/VX and TGV Multinet. */ /* */ /* Components: required: if_dda.c, if_ddareg.h, if_ddavar.h, */ /* and one of: if_dda_uqbus.c if_dda_bibus.c */ /* optional: if_pi.c, if_pivar.h, if_x29.c, */ /* if_vmsx29.c */ /* */ /*************************************************************************/ #include "dda.h" #if NDDA > 0 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% SYSTEM CONFIGURATION %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #if !defined(ACC_ULTRIX) && !defined(ACC_BSD) && !defined(ACC_VMS) ERROR an ACC OS specific option must be defined in your config file ERROR #endif /* * now define the un-set options to zero */ #if !defined(ACC_ULTRIX) #define ACC_ULTRIX 00 #endif #if !defined(ACC_BSD) #define ACC_BSD 00 #endif #if !defined(ACC_VMS) #define ACC_VMS 00 #endif /* * the define DDA_MSGQ enables the message queue. this adds 2k to the * data size of the driver. It should only be used during driver development */ /*#define DDA_MSGQ /* uncomment this to enable message queue */ /* * The following line disables the use of the histogram facilities. This * value (DDA_HISTOGRAM) is automatically undefined for all 4.2 and ULTRIX * 1.2 systems which do not support the histogram facilities. */ #define DDA_HISTOGRAM /* comment this out to disable histogram */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% INCLUDE FILES %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #ifndef SIMULATION /* real unix system */ #include "../machine/pte.h" /* page table entries */ #include "../h/param.h" #include "../h/systm.h" #include "../h/mbuf.h" #include "../h/buf.h" #include "../h/protosw.h" #include "../h/socket.h" #include "../h/vmmac.h" #include "../h/errno.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/kernel.h" #include "../h/ioctl.h" #include "../vax/cpu.h" #include "../vax/mtpr.h" #include "../net/if.h" #include "../net/netisr.h" #include "../net/route.h" #include "../netinet/in.h" #include "../netinet/in_systm.h" # if ACC_BSD > 42 || ACC_ULTRIX > 12 # include "../netinet/in_var.h" # endif #include "../netinet/ip.h" #include "../netinet/ip_var.h" #include "../vaxif/if_ddareg.h" #include "../vaxif/if_ddavar.h" #else SIMULATION #include "machine/pte.h" /* page table entries */ #include "h/param.h" #include "h/systm.h" #include "h/mbuf.h" #include "h/buf.h" #include "h/protosw.h" #include "h/socket.h" #include "h/vmmac.h" #include "h/errno.h" #include "h/dir.h" #include "h/user.h" #include "h/kernel.h" #include "h/ioctl.h" #include "vax/cpu.h" #include "vax/mtpr.h" #include "net/if.h" #include "net/netisr.h" #include "net/route.h" #include "netinet/in.h" #include "netinet/in_systm.h" # if ACC_BSD > 42 || ACC_ULTRIX > 12 # include "netinet/in_var.h" # endif #include "netinet/ip.h" #include "netinet/ip_var.h" #include "if_ddareg.h" #include "if_ddavar.h" # ifndef SIOCACPCONFIG # define SIOCACPCONFIG _IOWR(i,40,struct ifreq) # endif # ifndef INET # define INET 1 # endif extern struct ifqueue ipintrq; /* IP input queue */ #endif SIMULATION #if ACC_VMS > 00 # ifdef eunice # define WINS # else # define MULTINET # endif #endif #if ACC_VMS > 00 # ifdef WINS # include <vms/adpdef.h> /* Define Adapters */ # include <vms/dcdef.h> /* Define AT$_UBA, adapter type */ # else MULTINET # include "../vaxif/if_ddaioctl.h" /* not in ioctl.h */ # endif #endif /* disable histogram functions for BSD 4.2 and ULTRIX 1.2 */ #if ACC_BSD == 42 || ACC_ULTRIX == 12 # undef DDA_HISTOGRAM #endif /* Ultrix doesn't have syslog, so use printf instead. Since the two * functions take different arg list formats, embed the open paren in * the defined symbol; provide DDAELOG to close the call while keeping * parentheses matched. The argument to DDALOG is ignored for printf; * for log(), debugging messages use LOG_DEBUG, all others use LOG_ERR. */ #if (ACC_BSD > 42 || ACC_VMS > 00) && !defined(SIMULATION) # include "syslog.h" # define DDALOG(s) log( s, #else # define DDALOG(s) printf( #endif #define DDAELOG ) #ifndef DDADEBUG #define PRIVATE static /* hide our internal functions */ #else #define PRIVATE /* let the world see them */ #endif /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% GLOBAL FUNCTIONS %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ int ddaprobe(); int ddaattach(); int ddareset(); int ddainit(); int ddaoutput(); int ddatimer(); int ddaioctl(); int ddainta(); /* service interrupt "a" from front end */ int ddaintb(); /* service interrupt "b" from front end */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL FUNCTIONS %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void send_config(); PRIVATE struct dda_cb *locate_x25_lcn(); PRIVATE struct dda_cb *find_free_lcn(); PRIVATE boolean convert_ip_addr(); PRIVATE u_long convert_x25_addr(); PRIVATE boolean make_x25_call(); PRIVATE void dda_start(); PRIVATE void dda_rrq(); PRIVATE void dda_wrq(); PRIVATE int start_chn(); PRIVATE void dda_data(); PRIVATE void dda_supr(); PRIVATE void supr_msg(); PRIVATE boolean decode_ring(); PRIVATE void decode_answer(); PRIVATE void clear_lcn(); PRIVATE void send_restart(); PRIVATE void send_supr(); PRIVATE void start_supr(); PRIVATE void abort_io(); PRIVATE void prt_addr(); #ifdef DDA_PAD_OR_RAW PRIVATE int dda_decode_type(); #endif #ifdef DDA_PADOPT PRIVATE void x29_data(); PRIVATE void x29_supr(); PRIVATE void x29_init(); #endif DDA_PADOPT #ifdef DDA_RAWOPT PRIVATE void pi_data(); PRIVATE void pi_supr(); PRIVATE void pi_init(); PRIVATE int pi_circuit_to_handle_protocol(); #endif DDA_RAWOPT #ifdef DDADEBUG PRIVATE void prt_bytes(); #endif PRIVATE char *fmt_x25(); #ifdef DDA_HISTOGRAM PRIVATE void hist_init(); /* histogram functions */ PRIVATE void hist_lcn_state(); PRIVATE void hist_all_lcns(); PRIVATE void hist_link_state(); PRIVATE void hist_read(); PRIVATE int hist_copyout(); #else DDA_HISTOGRAM /* make all histogram functions no-op's */ #define hist_init(a,b) #define hist_lcn_state(a,b,c) #define hist_all_lcns(a,b) #define hist_link_state(a,b,c) #define hist_read(a) #define hist_copyout(a,b) #endif DDA_HISTOGRAM /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL VARIABLES %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE int tmo_data_idle = TMO_DATA_IDLE; /* idle circuit timeout for * all boards */ PRIVATE int nddach[4] = { /* number of channels currently in use */ NDDACH_DEFAULT, NDDACH_DEFAULT, NDDACH_DEFAULT, NDDACH_DEFAULT }; PRIVATE char *dda_product; /* name of product, like "ACP5250" */ PRIVATE int dda_hasmaint; /* do we have a maintenance board? */ /* the message bits are used in the DMESG macros defined in if_ddavar.h */ /* word 1 and 2 (msgs 0 - 63) are reserved for the IP interface */ /* word 3 (msgs 64 - 95) are reserved for the PI interface */ /* word 4 (msgs 96 - 127) are reserved for the X.29 interface */ /* word 5 and 6 (msgs 128 - 191) are reserved for debugging main module*/ /* word 7 (msgs 192 - 223) are reserved for debugging the PI */ /* word 8 (msgs 224 - 255) are reserved for debugging X29 */ /* word 9 (msgs 256 - 287) are reserved for call logging */ #define NDMESGWORDS 9 #define MAXDMSGS (NDMESGWORDS * 32) PRIVATE long ddamsgs[NDDA][NDMESGWORDS]; /* | | | | | | | | | * default: all informational messages on, /--/--/--/ | | | | | * all debug messages off, --------------------/---/---/---/ | * log busys, but not calls or I/O aborts ---------------------/ */ /* Must be as large as the larger of (trtab, ddactl, dnload): */ char dda_iobuf[sizeof(struct ddactl)]; struct dda_softc dda_softc[NDDA]; /* per device infomation */ /* header for building command to be sent to the front end in */ /* response to ACPCONFIG user command */ PRIVATE u_char acpconfig_msg[] = { LINE_CNTL, /* set command code */ 0x00, /* not used */ 0x00, /* not used */ 0x00, /* extension length (set at runtime) */ 0x00, /* cmd space */ 0x00, 0x00, 0x00, 0x00 }; PRIVATE u_char bfr_size_msg[] = { SET_BFR_SIZE, /* set command code */ 0x00, /* not used */ 0x00, /* not used */ 0x01, /* extension length */ 0x00, /* cmd space */ 0x00, 0x00, 0x00, 0x00 }; PRIVATE u_char ddacb_cmnd[4] = { CALL, 0, 0, 0 }; PRIVATE u_char ddacb_called_addr[16] = {0}; PRIVATE u_char ddacb_calling_addr[16] = {0}; PRIVATE u_char ddacb_facilities[64] = {0}; PRIVATE u_char ddacb_protocol[5] = {0}; PRIVATE u_char ddacb_user_data[64] = {0}; #ifdef DDADEBUG u_char dda_silo_counter; u_char dda_debug_silo[256]; #endif /* Table of baud rate values and the associated parameter for the Set */ /* System Parameters message, ddainit_msg. The 'parameter1' is nonzero */ /* for valid baud rate divisors. These are nominal baud rates. */ PRIVATE struct baud { char b_value; u_char parameter1; /* first byte of baud rate setting */ u_char parameter2; /* second byte of baud rate setting */ } ddabaud_rate[] = { { 1, 0x02, 0x00 }, /* 2.00M */ { 2, 0x03, 0x00 }, /* 1.33M */ { 3, 0x04, 0x00 }, /* 1.00M */ { 4, 0x08, 0x00 }, /* 500K */ { 5, 0x10, 0x00 }, /* 250K */ { 6, 0x28, 0x00 }, /* 100K */ { 7, 0x3e, 0x00 }, /* 64K */ { 8, 0x47, 0x00 }, /* 56K */ { 9, 0x85, 0x00 }, /* 30K */ { 10, 0xd0, 0x00 }, /* 19.2K */ { 11, 0xa1, 0x01 }, /* 9600 */ { 12, 0x41, 0x03 }, /* 4800 */ { 13, 0x83, 0x06 }, /* 2400 */ { 14, 0x05, 0x0d }, /* 1200 */ { 0, 0, 0 } }; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% Address Translation Table for Internet <-> X.25 addresses %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #define DDANATT 32 /* number of addr translation table entries */ PRIVATE int dda_num_addr_tr[NDDA] = {0}; /* number of address * translations */ /* currently stored */ PRIVATE struct dda_addr_tr { /* X.25 PDN address translation table */ u_long ip_addr; /* internet address */ u_char x25_addr[MAXADDRLEN]; /* X.25 address */ } dda_addr_tr[NDDA][DDANATT] = {{ 0L, ""}}; /* null */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% Aliasing of IP address for 4.2 ==> 4.3 compatibility %%*/ /*%% Note: this union is not required in 4.2, since the s_net %%*/ /*%% field and its friends are in an include file. We use it to %%*/ /*%% minimize the number of #ifdef dependencies in the code. %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #ifdef s_net /* 4.2 */ # undef s_net # undef s_host # undef s_lh # undef s_impno #endif union imp_addr { struct in_addr ip; struct imp { u_char s_net; u_char s_host; u_char s_lh; u_char s_impno; } imp; }; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% GLOBAL ROUTINES %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #ifdef ACP_BI #include "if_dda_bibus.c" #else #include "if_dda_uqbus.c" #endif /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAIOCTL() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine processes device dependent ioctl's. Supported */ /* ioctls set the host's internet address for this network */ /* interface, or send Set System Parameters Message to the ACP. */ /* The logic for setting the interface address must remain */ /* compatible with both ifconfig and acpconfig programs. */ /* If the ioctl comes from the acpconfig program, the front end */ /* is not initialized because the user will specify explicitly */ /* what parameters are desired. If the ioctl comes from the */ /* ifconfig program, the fron end is initialized with default */ /* parameters in the ddainit_msg array. */ /* */ /* Call: ddaioctl(ifp, cmd, data) */ /* Argument: ifp: pointer to the network interface data */ /* structure, ifnet */ /* cmd: identifies the type of ioctl */ /* data: information for the ioctl */ /* Returns: 0 for success, or the nonzero error value: */ /* EINVAL invalid ioctl request */ /* Called by: network software, address of this routine is */ /* defined in af_inet network interface struct */ /* Calls to: splimp() */ /* if_rtinit() */ /* in_netof() */ /* in_lnaof() */ /* ddainit() */ /* send_config() */ /* DDALOG() */ /* splx() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #ifdef MULTINET volatile int StatQuery_Completed; /* Polled for board stat ioctl */ #endif ddaioctl(ifp, cmd, data) register struct ifnet *ifp; int cmd; caddr_t data; { register struct dda_softc *ds = &dda_softc[ifp->if_unit]; struct ifreq *ifr = (struct ifreq *) data; #if defined(DDA_PADOPT) && defined(WINS) int prealloc_x29(); /* Preallocate UCBs for X29 */ #endif #if ACC_BSD == 42 || ACC_ULTRIX == 12 struct sockaddr_in *sin = (struct sockaddr_in *) & ifr->ifr_addr; #else struct ifaddr *ifa = ds->dda_if.if_addrlist; #endif int s; int error = 0; int i; register struct dda_addr_tr *atp, *btp; struct trtab *tr; struct ddactl *da; char arg2[MAXADDRLEN], code; #ifdef DDADEBUG if (DDADBCH(4, ifp->if_unit)) { DDALOG(LOG_DEBUG) "dda%d: ioctl()\n", ifp->if_unit DDAELOG; } #endif DDADEBUG /* * This may not be necessary here, but under some flavors of BSDish * systems (2.0ULTRIX) this routine is apparently called at splimp(). In * the case that we are currently processing ioctls issued from acpconfig * in /etc/rc, the board may not have come on line yet - so we need to be * able to process the B interrupt while in the delay loop below. */ #ifndef MULTINET s = spl0(); #endif switch (cmd) { case SIOCSIFADDR: if (!suser()) return EACCES; #if ACC_BSD == 42 || ACC_ULTRIX == 12 if (ifp->if_flags & IFF_RUNNING) if_rtinit(ifp, -1); /* delete previous route */ ifp->if_addr = *(struct sockaddr *) sin; ifp->if_net = in_netof(sin->sin_addr); ifp->if_host[0] = in_lnaof(sin->sin_addr); if (ifp->if_flags & IFF_RUNNING) if_rtinit(ifp, RTF_UP); else ddainit(ifp->if_unit); ds->dda_ipaddr.s_addr = ((struct sockaddr_in *) & ifp->if_addr)->sin_addr.s_addr; #else /* 4.3 networking */ if (ifa->ifa_addr.sa_family != AF_INET) return (EINVAL); if ((ifp->if_flags & IFF_RUNNING) == 0) ddainit(ifp->if_unit); ds->dda_ipaddr = IA_SIN(ifa)->sin_addr; #endif /* 4.3 networking */ break; case SIOCACPCONFIG: /* process ioctl from acpconfig program */ code = *(ifr->ifr_data); /********************************************************* * * * Commands n, h, q, and r are non-privileged * * * *********************************************************/ if (!suser() && code != 'n' && code != 'h' && code != 'q' && code != 'r') return EACCES; #if ACC_BSD == 42 || ACC_ULTRIX == 12 sin = (struct sockaddr_in *) & ds->dda_if.if_addr; if (in_netof(sin->sin_addr) == 0) #else if (ds->dda_if.if_addrlist == 0) #endif { error = EDESTADDRREQ; /* error, no internet address */ goto exit; } /* for command to set baud rate, look up the value for the */ /* baud rate divisor in the ddabaud_rate table, put value */ /* in the Set System Parameters message, ddainit_msg */ if (code >= 1 && code <= 14) { register struct baud *p; if (error = diags_completed(ds)) goto exit; for (p = ddabaud_rate; p->b_value; p++) { if ((*(ifr->ifr_data) - p->b_value) == 0) break; } /* if internal clock not set, do so */ if ((ds->dda_init & DDA_INTCLOCK) == 0) { ds->dda_init |= DDA_INTCLOCK; acpconfig_msg[MSG_OFFSET] = CLOCK_CNTL; acpconfig_msg[MSG_OFFSET + 1] = INTERNAL_CLOCK; acpconfig_msg[MSG_OFFSET + 2] = BAUD_CNTL; acpconfig_msg[MSG_OFFSET + 3] = p->parameter1; acpconfig_msg[MSG_OFFSET + 4] = p->parameter2; acpconfig_msg[MSG_LENGTH] = 5; } else { acpconfig_msg[MSG_OFFSET] = BAUD_CNTL; acpconfig_msg[MSG_OFFSET + 1] = p->parameter1; acpconfig_msg[MSG_OFFSET + 2] = p->parameter2; acpconfig_msg[MSG_LENGTH] = 3; } if ((p->b_value == 0) || (p->parameter1 == 0)) error = EINVAL; /* baud rate value invalid */ else send_config(ds, acpconfig_msg); /* send message to front end */ goto exit; } switch (code) { case 'a': /* Add address translation table entry */ if (error = diags_completed(ds)) goto exit; if (dda_num_addr_tr[ifp->if_unit] >= DDANATT) { /* table already full */ error = ENOMEM; goto exit; } /* * Copy in user arguments and point "tr" at them. Then scan the * translation table and either find location to insert or flag * error */ if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct trtab))) { error = EFAULT; goto exit; } tr = (struct trtab *) dda_iobuf; for (i = 0, atp = &dda_addr_tr[ifp->if_unit][0]; i < dda_num_addr_tr[ifp->if_unit]; i++, atp++) { if (atp->ip_addr == tr->ipaddr) { if (bcmp((char *) atp->x25_addr, (char *) tr->x25addr, MAXADDRLEN)) { error = EADDRINUSE; goto exit; } else /* addresses are the same, just ignore ioctl */ goto exit; } if (atp->ip_addr > tr->ipaddr) /* insert entry here */ break; } for (btp = &dda_addr_tr[ifp->if_unit][dda_num_addr_tr[ifp->if_unit]]; btp > atp; btp--) { /* open up space for a new entry */ btp->ip_addr = (btp - 1)->ip_addr; bcopy((btp - 1)->x25_addr, btp->x25_addr, MAXADDRLEN); } atp->ip_addr = tr->ipaddr; bcopy(tr->x25addr, atp->x25_addr, MAXADDRLEN); dda_num_addr_tr[ifp->if_unit]++; /* one more table entry */ goto exit; case 'D': if (error = diags_completed(ds)) goto exit; /* clear table for use by 'r' flag of acpconfig */ for (i = 0, atp = &dda_addr_tr[ifp->if_unit][0]; i < dda_num_addr_tr[ifp->if_unit]; i++, atp++) { atp->ip_addr = 0L; atp->x25_addr[0] = 0; } dda_num_addr_tr[ifp->if_unit] = 0; goto exit; case 'd': /* Delete address translation table entry */ if (error = diags_completed(ds)) goto exit; if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct trtab))) { error = EFAULT; goto exit; } tr = (struct trtab *) dda_iobuf; error = EFAULT; /* in case inet address not in table */ for (i = 0, atp = &dda_addr_tr[ifp->if_unit][0]; i < dda_num_addr_tr[ifp->if_unit]; i++, atp++) { if (atp->ip_addr == tr->ipaddr) { error = 0; /* found it: cancel error */ for (; i < dda_num_addr_tr[ifp->if_unit] - 1; i++, atp++) { atp->ip_addr = (atp + 1)->ip_addr; bcopy((atp + 1)->x25_addr, atp->x25_addr, MAXADDRLEN); } atp->ip_addr = 0L; /* clear last vacated entry */ atp->x25_addr[0] = 0; dda_num_addr_tr[ifp->if_unit]--; /* one fewer table * entries */ break; } } goto exit; case 'f': /* -f facility status */ /* * The first byte of the "msg" selects the flow control parameter * and the "drval" field holds the status (on or off). */ if (error = diags_completed(ds)) goto exit; if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) { error = EFAULT; goto exit; } if (ds->dda_firmrev < 0x21) { /* need 2.0 or above ROMs */ error = ENOPROTOOPT; goto exit; } da = (struct ddactl *) dda_iobuf; switch (da->msg[0]) { case 0: /* packet */ if (da->drval) ds->dda_init |= DDA_PKTNEG; else ds->dda_init &= ~DDA_PKTNEG; break; case 1: /* window */ if (da->drval) ds->dda_init |= DDA_WNDNEG; else ds->dda_init &= ~DDA_WNDNEG; break; } goto exit; case 'o': /* Set options */ if (error = diags_completed(ds)) goto exit; if (ds->dda_firmrev < 0x21) { /* need 2.1 or above ROMs */ error = ENOPROTOOPT; goto exit; } if (ds->dda_state != S_DISABLED) { /* must bring link down */ error = EINPROGRESS; goto exit; } if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) { error = EFAULT; goto exit; } da = (struct ddactl *) dda_iobuf; acpconfig_msg[MSG_OFFSET] = PKT_OPTIONS; acpconfig_msg[MSG_OFFSET + 1] = da->msg[0]; acpconfig_msg[MSG_LENGTH] = 2; #ifdef DDADEBUG if (DDADBCH(4, ifp->if_unit)) { DDALOG(LOG_DEBUG) "dda%d: acpconfig_msg is %x %x %x\n", ifp->if_unit, acpconfig_msg[MSG_LENGTH], acpconfig_msg[MSG_OFFSET], acpconfig_msg[MSG_OFFSET + 1] DDAELOG; } #endif DDADEBUG send_config(ds, acpconfig_msg); goto exit; case 'N': /* read network id */ if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) { error = EFAULT; goto exit; } da = (struct ddactl *) dda_iobuf; ds->dda_net_id = da->drval; goto exit; case 'r': /* Read address translation table entry */ /* * The value stored in "ipaddr" is not an address, but an index * of a translation table entry to read out. The x25_addr field * in the input structure is not used. */ if (error = diags_completed(ds)) goto exit; if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct trtab))) { error = EFAULT; goto exit; } tr = (struct trtab *) dda_iobuf; i = tr->ipaddr; if (i >= DDANATT) { /* scanned the whole table */ error = EFAULT; goto exit; } tr->ipaddr = dda_addr_tr[ifp->if_unit][i].ip_addr; bcopy(dda_addr_tr[ifp->if_unit][i].x25_addr, tr->x25addr, MAXADDRLEN); if (copyout(tr, ifr->ifr_data, sizeof(struct trtab))) error = EFAULT; goto exit; #ifdef DDA_HISTOGRAM case 'h': /* read histogram */ if (error = diags_completed(ds)) goto exit; hist_read(ifp->if_unit); if (hist_copyout(ifp->if_unit, ifr->ifr_data)) error = EFAULT; goto exit; case 'H': /* read and reset histogram */ if (error = diags_completed(ds)) goto exit; hist_read(ifp->if_unit); if (hist_copyout(ifp->if_unit, ifr->ifr_data)) error = EFAULT; else hist_init(ifp->if_unit, 1); goto exit; #endif DDA_HISTOGRAM case 'v': /* -v variable value */ /* * There are two "variables" in the driver which can be set via * ioctl: packet size, and window size. The "drval" field holds * the value and the first byte of the "msg" selects the variable. * Note that the selector is another little undocumented piece of * the interface between here and the acpconfig program. It is * coupled to the ordering of a little string table inside that * program; new parameters should be added at the end, not the * middle! */ /* No check to see if powerup diags are completed */ if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) { error = EFAULT; goto exit; } da = (struct ddactl *) dda_iobuf; switch (da->msg[0]) { case 0: /* set logging (obsolete) */ case 1: /* set debug (obsolete) */ case 2: /* set debug unit (obsolete) */ error = EINVAL; break; /* * For both packet and window sizes, we check that the link * is currently down. The new parameters will be sent to the * FEP when the link is next brought up. See processing for * -u flag. */ case 3: /* set packetsize */ if (error = diags_completed(ds)) goto exit; if (ds->dda_firmrev < 0x21) { /* need 2.1 or above ROMs */ error = ENOPROTOOPT; goto exit; } if (ds->dda_state != S_DISABLED) { /* must bring link down */ error = EINPROGRESS; goto exit; } /* * X.25 (1984) section 7.2.2.1.1 says 12 (4096 byte packets) * BBN report 5760 (September 1984) 14.2.1.2 says 10. We just * check for 12. */ if (da->drval < 4 || da->drval > 12) error = EINVAL; else { int packetsize = 1 << da->drval; acpconfig_msg[MSG_LENGTH] = 3; acpconfig_msg[MSG_OFFSET] = MAX_PKT_SZ; /* Max negotiable */ /* pkt size */ acpconfig_msg[MSG_OFFSET + 1] = packetsize & 0xFF; acpconfig_msg[MSG_OFFSET + 2] = (packetsize >> 8) & 0xFF; send_config(ds, acpconfig_msg); } break; case 4: /* set windowsize */ if (error = diags_completed(ds)) goto exit; if (ds->dda_firmrev < 0x21) { /* need 2.0 or above ROMs */ error = ENOPROTOOPT; goto exit; } if (ds->dda_state != S_DISABLED) { /* must bring link down */ error = EINPROGRESS; goto exit; } if (da->drval < 1 || da->drval > 127) error = EINVAL; else { acpconfig_msg[MSG_LENGTH] = 2; acpconfig_msg[MSG_OFFSET] = MAX_PKT_WN; /* Max negotiable */ /* pkt window */ acpconfig_msg[MSG_OFFSET + 1] = da->drval; send_config(ds, acpconfig_msg); } break; } goto exit; case 'm': /* -m message */ if (error = diags_completed(ds)) goto exit; if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) { error = EFAULT; goto exit; } da = (struct ddactl *) dda_iobuf; send_config(ds, da->msg); goto exit; case 'n': /* -n svc_count */ if (error = diags_completed(ds)) goto exit; if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) { error = EFAULT; goto exit; } if (ds->dda_firmrev < 0x21) { /* need 2.1 or above ROMs */ error = ENOPROTOOPT; goto exit; } da = (struct ddactl *) dda_iobuf; i = 0; /* i holds the return value */ if (da->drval == 0) i = nddach[ifp->if_unit]; else if (ds->dda_state != S_DISABLED) { /* must bring link down */ error = EINPROGRESS; goto exit; } else { if (!suser()) { error = EACCES; goto exit; } if (da->drval < 1 || da->drval > NDDACH) error = EINVAL; else { acpconfig_msg[MSG_LENGTH] = 2; acpconfig_msg[MSG_OFFSET] = SVC_LIMIT; acpconfig_msg[MSG_OFFSET + 1] = da->drval; nddach[ifp->if_unit] = da->drval; send_config(ds, acpconfig_msg); } } if (copyout(&i, ifr->ifr_data, sizeof(int))) error = EFAULT; goto exit; case 'c': /* -c msgnum -- dis/enable driver mesg */ if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) { error = EFAULT; goto exit; } da = (struct ddactl *) dda_iobuf; if (da->drval < 0 || da->drval >= MAXDMSGS) error = EINVAL; else { u_char new_val; DMESGTOG(ifp->if_unit, da->drval); new_val = DMESGVAL(ifp->if_unit, da->drval) ? 1 : 0; /* 1 means disabled, 0 means enabled */ if (copyout(&new_val, ifr->ifr_data, sizeof(u_char))) error = EFAULT; } goto exit; case 't': /* -t sec -- set data idle timeout */ if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) { error = EFAULT; goto exit; } da = (struct ddactl *) dda_iobuf; if (da->drval < 1) error = EINVAL; else tmo_data_idle = da->drval / DDA_TIMEOUT; goto exit; case 'q': /* driver/FE/shm/silo state queries */ if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) { error = EFAULT; goto exit; } da = (struct ddactl *) dda_iobuf; switch (da->msg[0]) { case 0: /* front end state query */ if ((error = diags_completed(ds)) == 0) { int s2 = splimp(); /* need 2.0 or above ROMs */ if (ds->dda_firmrev < 0x21) { error = ENOPROTOOPT; splx(s2); /* We got it and woke up */ break; } #ifdef MULTINET StatQuery_Completed = 0; send_supr(ds, STATQUERY, 0, 0); splx(s2); /* drop ioctl so we can be scheduled */ while (!StatQuery_Completed); #else MULTINET send_supr(ds, STATQUERY, 0, 0); sleep(dda_iobuf, PSLEP); /* Interruptible with ^C */ splx(s2); /* We got it and woke up */ #endif MULTINET if (copyout(dda_iobuf, ifr->ifr_data, sizeof(struct ddactl))) error = EFAULT; } break; case 1: /* driver state query */ da->msg[0] = ds->dda_state; da->msg[1] = ds->dda_init; da->msg[2] = ds->dda_flags; da->msg[3] = ds->dda_firmrev; if (copyout(dda_iobuf, ifr->ifr_data, sizeof(struct ddactl))) error = EFAULT; break; #ifdef DDADEBUG case 2: /* debug query */ if (copyout(dda_debug_silo, ifr->ifr_data, 256)) error = EFAULT; break; #endif #if defined(DDADEBUG) && defined(ACP_BI) case 3: /* shm/biic query (temporary) */ { register struct uba_device *ui = ddainfo[ifp->if_unit]; dda_dump_shm((SYSGEN_BLOCK *) ds->dda_mapreg); dda_dump_biic_regs((struct biic_regs *) ui->ui_addr); } break; #endif default: error = EINVAL; } goto exit; case '0': /* -u 0 */ if (error = diags_completed(ds)) goto exit; acpconfig_msg[MSG_OFFSET] = LINK_DISABLE; acpconfig_msg[MSG_LENGTH] = 1; hist_link_state(ifp->if_unit, ds->dda_state, S_GOING_DOWN); ds->dda_state = S_GOING_DOWN; break; case '1': /* -u 1 */ if (error = diags_completed(ds)) goto exit; acpconfig_msg[MSG_OFFSET] = LINK_LOOPBACK; acpconfig_msg[MSG_OFFSET + 1] = LOOP_NONE; acpconfig_msg[MSG_OFFSET + 2] = DTE_DCE_MODE; acpconfig_msg[MSG_OFFSET + 3] = DTE; acpconfig_msg[MSG_OFFSET + 4] = LINK_ENABLE; acpconfig_msg[MSG_LENGTH] = 5; ds->dda_state = S_COMING_UP; hist_init(ifp->if_unit, 0); break; case '2': /* -u 2 */ if (error = diags_completed(ds)) goto exit; acpconfig_msg[MSG_OFFSET] = LINK_LOOPBACK; acpconfig_msg[MSG_OFFSET + 1] = LOOP_NONE; acpconfig_msg[MSG_OFFSET + 2] = DTE_DCE_MODE; acpconfig_msg[MSG_OFFSET + 3] = DCE; acpconfig_msg[MSG_OFFSET + 4] = LINK_ENABLE; acpconfig_msg[MSG_LENGTH] = 5; ds->dda_state = S_COMING_UP; hist_init(ifp->if_unit, 0); break; case '3': /* -u 3 */ if (error = diags_completed(ds)) goto exit; acpconfig_msg[MSG_OFFSET] = LINK_LOOPBACK; acpconfig_msg[MSG_OFFSET + 1] = LOOP_EXTERNAL; acpconfig_msg[MSG_OFFSET + 2] = LINK_ENABLE; acpconfig_msg[MSG_LENGTH] = 3; ds->dda_state = S_COMING_UP; hist_init(ifp->if_unit, 0); break; case '4': /* -u 4 */ if (error = diags_completed(ds)) goto exit; acpconfig_msg[MSG_OFFSET] = LINK_LOOPBACK; acpconfig_msg[MSG_OFFSET + 1] = LOOP_INTERNAL; acpconfig_msg[MSG_OFFSET + 2] = LINK_ENABLE; acpconfig_msg[MSG_LENGTH] = 3; ds->dda_state = S_COMING_UP; hist_init(ifp->if_unit, 0); break; case 'b': /* -b 0 */ if (error = diags_completed(ds)) goto exit; acpconfig_msg[MSG_OFFSET] = CLOCK_CNTL; acpconfig_msg[MSG_OFFSET + 1] = EXTERNAL_CLOCK; acpconfig_msg[MSG_LENGTH] = 2; ds->dda_init &= ~DDA_INTCLOCK; break; case 'S': /* select DDN standard X.25 service */ /* -s 0 or -s standard */ if (error = diags_completed(ds)) goto exit; if (ds->dda_if.if_flags & IFF_UP && ds->dda_init & DDA_PDN) { error = EALREADY; goto exit; /* no PDN->DDN mode change if running */ } ds->dda_init &= ~(DDA_BASIC | DDA_PDN); ds->dda_init |= DDA_STANDARD; goto exit; case 'T': /* select DDN basic X.25 service */ /* -s 1 or -s basic */ if (error = diags_completed(ds)) goto exit; if (ds->dda_if.if_flags & IFF_UP && ds->dda_init & DDA_PDN) { error = EALREADY; goto exit; /* no PDN->DDN mode change if running */ } ds->dda_init &= ~(DDA_PDN | DDA_STANDARD); ds->dda_init |= DDA_BASIC; goto exit; case 'U': /* select X.25 Public Data Network service */ /* -s 2 or -s pdn */ if (error = diags_completed(ds)) goto exit; if (ds->dda_if.if_flags & IFF_UP && (ds->dda_init & (DDA_BASIC | DDA_STANDARD))) { error = EALREADY; goto exit; /* no DDN->PDN mode change if running */ } ds->dda_init &= ~(DDA_BASIC | DDA_STANDARD); ds->dda_init |= DDA_PDN; goto exit; case 'e': /* set buffer size */ /* -e size size is encoded in second byte */ if (error = diags_completed(ds)) goto exit; /* * check to see if we have newer at least version 2.2 roms. */ if (ds->dda_firmrev < 0x22) { error = ENOPROTOOPT; goto exit; } if (ds->dda_if.if_flags & IFF_UP) { error = EALREADY; goto exit; /* no PDN->DDN mode change if running */ } bfr_size_msg[MSG_OFFSET] = ifr->ifr_data[1]; send_config(ds, bfr_size_msg); bufreset(ifp->if_unit); goto exit; #ifdef ACP_BI case 'L': { struct dda_dnload dl; if (copyin(ifr->ifr_data, &dl, sizeof(struct dda_dnload))) { error = EFAULT; goto exit; } error = dda_dload(ifp->if_unit, &dl); goto exit; } #endif #if defined(DDA_PADOPT) && defined(WINS) case 'x': /* Preallocate UCBs for X29 -- VMS only */ printf("Preallocated %d PTYs for X29\n", prealloc_x29()); goto exit; #endif case 'z': /* reset specified front-end device, -z */ /* second parm is supposed to be uban, but ddareset doesn't care about it */ ddareset(ifp->if_unit, 0); goto exit; default: error = EINVAL; goto exit; } if ((*(ifr->ifr_data) != '0') && (ds->dda_init & DDA_PDN) && #if ACC_BSD == 42 || ACC_ULTRIX == 12 (convert_ip_addr(sin->sin_addr, (u_char *) arg2, ds) == 0) #else (convert_ip_addr(ds->dda_ipaddr, (u_char *) arg2, ds) == 0) #endif ) { error = EADDRNOTAVAIL; goto exit; } send_config(ds, acpconfig_msg); break; default: error = EINVAL; } exit: #ifndef MULTINET splx(s); #endif return (error); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAOUTPUT() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine is called by the network software when it has an */ /* IP datagram to send out this interface. An attempt is made */ /* to find a LCN which has a virtual circuit open to the */ /* indicated host. If an LCN is found the packet is queued for */ /* output on that LCN. */ /* */ /* Call: ddaoutput(ifp, m0, dst) */ /* Arguments: ifp: locates the network interface, ifnet */ /* m0: locates an mbuf buffer */ /* dst: is the socket destination address */ /* Returns: 0 for success, or one of following nonzero */ /* error indications: */ /* ENETDOWN */ /* EAFNOSUPPORT */ /* ENOBUFS */ /* Called by: network software, address of this routine is */ /* defined in the dda_if network interface struct */ /* Calls to: DDALOG() */ /* m_freem() */ /* splimp() */ /* locate_x25_lcn() */ /* IF_QFULL() */ /* IF_DROP() */ /* splx() */ /* IF_ENQUEUE() */ /* dda_start() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ ddaoutput(ifp, m0, dst) struct ifnet *ifp; struct mbuf *m0; struct sockaddr_in *dst; { register struct mbuf *m = m0; register struct dda_softc *ds = &dda_softc[ifp->if_unit]; register struct dda_cb *dc; register struct ifqueue *oq; struct mbuf *prev; int s; union imp_addr imp_addr; #ifdef DDADEBUG if (DDADBCH(1, ifp->if_unit)) { imp_addr.ip = dst->sin_addr; DDALOG(LOG_DEBUG) "dda%d: ddaoutput: dst = %d.%d.%d.%d\n", ifp->if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno DDAELOG; } #endif DDADEBUG if ((ds->dda_if.if_flags & IFF_UP) == 0) return (ENETDOWN); switch (dst->sin_family) { #ifdef INET case AF_INET: break; #endif INET default: DMESG(ifp->if_unit, 2, (DDALOG(LOG_ERR) "dda%d: can't handle af%d\n", ifp->if_unit, dst->sin_family DDAELOG)); m_freem(m0); return (EAFNOSUPPORT); } #ifdef DDADEBUG if (DDADBCH(2, ifp->if_unit)) { imp_addr.ip = dst->sin_addr; DDALOG(LOG_DEBUG) "dda%d: ddaoutput: dst = %d.%d.%d.%d\n", ifp->if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno DDAELOG; } #endif DDADEBUG /* Mod to V1.5b ==> V1.5b1 */ /* In 4.3, the IP code may pass mbuf chains with 0-length mbufs */ /* This causes "transfer count = 0" messages and might even */ /* cause actual garbage data transmission if the mbuf is at the */ /* end of the chain (we don't think it ever will be, but one */ /* can't be too sure...so we scan the chain first). */ /* WE DO ASSUME that there is at least one nonempty mbuf! */ /* (ULTRIX: we don't know, but the code is at worst harmless) */ while (m0->m_len == 0) { m = m0; m0 = m0->m_next; m->m_next = 0; m_freem(m); } /* Now we know the first mbuf (at m0) is not zero length */ prev = m0; m = m0->m_next; while (m) { if (m->m_len == 0) { prev->m_next = m->m_next; m->m_next = 0; m_freem(m); m = prev->m_next; } else { prev = m; m = m->m_next; } } m = m0; /* reset m to beginning of modified chain */ s = splimp(); /* disable interrrupts */ /* try to find an LCN */ if (dc = locate_x25_lcn(ds, dst->sin_addr)) { /* if found */ #ifdef DDADEBUG if (DDADBCH(2, ifp->if_unit)) { DDALOG(LOG_DEBUG) "dda%d: ddaoutput: lcn found = %d\n", ifp->if_unit, dc->dc_lcn DDAELOG; } #endif DDADEBUG oq = &(dc->dc_oq); /* point to output queue */ if (IF_QFULL(oq)) { /* if q full */ IF_DROP(oq); /* drop the data */ m_freem(m); ds->dda_if.if_collisions++; /* for netstat display */ splx(s); return (ENOBUFS); } IF_ENQUEUE(oq, m); /* otherwise queue it */ dda_start(ds, dc); /* and try to output */ splx(s); return (0); } else { /* if no circuit available */ IF_DROP(&ifp->if_snd); /* drop the data */ m_freem(m); splx(s); return (EHOSTUNREACH); } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDATIMER() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine is entered to perform timer management. The */ /* LCN table is scanned for active timers (nonzero) which are */ /* decremented. If a timer expires (becomes zero), the proper */ /* action is taken. */ /* */ /* */ /* Call: ddatimer(unit) */ /* Arguments: unit: ACP device unit number */ /* Returns: nothing */ /* Called by: ddainit() */ /* Calls to: splimp() */ /* send_restart() */ /* clear_lcn() */ /* splx() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ int ddatimer(unit) int unit; { register struct dda_softc *ds = &dda_softc[unit]; register struct dda_cb *dc; register int s, lcn; #ifdef DDADEBUG if (DDADBCH(3, unit)) { DDALOG(LOG_DEBUG) "dda%d: ddatimer()\n", unit DDAELOG; } #endif DDADEBUG ds->dda_if.if_timer = DDA_TIMEOUT; /* restart timer */ dc = ds->dda_cb; s = splimp(); /* LCNLINK */ for (lcn = 0; lcn <= nddach[unit]; lcn++) { /* scan all LCN's */ #ifdef DDADEBUG if (dc->dc_out_t && lcn > 0 && (--(dc->dc_out_t) == 0)) { DDALOG(LOG_DEBUG) "dda%d: write completion timeout lcn %d\n", unit, lcn DDAELOG; } #endif if (dc->dc_timer && (--(dc->dc_timer) == 0)) { /* if a timer expired */ if (dc->dc_state == LC_RESTART) { /* if a restart was out */ send_restart(ds); /* send another one */ break; } else { /* otherwise */ #ifdef DDA_PAD_OR_RAW /* if it is not an X.29 connection */ if ((dc->dc_flags & (DC_X29W | DC_X29 | DC_RAW)) == 0) #endif clear_lcn(ds, dc); /* clear the LCN */ } } dc++; } splx(s); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DIAGS_COMPLETED() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine checks to see that power up diagnostics have completed*/ /* It waits for a while if necessary. */ /* Call: diags_completed(ds) */ /* Argument: ds - pointer to softc structure; */ /* Returns: 0 if board is up, EINTR if it never came on line. */ /* Called by: ddaioctl() */ /* Calls to: */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ diags_completed(ds) struct dda_softc *ds; { int nretries = 10; /* * It's overkill to check this on EVERY ioctl, because it only matters if * we are going to touch the board. But the driver has had repeated * problems with not checking it when it should have - overkill is * preferred. The delays are here rather then in the acpconfig program * due to a bug in acpconfig. They will only be executed during * /etc/rc.local when the board has not had a chance to do the "B" * interrupt yet. At that time the machine will be running essentially * single thread so it won't really matter where the delays are. (ie, if * we put the delay into acpconfig and kept calling here 10 times, the * machine would not do anything else useful in the meantime - might as * well loop here). */ while (((ds->dda_flags & DDAF_OK) == 0) && nretries-- > 0) DELAY(3000000); if ((ds->dda_flags & DDAF_OK) == 0) /* never came on line... */ return (EINTR); else return (0); } /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL FUNCTIONS %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% SEND_CONFIG() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Send a Set System Parameters Message to the front end in */ /* response to an ACPCONFIG command from the user. */ /* */ /* Call: send_config(ds, p) */ /* Argument: ds: pointer to ACP device control structure */ /* p: pointer to the message */ /* Returns: nothing */ /* Called by: ddaioctl() */ /* Calls to: MGET() */ /* DDALOG() */ /* mtod() */ /* bcopy() */ /* sizeof() */ /* start_supr() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void send_config(ds, p) struct dda_softc *ds; u_char *p; { struct mbuf *m; register u_char *bp; int length; #ifdef DDADEBUG if (DDADBCH(7, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: send_config()\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) { DMESG(ds->dda_if.if_unit, 18, (DDALOG(LOG_ERR) "dda%d: can't get bfr for acpconfig msg\n", ds->dda_if.if_unit DDAELOG)); return; } bp = mtod(m, u_char *); /* point to data section of mbuf */ length = p[MSG_LENGTH] + MSG_OFFSET; /* msg length */ if (length > MLEN - 1) { /* * Supervisory messages have to fit in a small mbuf. The driver * itself is careful never to get in trouble this way, but now that * we have "-m" the user could. Dropping such a message is not * likely to cause any big problems, and the user can rephrase the * request. */ DMESG(ds->dda_if.if_unit, 19, (DDALOG(LOG_ERR) "dda%d: supervisor message too long\n", ds->dda_if.if_unit DDAELOG)); m->m_next = 0; m_freem(m); return; } bcopy(p, bp, length); m->m_len = length; /* set msg length */ #ifdef DDADEBUG if (DDADBCH(8, ds->dda_if.if_unit)) { prt_bytes(ds->dda_if.if_unit, "send_config", bp, length); } #endif DDADEBUG start_supr(ds, m); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% LOCATE_X25_LCN() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine tries to locate an X25 LCN associated with a */ /* remote internet address. A linear search of the LCN table */ /* is made for a matching address. If the search succeeds, the */ /* LCN is returned. If the search fails, the LCN table is */ /* searched for an unused table entry. If an unused table */ /* entry is found, an X25 call is generated to the host */ /* specified in the destination internet address. If no LCN is */ /* available, zero is returned. */ /* */ /* Call: locate_x25_lcn(ds, ip_addr) */ /* Argument: ds: pointer to dev control block struct */ /* ip_addr: IP address */ /* Returns: pointer to the dda control block which */ /* contains LCN, else zero for failure */ /* Called by: ddaoutput() */ /* Calls to: convert_ip_addr() */ /* make_x25_call() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE struct dda_cb * locate_x25_lcn(ds, ip_addr) struct dda_softc *ds; struct in_addr ip_addr; { register int lcn, maxlcn; register struct dda_cb *dc; union imp_addr imp_addr; #ifdef DDADEBUG if (DDADBCH(9, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: locate_x25_lcn()\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG imp_addr.ip = ip_addr; /* DDN X.25 doesn't know net number */ if (!(ds->dda_init & DDA_PDN)) { if ((imp_addr.imp.s_net & 0x80) == 0x00) { /* class A */ imp_addr.imp.s_net = 0; imp_addr.imp.s_lh = 0; } else if ((imp_addr.imp.s_net & 0xc0) == 0x80) { /* class B */ imp_addr.imp.s_net = 0; imp_addr.imp.s_host = 0; } else { /* class C, should check for class C */ imp_addr.imp.s_net = 0; imp_addr.imp.s_host = 0; imp_addr.imp.s_lh = 0; } } /* LCNLINK */ maxlcn = nddach[ds->dda_if.if_unit]; dc = &(ds->dda_cb[1]); /* scan LCN table for addr match */ for (lcn = 1; lcn <= maxlcn; lcn++) { if ((dc->dc_key.key_addr.s_addr == imp_addr.ip.s_addr) && (dc->dc_state == LC_CALL_PENDING || dc->dc_state == LC_DATA_IDLE)) return (dc); /* return LCN */ dc++; } if ((dc = find_free_lcn(ds)) == 0) return (0); ddacb_user_data[0] = (u_char) 0; /* we have no user data */ if (convert_ip_addr(ip_addr, ddacb_called_addr, ds) && make_x25_call(ds, dc, ip_addr, X25_PROTO_IP)) { /* addr can be converted */ dc->dc_inaddr = ip_addr; /* store dest ip addr */ dc->dc_key.key_addr.s_addr = imp_addr.ip.s_addr; /* store match key */ #ifdef DDADEBUG if (DDADBCH(9, ds->dda_if.if_unit)) { imp_addr.ip = ip_addr; DDALOG(LOG_DEBUG) "dda%d: locate_x25_lcn: made call to %d.%d.%d.%d\n", ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno DDAELOG; } #endif DDADEBUG return (dc); /* and return the LCN */ } else { return (0); /* give up */ } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% FIND_FREE_LCN() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine tries to locate a free X25 LCN. */ /* The LCN table is searched for an unused entry. */ /* If no LCN is available, zero is returned. */ /* */ /* Call: find_free_lcn(ds) */ /* Argument: ds: pointer to dev control block struct */ /* Returns: pointer to the dda control block which */ /* contains LCN, else zero for failure */ /* Called by: locate_x25_lcn() */ /* supr_msg() */ /* xxcntl() */ /* Calls to: DDALOG() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE struct dda_cb * find_free_lcn(ds) struct dda_softc *ds; { struct dda_cb *dc; register int lcn, maxlcn, dwnflg = 0; /* LCNLINK */ dc = &(ds->dda_cb[1]); /* scan LCN table for free entry */ maxlcn = nddach[ds->dda_if.if_unit]; for (lcn = 1; lcn <= maxlcn; lcn++) { #ifdef DDA_PAD_OR_RAW if (dc->dc_state == LC_IDLE && (dc->dc_flags & (DC_X29W | DC_X29 | DC_RAW)) == 0) #else if (dc->dc_state == LC_IDLE) #endif DDA_PAD_OR_RAW break; else if (dc->dc_state == LC_RESTART || dc->dc_state == LC_DOWN) dwnflg = 1; dc++; } /* LCNLINK */ if (lcn > maxlcn) { /* if we didn't find a free entry */ if (LOG_BUSY) { if (dwnflg) DDALOG(LOG_ERR) "dda%d: no circuits available (link not up)\n", ds->dda_if.if_unit DDAELOG; else DDALOG(LOG_ERR) "dda%d: all circuits in use\n", ds->dda_if.if_unit DDAELOG; } return (0); /* return empty handed */ } return (dc); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% CONVERT_IP_ADDR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Based on the type of X.25 service, this routine performs */ /* one of two functions. For PDN X.25 service, the address */ /* translation table is searched for presence of local X.25 */ /* address (which was entered by the user via acpconfig). */ /* */ /* For DDN X.25 service, this routine accepts an internet */ /* address and attempts to translate to an equivalent X25 */ /* address. This follows the guidelines in the DDN X25 */ /* interface spec. The resultant X25 address is stored in the */ /* X25 called addr buffer. */ /* */ /* NOTE: Although front end was designed to accept ASCII coded */ /* digits for the address fields, we only supply the binary */ /* values. The front end only uses low four bits to extract */ /* the binary value from the ASCII digits, so this works out. */ /* */ /* */ /* */ /* */ /* */ /* */ /* Call: convert_ip_addr(ip_addr, x25addr, ds) */ /* Argument: ip_addr: IP address */ /* x25addr: X.25 address */ /* ds: &dda_softc[unit] */ /* Returns: 1 for success */ /* Called by: locate_x25_lcn() */ /* make_x25_call() */ /* ddaioctl() */ /* Calls to: bcopy() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE boolean convert_ip_addr(ip_addr, x25addr, ds) struct in_addr ip_addr; u_char x25addr[]; struct dda_softc *ds; { register struct dda_addr_tr *atp, *hip, *lop; register int temp; union imp_addr imp_addr; /****************************************************************/ /* processing for Public Data Network (PDN) X.25 service */ /* search address translation table for local address */ /****************************************************************/ if (ds->dda_init & DDA_PDN) { x25addr[0] = 0; /* clear result X.25 address length */ lop = &dda_addr_tr[ds->dda_if.if_unit][0]; /* set up for binary * search */ hip = &dda_addr_tr[ds->dda_if.if_unit][dda_num_addr_tr[ds->dda_if.if_unit]]; while (lop < hip - 1) { /* binary search loop */ atp = lop + (hip - lop) / 2; if (atp->ip_addr > ip_addr.s_addr) hip = atp; else lop = atp; } if (lop->ip_addr == ip_addr.s_addr) bcopy(lop->x25_addr, x25addr, MAXADDRLEN); } /****************************************************************/ /* processing for DDN Standard or Basic X.25 service */ /* convert IP address to X.25 address */ /****************************************************************/ else { int imp_no, imp_port; imp_addr.ip = ip_addr; if ((imp_addr.imp.s_net & 0x80) == 0x00) { /* class A */ imp_no = imp_addr.imp.s_impno; imp_port = imp_addr.imp.s_host; } else if ((imp_addr.imp.s_net & 0xc0) == 0x80) { /* class B */ imp_no = imp_addr.imp.s_impno; imp_port = imp_addr.imp.s_lh; } else { /* class C */ imp_no = imp_addr.imp.s_impno / 32; imp_port = imp_addr.imp.s_impno % 32; } x25addr[0] = 12; /* set addr length */ x25addr[1] = 0; /* clear DNIC */ x25addr[2] = 0; x25addr[3] = 0; x25addr[4] = 0; if (imp_port < 64) { /* Physical: 0000 0 IIIHH00 [SS] *//* s_impno * -> III, s_host -> HH */ x25addr[5] = 0; /* set flag bit */ x25addr[6] = imp_no / 100; x25addr[7] = (imp_no % 100) / 10; x25addr[8] = imp_no % 10; x25addr[9] = imp_port / 10; x25addr[10] = imp_port % 10; } else { /* Logical: 0000 1 RRRRR00 [SS] *//* s * _host * 256 + s_impno -> RRRRR */ temp = (imp_port << 8) + imp_no; x25addr[5] = 1; x25addr[6] = temp / 10000; x25addr[7] = (temp % 10000) / 1000; x25addr[8] = (temp % 1000) / 100; x25addr[9] = (temp % 100) / 10; x25addr[10] = temp % 10; } x25addr[11] = 0; /* clear rest of addr */ x25addr[12] = 0; } #ifdef DDADEBUG if (DDADBCH(11, ds->dda_if.if_unit)) { imp_addr.ip = ip_addr; DDALOG(LOG_DEBUG) "dda%d: convert_ip_addr: %d.%d.%d.%d ==> %s\n", ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno, fmt_x25(&x25addr[1], (int) x25addr[0]) DDAELOG; } #endif DDADEBUG return (x25addr[0] ? 1 : 0); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% CONVERT_X25_ADDR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine accepts an X25 address and attempts to */ /* translate to an equivalent internet address. For DDA this */ /* follows the guidelines in the DDA X25 interface spec. The */ /* resultant internet address is returned to the caller. */ /* */ /* Call: convert_x25_addr(x25addr, ds) */ /* Argument: x25addr: X.25 address */ /* ds: &dda_softc[unit] */ /* dc: pointer to allocated dda_cb structure */ /* Returns: IP address for success, else zero for fail */ /* Called by: supr_msg() */ /* Calls to: DDALOG() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE u_long convert_x25_addr(x25addr, ds, dc) u_char x25addr[]; struct dda_softc *ds; struct dda_cb *dc; { register int cnt, temp; union imp_addr imp_addr; register struct dda_addr_tr *atp; dc->dc_inaddr.s_addr = imp_addr.ip.s_addr = 0L; if (ds->dda_init & DDA_PDN) { for (atp = &dda_addr_tr[ds->dda_if.if_unit][0]; atp < &dda_addr_tr[ds->dda_if.if_unit][dda_num_addr_tr[ds->dda_if.if_unit]]; atp++) { if (bcmp((char *) atp->x25_addr, (char *) x25addr, x25addr[0] + 1) == 0) { /* set key address and print address up */ dc->dc_inaddr.s_addr = imp_addr.ip.s_addr = atp->ip_addr; break; } } } else { int imp_no, imp_port; struct in_addr my_addr; my_addr = ds->dda_ipaddr; if (((cnt = x25addr[0]) < MINADDRLEN - 1) || (cnt > MAXADDRLEN - 1)) { DMESG(ds->dda_if.if_unit, 20, (DDALOG(LOG_ERR) "dda%d: illegal X25 address length!\n", ds->dda_if.if_unit DDAELOG)); return (0L); } switch (x25addr[5] & 0x0f) { case 0: /* Physical: 0000 0 IIIHH00 [SS] */ imp_no = ((int) (x25addr[6] & 0x0f) * 100) + ((int) (x25addr[7] & 0x0f) * 10) + ((int) (x25addr[8] & 0x0f)); imp_port = ((int) (x25addr[9] & 0x0f) * 10) + ((int) (x25addr[10] & 0x0f)); break; case 1: /* Logical: 0000 1 RRRRR00 [SS] */ temp = ((int) (x25addr[6] & 0x0f) * 10000) + ((int) (x25addr[7] & 0x0f) * 1000) + ((int) (x25addr[8] & 0x0f) * 100) + ((int) (x25addr[9] & 0x0f) * 10) + ((int) (x25addr[10] & 0x0f)); imp_port = temp >> 8; imp_no = temp & 0xff; break; default: DMESG(ds->dda_if.if_unit, 21, (DDALOG(LOG_ERR) "dda%d: illegal X25 address format!\n", ds->dda_if.if_unit DDAELOG)); return (0L); } dc->dc_inaddr = imp_addr.ip = my_addr; /* use local net number */ if ((imp_addr.imp.s_net & 0x80) == 0x00) { /* class A */ imp_addr.imp.s_net = 0; /* mask net number */ imp_addr.imp.s_lh = 0; /* mask logical host */ imp_addr.imp.s_host = imp_port; ((union imp_addr *) & dc->dc_inaddr.s_addr)->imp.s_host = imp_port; imp_addr.imp.s_impno = imp_no; ((union imp_addr *) & dc->dc_inaddr.s_addr)->imp.s_impno = imp_no; } else if ((imp_addr.imp.s_net & 0xc0) == 0x80) { /* class B */ imp_addr.imp.s_net = 0; imp_addr.imp.s_lh = imp_port; ((union imp_addr *) & dc->dc_inaddr.s_addr)->imp.s_lh = imp_port; imp_addr.imp.s_host = 0; imp_addr.imp.s_impno = imp_no; ((union imp_addr *) & dc->dc_inaddr.s_addr)->imp.s_impno = imp_no; } else { /* class C */ imp_addr.imp.s_impno = (imp_no << 5) + imp_port; ((union imp_addr *) & dc->dc_inaddr.s_addr)->imp.s_impno = imp_addr.imp.s_impno; imp_addr.imp.s_lh = 0; imp_addr.imp.s_host = 0; imp_addr.imp.s_net = 0; } } #ifdef DDADEBUG if (DDADBCH(12, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: convert_x25_addr: %s ==> %d.%d.%d.%d\n", ds->dda_if.if_unit, fmt_x25(&x25addr[1], (int) x25addr[0]), imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno DDAELOG; } #endif DDADEBUG return (imp_addr.ip.s_addr); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% MAKE_X25_CALL() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine places an X25 call using the X25 Call Msg */ /* buffer. The calling LCN is placed in the appropriate state */ /* and a timer is started. Based on dda_init flag, implement */ /* DDN standard or basic service. (If PDN mode is set, then */ /* the logic for basic service is followed.) */ /* */ /* Call: make_x25_call(ds, dc, ip_addr, proto */ /* udlen, ud) */ /* Arguments: ds: pointer to device control structure */ /* dc: pointer to the Logical Channel control */ /* block structure */ /* ip_addr: callee's ip address */ /* proto: protocol identifier byte */ /* udlen: user data length */ /* ud: user data */ /* Returns: one for success, zero for failure */ /* Called by: locate_x25_lcn() */ /* Calls to: MGET() */ /* mtod() */ /* convert_ip_addr() */ /* bcopy() */ /* IF_ENQUEUE() */ /* start_supr() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE boolean make_x25_call(ds, dc, ip_addr, proto, udlen, ud) register struct dda_softc *ds; register struct dda_cb *dc; struct in_addr ip_addr; u_char proto; u_char udlen; u_char *ud; { register struct mbuf *m_callbfr; register u_char *cb; union imp_addr imp_addr; #if ACC_BSD == 42 || ACC_ULTRIX == 12 struct sockaddr_in *our_addr; #endif #ifdef DDADEBUG if (DDADBCH(13, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: make_x25_call: lcn used = %d\n", ds->dda_if.if_unit, dc->dc_lcn DDAELOG; } #endif DDADEBUG MGET(m_callbfr, M_DONTWAIT, MT_DATA); /* try to get call cmnd * buffer */ if (m_callbfr == 0) { DMESG(ds->dda_if.if_unit, 22, (DDALOG(LOG_ERR) "dda%d: couldn't get mbuf for call command\n", ds->dda_if.if_unit DDAELOG)); return (0); } cb = mtod(m_callbfr, u_char *); if (ds->dda_net_id == TRANSPAC) { ddacb_calling_addr[0] = 0; /* send a 0 length calling address */ } else { #if ACC_BSD == 42 || ACC_ULTRIX == 12 our_addr = (struct sockaddr_in *) & (ds->dda_if.if_addr); (void) convert_ip_addr(our_addr->sin_addr, ddacb_calling_addr, ds); #else (void) convert_ip_addr(ds->dda_ipaddr, ddacb_calling_addr, ds); #endif } ddacb_protocol[0] = 4; ddacb_protocol[1] = proto; /* protocol type */ ddacb_protocol[2] = 0; ddacb_protocol[3] = 0; ddacb_protocol[4] = 0; /* * CCITT standard facilities must precede DDN specific facilities See BBN * report 5476 section 2.1.2. Firmware preceding rev 0x20 does not * support packet size / window negotiation. */ ddacb_facilities[0] = 0; /* initialize facilities length */ if (ds->dda_firmrev >= 0x21) { ddacb_facilities[0] = 0; if (ds->dda_init & DDA_PKTNEG) { int n = ddacb_facilities[0]; /* length so far */ ddacb_facilities[n + 1] = X25_FACIL_PKTSIZE; ddacb_facilities[n + 2] = PKTSIZE_LARGE; ddacb_facilities[n + 3] = PKTSIZE_LARGE; ddacb_facilities[0] += 3; } if (ds->dda_init & DDA_WNDNEG) { int n = ddacb_facilities[0]; /* length so far */ ddacb_facilities[n + 1] = X25_FACIL_WINSIZE; ddacb_facilities[n + 2] = WINSIZE_LARGE; ddacb_facilities[n + 3] = WINSIZE_LARGE; ddacb_facilities[0] += 3; } } if ((ds->dda_init & (DDA_BASIC | DDA_PDN)) == 0) { /* DDN standard mode, * tell callee */ int n = ddacb_facilities[0]; /* length so far */ ddacb_facilities[0] += 4; /* additional facility bytes */ ddacb_facilities[n + 1] = DDN_FACIL_MARKER; /* end of CCITT stuff, */ ddacb_facilities[n + 2] = DDN_FACIL_MARKER; /* and start DDN local */ ddacb_facilities[n + 3] = X25_FACIL_DDA; /* DDA standard mode */ ddacb_facilities[n + 4] = FAC_DDASTD; } ddacb_cmnd[0] = CALL; /* set command code */ ddacb_cmnd[1] = dc->dc_lcn << 1; /* set channel id */ ddacb_cmnd[2] = 0; ddacb_cmnd[3] = (ddacb_called_addr[0] + 1) + /* tally cmnd ext len */ (ddacb_calling_addr[0] + 1) + (ddacb_protocol[0] + 1) + (ddacb_facilities[0] + 1) + (ddacb_user_data[0] + 1); if ((unsigned) ddacb_cmnd[3] + 4 > MLEN) { DMESG(ds->dda_if.if_unit, 38, (DDALOG(LOG_ERR) "dda%d: make_x25_call message too large for mbuf (%d bytes)\n", ds->dda_if.if_unit, (unsigned) ddacb_cmnd[3] + 4 DDAELOG)); return 0; /* failure */ } m_callbfr->m_len = ddacb_cmnd[3] + 4; /* copy command header */ bcopy(ddacb_cmnd, cb, 4); cb += 4; /* copy called address */ bcopy(ddacb_called_addr, cb, ddacb_called_addr[0] + 1); cb += (ddacb_called_addr[0] + 1); /* copy calling address */ bcopy(ddacb_calling_addr, cb, ddacb_calling_addr[0] + 1); cb += (ddacb_calling_addr[0] + 1); /* copy protocol */ bcopy(ddacb_protocol, cb, ddacb_protocol[0] + 1); cb += (ddacb_protocol[0] + 1); /* copy facilities */ bcopy(ddacb_facilities, cb, ddacb_facilities[0] + 1); cb += (ddacb_facilities[0] + 1); /* copy user data */ bcopy(ddacb_user_data, cb, ddacb_user_data[0] + 1); cb += (ddacb_user_data[0] + 1); hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_CALL_PENDING); dc->dc_state = LC_CALL_PENDING; /* set state */ dc->dc_timer = TMO_CALL_PENDING; /* start call timeout */ #ifdef DDADEBUG if (DDADBCH(13, ds->dda_if.if_unit)) { prt_bytes(ds->dda_if.if_unit, "make_x25_call: call_bfr", mtod(m_callbfr, u_char *), m_callbfr->m_len); } #endif DDADEBUG if (LOG_CALLS) { imp_addr.ip = ip_addr; DDALOG(LOG_ERR) "dda%d: Calling %d.%d.%d.%d (%s) on lcn %d\n", ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno, fmt_x25(&ddacb_called_addr[1], (int) ddacb_called_addr[0]), dc->dc_lcn DDAELOG; } start_supr(ds, m_callbfr); return (1); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDA_START() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine attempts to start output of data queued on a */ /* specific LCN. If the LCN was not already busy and data is */ /* available for output, the data is copied into the LCN's I/O */ /* buffer and a write request queued to the ACP device. Data */ /* is passed in mbuf(s) from IP to ddaoutput(), ddaoutput() */ /* queues the data, and the data is dequeued here. */ /* */ /* Call: dda_start(ds, dc) */ /* Arguments: ds: pointer to device control structure */ /* dc: pointer to the Logical Channel control */ /* block structure */ /* Returns: nothing */ /* Called by: ddaoutput() */ /* x25_init() */ /* make_x25_call() */ /* supr_msg() */ /* send_supr() */ /* dda_data() */ /* dda_supr() */ /* Calls to: IF_DEQUEUE() */ /* dda_wrq() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void dda_start(ds, dc) register struct dda_softc *ds; register struct dda_cb *dc; { register struct mbuf *m; register struct hdx_chan *hc = &dc->dc_wchan; register int s; #ifdef DDADEBUG if (DDADBCH(14, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: dda_start()\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG /* * If output isn't active, attempt to start sending a new packet. */ if ((dc->dc_flags & DC_OBUSY) || (dc->dc_oq.ifq_len == 0) || ((dc->dc_lcn != 0) && (dc->dc_state != LC_DATA_IDLE))) { return; } if (dc->dc_lcn != 0) dc->dc_timer = tmo_data_idle; /* * Raise priority whenever touching dc_oq because * the mbufs on this queue may be asynchronously * freed upon receipt of a line status msg, restart, * clear, or reset. */ s = splimp(); IF_DEQUEUE(&dc->dc_oq, m); splx(s); if (m == 0) { /* XXX this is a bug catcher XXX */ DMESG(ds->dda_if.if_unit, 24, (DDALOG(LOG_ERR) "dda%d: dequeued NULL mbuf in IP output chain!\n", ds->dda_if.if_unit DDAELOG)); DMESG(ds->dda_if.if_unit, 24, (DDALOG(LOG_ERR) "RESET dda%d MANUALLY: use /etc/acpconfig dda%d -z\n", ds->dda_if.if_unit, ds->dda_if.if_unit DDAELOG)); ds->dda_if.if_flags &= ~(IFF_RUNNING | IFF_UP); hist_link_state(ds->dda_if.if_unit, ds->dda_state, S_DISABLED); ds->dda_state = S_DISABLED; dda_disable(ds->dda_if.if_unit); return; } s = splimp(); hc->hc_mbuf = m; hc->hc_curr = m; #ifdef DDA_PAD_OR_RAW /* crufty kludge to get the Qbit */ if (dc->dc_flags & (DC_X29 | DC_X29W | DC_RAW)) { /* raw or x29? */ if (m->m_len < (MLEN - 1)) /* small mbuf? */ hc->hc_sbfc = m->m_dat[MLEN - 1]; /* ok, get the subfunc byte */ else hc->hc_sbfc = 0; /* subfunc must be zero for large buffers */ } else hc->hc_sbfc = 0; /* subfunc must be zero for ip buffers */ #else hc->hc_sbfc = 0; #endif splx(s); dc->dc_flags |= DC_OBUSY; dda_wrq(ds, hc, 0); /* write request to ACP */ } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDA_DATA() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine is called when a data channel I/O completes. */ /* If the completion was for a write, an attempt is made to */ /* start output on the next packet waiting for output on that */ /* LCN. If the completion was for a read, the received packet */ /* is sent to the IP input queue (if no error) and another read */ /* is started on the LCN. */ /* */ /* Call: dda_data(ds, hc, cc, cnt) */ /* Argument: ds: device control block */ /* hc: half duplex channel control block */ /* cc: Mailbox I/O completion status */ /* cnt: byte count */ /* Returns: nothing */ /* Called by: ddainta() */ /* Calls to: m_freem() */ /* dda_start() */ /* IF_QFULL() */ /* IF_DROP() */ /* IF_ENQUEUE() */ /* schednetisr() */ /* dda_rrq() */ /* dda_wrq() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void dda_data(ds, hc, cc, cnt) register struct dda_softc *ds; register struct hdx_chan *hc; int cc, cnt; { register struct dda_cb *dc = &(ds->dda_cb[hc->hc_chan / 2]); struct ifqueue *inq = &ipintrq; /* IP input queue */ /* note that this routine is a weird case in which Ultrix 2.0 behaves like * a 4.2 system rather than a 4.3 system. This is reflected in the structure * of conditional compilation segments. */ #if ACC_BSD > 42 /* 4.3bsd or newer */ register struct mbuf *m, *mb; struct ifnet *ifp; #else /* 4.2, or all flavors of Ultrix */ register struct mbuf *m; #endif #ifdef DDADEBUG if (DDADBCH(18, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: dda_data: chan=%d cc=%x cnt=%x\n", ds->dda_if.if_unit, hc->hc_chan, cc, cnt DDAELOG; } #endif DDADEBUG #if ACC_BSD > 42 ifp = &ds->dda_if; #endif if (hc->hc_chan & 0x01) { /* was it read or write? *//* write, fire up * next output */ #ifdef DDADEBUG dc->dc_out_t = TMO_OFF; /* turn off output completion timer */ #endif hc = &dc->dc_wchan; if ((hc->hc_func != DDAABT) && (hc->hc_curr = hc->hc_curr->m_next)) dda_wrq(ds, hc, 0); else { m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; if (hc->hc_func == DDAABT) { hc->hc_func &= ~DDAABT; hc->hc_inv &= ~INVALID_MBUF; } else ds->dda_if.if_opackets++; dc->dc_flags &= ~DC_OBUSY; dda_start(ds, dc); } } else { /* read, process rcvd packet */ hc = &dc->dc_rchan; #ifdef DDADEBUG if (DDADBCH(19, ds->dda_if.if_unit)) { u_char *p; p = mtod(hc->hc_curr, u_char *); prt_bytes(ds->dda_if.if_unit, "received data", p, (cnt < 64 ? cnt : 64)); } #endif DDADEBUG if (cc == DDAIOCOK) { /* Queue good packet for input */ #ifdef DDADEBUG if (DDADBCH(19, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: dda_data: chan=%d DDAIOCOK\n", ds->dda_if.if_unit, hc->hc_chan DDAELOG; } #endif DDADEBUG ds->dda_if.if_ipackets++; hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_DATA_IDLE); if (dc->dc_state == LC_DATA_IDLE) dc->dc_timer = tmo_data_idle; hc->hc_curr->m_len += cnt; /* update byte count */ m = hc->hc_mbuf; /* que mbuf chain */ #if ACC_BSD > 42 /* Prepend ifp pointer for 4.3 */ MGET(mb, M_DONTWAIT, MT_DATA); if (mb == 0) { DMESG(ds->dda_if.if_unit, 26, (DDALOG(LOG_ERR) "dda%d: couldn't get mbuf for ifp header\n", ds->dda_if.if_unit DDAELOG)); m_freem(m); return; } *(mtod(mb, struct ifnet **)) = ifp; mb->m_len = sizeof(ifp); mb->m_next = m; if (IF_QFULL(inq)) { IF_DROP(inq); m_freem(mb); } else { IF_ENQUEUE(inq, mb); schednetisr(NETISR_IP); } #else if (IF_QFULL(inq)) { IF_DROP(inq); m_freem(m); } else { IF_ENQUEUE(inq, m); schednetisr(NETISR_IP); } #endif hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } else if (cc == DDAIOCOKP) { /* good completion, more data pending */ hc->hc_curr->m_len += cnt; } else { m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } /* hang a new data read */ dda_rrq(ds, hc); } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDA_SUPR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine is called when a supervisor I/O completes. */ /* If the completion was for a write, an attempt is made to */ /* start output on the next supervisor command waiting for */ /* output. If the completion was for a read, the received */ /* supervisor message is processed and another read is started. */ /* */ /* Call: dda_supr(ds, hc, cc) */ /* Argument: ds: device control block */ /* hc: half duplex channel control block */ /* cc: Mailbox I/O completion status */ /* Returns: nothing */ /* Called by: ddainta() */ /* Calls to: dda_start() */ /* mtod() */ /* supr_msg() */ /* m_free() */ /* dda_rrq() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void dda_supr(ds, hc, cc, cnt) register struct dda_softc *ds; struct hdx_chan *hc; int cc; int cnt; { register struct dda_cb *dc = &(ds->dda_cb[hc->hc_chan / 2]); u_char *p; #ifdef DDADEBUG if (DDADBCH(20, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: dda_supr: chan=%d cc=%x\n", ds->dda_if.if_unit, hc->hc_chan, cc DDAELOG; } #endif DDADEBUG /* an odd-numbered channel indicates a write */ /* the supr msg is assumed to be in 1 mbuf */ if (hc->hc_chan & 0x01) { m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; dc->dc_flags &= ~DC_OBUSY; dda_start(ds, dc); } /* otherwise, process the read */ else { if (cc == DDAIOCOK) { p = mtod(hc->hc_curr, u_char *); /* point to data in mbuf */ #ifdef DDA_PAD_OR_RAW switch (dda_decode_type(ds, p)) { # ifdef DDA_PADOPT case 1: # ifdef DDADEBUG if (DDADBCH(20, ds->dda_if.if_unit)) { printf("dda%d: dda_supr(): case 1: chan = %x, p = %x\n", ds->dda_if.if_unit, hc->hc_chan, *p); } # endif DDADEBUG x29_supr(ds, p); break; # endif # ifdef DDA_RAWOPT case 2: # ifdef DDADEBUG if (DDADBCH(20, ds->dda_if.if_unit)) { printf("dda%d: dda_supr(): case 2: chan = %x, p = %x\n", ds->dda_if.if_unit, hc->hc_chan, *p); } # endif DDADEBUG hc->hc_curr->m_len += cnt; pi_supr(ds, hc->hc_curr); /* don't free mbuf here */ hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; dda_rrq(ds, hc);/* hang a new supr read */ return; # endif default: supr_msg(ds, p);/* process supervisor message */ break; } #else DDA_PAD_OR_RAW supr_msg(ds, p); /* process supervisor message */ #endif DDA_PAD_OR_RAW } else if (cc == DDAIOCOKP) { DMESG(ds->dda_if.if_unit, 28, (DDALOG(LOG_ERR) "dda%d: truncated supervisor message\n", ds->dda_if.if_unit DDAELOG)); } m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; dda_rrq(ds, hc); /* hang a new supr read */ } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% SUPR_MSG() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine processes received supervisor messages. */ /* Depending on the message type, the appropriate action is */ /* taken. */ /* */ /* Call: supr_msg(ds, p) */ /* Arguments: ds: pointer to dev control block struct */ /* p: pointer to a character array */ /* containing the supervisor message */ /* Returns: nothing */ /* Called by: dda_supr() */ /* Calls to: DDALOG() */ /* IF_DEQUEUE() */ /* m_freem() */ /* send_restart() */ /* send_supr() */ /* dda_start() */ /* decode_ring() */ /* decode_answer() */ /* convert_x25_addr() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void supr_msg(ds, p) struct dda_softc *ds; u_char p[]; { register struct dda_cb *dc; register int lcn; register int maxlcn; union imp_addr imp_addr; #ifdef DDADEBUG if (DDADBCH(21, ds->dda_if.if_unit)) { prt_bytes(ds->dda_if.if_unit, "supr_msg", p, (int) (4 + p[3])); } #endif DDADEBUG maxlcn = nddach[ds->dda_if.if_unit]; /* obtain SVC limit */ switch (p[0]) { case LINE_STATUS: /* link status msg */ if (p[2] == LINK_UP) { /* if link came up */ #ifdef DDADEBUG if (DDADBCH(21, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: supr_msg: HDLC link up\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG send_restart(ds); /* send restart msg */ ds->dda_state = S_COMING_UP; } else { /* if link went down */ ds->dda_if.if_flags &= ~IFF_UP; /* ? should call if_down() ? */ hist_link_state(ds->dda_if.if_unit, ds->dda_state, S_DISABLED); ds->dda_state = S_DISABLED; dc = ds->dda_cb; /* LCNLINK */ for (lcn = 0; lcn <= maxlcn; lcn++) { /* for all LCN's */ dc->dc_inaddr.s_addr = 0; /* forget dest address */ dc->dc_key.key_addr.s_addr = 0; dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; dc->dc_state = LC_DOWN; /* set state */ dc->dc_timer = TMO_OFF; /* stop timer */ dc++; } hist_all_lcns(ds->dda_if.if_unit, LC_DOWN); abort_io(ds->dda_if.if_unit, ALL_CHANS); #ifdef DDA_PADOPT x29_init(ds->dda_if.if_unit, 1); #endif if (p[2] == LINK_DISABLED) /* link disabled */ DMESG(ds->dda_if.if_unit, 29, (DDALOG(LOG_ERR) "dda%d: link disabled\n", ds->dda_if.if_unit DDAELOG)); else DMESG(ds->dda_if.if_unit, 30, (DDALOG(LOG_ERR) "dda%d: link down\n", ds->dda_if.if_unit DDAELOG)); } break; case RESTART: /* restart received */ if (ds->dda_cb[0].dc_state != LC_RESTART) { /* if not restarting */ #ifdef DDADEBUG if (DDADBCH(21, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: supr_msg: RESTART rcvd, no RESTART pending\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG send_supr(ds, RSTRT_ACK, 0, 0); /* send restart ack */ } /* fall thru */ case RSTRT_ACK: /* restart ack */ if ((ds->dda_state == S_COMING_UP) || (ds->dda_state == S_LINK_UP)) { if (p[0] == RSTRT_ACK) { DMESG(ds->dda_if.if_unit, 31, (DDALOG(LOG_ERR) "dda%d: Restart Ack received\n", ds->dda_if.if_unit DDAELOG)); } else { /* restart. print cause and diagnostic. */ DMESG(ds->dda_if.if_unit, 31, (DDALOG(LOG_ERR) "dda%d: Restart (%x %x) received\n", ds->dda_if.if_unit, p[2], p[3] ? p[4] : 0 DDAELOG)); } ds->dda_if.if_flags |= IFF_UP; hist_link_state(ds->dda_if.if_unit, ds->dda_state, S_LINK_UP); ds->dda_state = S_LINK_UP; dc = ds->dda_cb; /* LCNLINK */ for (lcn = 0; lcn <= maxlcn; lcn++) { /* for all LCN's */ dc->dc_state = LC_IDLE; /* set state */ dc->dc_timer = TMO_OFF; /* stop timer */ dc->dc_inaddr.s_addr = 0; /* forget address */ dc->dc_key.key_addr.s_addr = 0; dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; dc++; } hist_all_lcns(ds->dda_if.if_unit, LC_IDLE); abort_io(ds->dda_if.if_unit, ALL_CHANS); DMESG(ds->dda_if.if_unit, 32, (DDALOG(LOG_ERR) "dda%d: (%s rev %d.%d) link up\n", ds->dda_if.if_unit, dda_product, (ds->dda_firmrev >> 4) & 0xF, ds->dda_firmrev & 0xF DDAELOG)); #ifdef DDA_PAD_OR_RAW x29_init(ds->dda_if.if_unit, 1); /* * wake up all processes that tried to open a x29 device but * slept because the board was not up */ wakeup(&ds->dda_state); #endif DDA_PAD_OR_RAW } else #ifdef DDADEBUG if (DDADBCH(21, ds->dda_if.if_unit)) { DDALOG(LOG_ERR) "dda%d: Unexpected RESTART in state %x\n", ds->dda_if.if_unit, ds->dda_state DDAELOG; } #endif DDADEBUG break; case ANSWER: /* call answered */ lcn = p[1] / 2; dc = &(ds->dda_cb[lcn]); if (dc->dc_state == LC_CALL_PENDING) { /* if a call pending */ hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_DATA_IDLE); decode_answer(p, dc); dc->dc_state = LC_DATA_IDLE; /* set state */ dc->dc_timer = tmo_data_idle; /* start timer */ dda_start(ds, dc); /* try to send data */ } if (LOG_CALLS) { DDALOG(LOG_ERR) "dda%d: lcn %d connected\n", ds->dda_if.if_unit, lcn DDAELOG; } break; case RING: /* incoming call */ /* if ring looks ok, and we find a free LCN to assign */ if (decode_ring(p) && (dc = find_free_lcn(ds))) { dc->dc_key.key_addr.s_addr = convert_x25_addr(ddacb_calling_addr, ds, dc); #ifdef DDADEBUG if (DDADBCH(21, ds->dda_if.if_unit)) { imp_addr.ip = dc->dc_inaddr; DDALOG(LOG_DEBUG) "dda%d: supr_msg: got call from %d.%d.%d.%d\n", ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno DDAELOG; } #endif DDADEBUG hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_DATA_IDLE); dc->dc_state = LC_DATA_IDLE; /* set state */ dc->dc_timer = tmo_data_idle; /* start timer */ dc->dc_pktsizein = 0; dc->dc_pktsizeout = 0; dc->dc_wsizein = 0; dc->dc_wsizeout = 0; send_supr(ds, ANSWER, (int) dc->dc_lcn * 2, (int) p[2]); /* send answer */ if (LOG_CALLS) { imp_addr.ip = dc->dc_inaddr; DDALOG(LOG_ERR) "dda%d: Accepting call from %d.%d.%d.%d (%s) on lcn %d\n", ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno, fmt_x25(&ddacb_calling_addr[1], (int) ddacb_calling_addr[0]), dc->dc_lcn DDAELOG; } } else { /* if no free LCN's */ send_supr(ds, CLEARVC, p[2], 0); /* clear call */ if (LOG_CALLS) { DDALOG(LOG_ERR) "dda%d: Rejecting call from %s on VC 0x%x\n", ds->dda_if.if_unit, fmt_x25(&ddacb_calling_addr[1], ddacb_calling_addr[0]), p[1] DDAELOG; } } break; case CLEARLC: /* clear by LCN */ /* * This could mean one of three things: If we have a call request * outstanding, this message means the call has failed. If we have a * clear request outstanding, this message completes the cleanup; the * channel is now available for reuse. If we have a call active, this * message means the other side is closing the circuit. */ lcn = p[1] / 2; /* get LCN */ dc = &(ds->dda_cb[lcn]); if (dc->dc_state != LC_CLR_PENDING) { /* if no clear pending */ send_supr(ds, CLEARLC, p[1], 0); /* ack the clear */ } if (dc->dc_state == LC_CALL_PENDING /* if call is cleared */ && (LOG_CALLS || DMESGVAL(ds->dda_if.if_unit, 33) == 0)) { imp_addr.ip = dc->dc_inaddr; DDALOG(LOG_ERR) "dda%d: Call to %d.%d.%d.%d on lcn %d failed (%x %x)\n", ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno, dc->dc_lcn, p[2], p[4] DDAELOG; } else if (LOG_CALLS) { if (dc->dc_state == LC_CLR_PENDING) { /* did we clear it? *//* y * es, IP address is * already gone. Say * channel is free. */ DDALOG(LOG_ERR) "dda%d: Cleared lcn %d\n", ds->dda_if.if_unit, dc->dc_lcn DDAELOG; } else { /* cleared by net, print more info */ imp_addr.ip = dc->dc_inaddr; DDALOG(LOG_ERR) "dda%d: Cleared lcn %d to %d.%d.%d.%d (%x %x)\n", ds->dda_if.if_unit, dc->dc_lcn, imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno, p[2], p[4] DDAELOG; } } hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_IDLE); /* LCNLINK delete */ dc->dc_state = LC_IDLE; /* set state */ dc->dc_timer = TMO_OFF; /* stop timer */ dc->dc_inaddr.s_addr = 0; /* forget address */ dc->dc_key.key_addr.s_addr = 0; dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; abort_io(ds->dda_if.if_unit, lcn); if (LOG_CALLS) { printf("dda%d: Cleared LCN %d cause code %x diag code %x\n", ds->dda_if.if_unit, dc->dc_lcn, p[2], p[4]); } break; case CLEARVC: /* clear by VCN */ send_supr(ds, CLEARVC, p[1], 0); /* send clear ack */ if (LOG_CALLS) { DDALOG(LOG_ERR) "dda%d: Network cleared VC %x (%x %x)\n", ds->dda_if.if_unit, p[1], p[2], p[4] DDAELOG; } #ifdef DDADEBUG else if (DDADBCH(21, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: supr_msg: CLEARVC VCN=%x\n", ds->dda_if.if_unit, p[1] DDAELOG; } #endif DDADEBUG break; case RESET: /* X25 reset */ lcn = p[1] / 2; /* get LCN */ dc = &(ds->dda_cb[lcn]); send_supr(ds, RESET_ACK, p[1], 0); /* send reset ack */ abort_io(ds->dda_if.if_unit, lcn); imp_addr.ip = dc->dc_inaddr; DMESG(ds->dda_if.if_unit, 34, (DDALOG(LOG_ERR) "dda%d: X25 RESET (%x %x) on lcn %d: %d.%d.%d.%d\n", ds->dda_if.if_unit, p[2], p[4], lcn, imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno DDAELOG)); break; case INTERRUPT: /* X25 interrupt */ lcn = p[1] / 2; /* get LCN */ dc = &(ds->dda_cb[lcn]); imp_addr.ip = dc->dc_inaddr; DMESG(ds->dda_if.if_unit, 35, (DDALOG(LOG_ERR) "dda%d: X25 INTERRUPT (%x) on lcn %d: %d.%d.%d.%d\n", ds->dda_if.if_unit, p[2], lcn, imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno DDAELOG)); break; case STATRESP: /* Statistics Response from FEP */ /* * Copy the whole message into a static buffer, dda_iobuf. The buffer * is viewed as a (struct ddactl). Wake up the ioctl thread which * will copy the message out for acpconfig. */ { struct ddactl *da = (struct ddactl *) dda_iobuf; bcopy(p, da->msg, max(4 + p[3], sizeof(da->msg))); #ifdef MULTINET StatQuery_Completed = 1; #else wakeup(dda_iobuf); #endif break; } default: DMESG(ds->dda_if.if_unit, 36, (DDALOG(LOG_ERR) "dda%d: supervisor error (%x %x %x %x)\n", ds->dda_if.if_unit, p[0], p[1], p[2], p[3] DDAELOG)); } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DECODE_ANSWER() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* This routine looks at the answer message from the FE */ /* and decodes it to find the negtiated packet and window sizes */ /* if they are present. */ /* */ /* Call: decode_answer(p, dc) */ /* Argument: p: pointer to mbuf data for ANSWER message */ /* dc: pointer to relavant lcn structure */ /* Returns: nothing */ /* Called by: supr_msg() */ /* Calls to: */ /* DDALOG() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void decode_answer(p, dc) u_char *p; struct dda_cb *dc; { register u_char *cp; int i, faclen; dc->dc_pktsizein = 0; dc->dc_pktsizeout = 0; dc->dc_wsizein = 0; dc->dc_wsizeout = 0; cp = p + 4; /* skip over code, lcn, vcn and count in * answer message */ /* cp now points to length of called address */ cp += *cp + 1; /* skip over called address and length byte */ /* cp now points to length of calling address */ cp += *cp + 1; /* skip over calling address and length byte */ /* cp now points to length of protocol */ cp += *cp + 1; /* skip over protocol and protocol length * byte */ /* cp now points to the facilities length */ faclen = *cp++; /* cp now points to start of facilities */ for (i = 0; i < faclen;) { switch (*cp & 0xc0) { case 0x00: /* single octet parameter field */ i += 2; cp += 2; break; case 0x40: /* double octet parameter field */ switch (*cp) { case X25_FACIL_PKTSIZE: /* 0x42, packet size */ dc->dc_pktsizein = *(cp + 1); dc->dc_pktsizeout = *(cp + 2); break; case X25_FACIL_WINSIZE: /* 0x43, window size */ dc->dc_wsizein = *(cp + 1); dc->dc_wsizeout = *(cp + 2); break; } i += 3; cp += 3; break; case 0x80: /* triple octet parameter field */ i += 4; cp += 4; break; case 0xc0: /* variable-length parameter field */ cp++; i += 2 + *cp; cp += 1 + *cp; break; /* Note: No other cases (i.e., default) possible */ } } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DECODE_RING() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine parses and validates the incoming call message. */ /* */ /* Call: decode_ring(p) */ /* Argument: p: pointer to the message */ /* Returns: 1 for success, else 0 for failure */ /* Called by: supr_msg() */ /* Calls to: bcopy() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE boolean decode_ring(p) register u_char *p; { register int cnt; #ifdef DDADEBUG if (DDADBCH(22, 0)) { /* no easy access to unit, assume unit 0 */ DDALOG(LOG_DEBUG) "dda: decode_ring()\n" DDAELOG; } #endif DDADEBUG p += 3; /* skip to cmnd ext length */ if (*p++ < 5) /* is count appropriate */ return (0); /* return false if not */ /* called address */ if ((cnt = *p + 1) > 16) /* is called addr len legal? */ return (0); /* return false if not */ bcopy(p, ddacb_called_addr, cnt); /* copy field */ p += cnt; /* calling address */ if ((cnt = *p + 1) > 16) /* is calling addr len legal? */ return (0); /* return false if not */ bcopy(p, ddacb_calling_addr, cnt); /* copy field */ p += cnt; /* protocol part of user data */ if ((cnt = *p + 1) > 5) /* is protocol len legal? */ return (0); /* return false if not */ bcopy(p, ddacb_protocol, cnt); /* copy field */ p += cnt; /* facilities */ if ((cnt = *p + 1) > 64) /* is facilities len legal? */ return (0); /* return false if not */ bcopy(p, ddacb_facilities, cnt); /* copy field */ p += cnt; /* ignore rest of user data for now */ #ifdef DDA_PAD_OR_RAW if (ddacb_protocol[0] == 0) return (0); #else DDA_PAD_OR_RAW if ((ddacb_protocol[0] == 0) || (ddacb_protocol[1] != X25_PROTO_IP)) return (0); /* bad if not IP */ #endif DDA_PAD_OR_RAW #ifndef DDA_PAD_OR_RAW return (1); /* looks ok */ #else # ifdef DDA_RAWOPT return (1); /* anything is ok if we're PI interface */ # else if (ddacb_protocol[1] == X25_PROTO_IP || ddacb_protocol[1] == X25_PROTO_X29) return (1); /* looks ok */ else return (0); /* bad if not IP or X29 */ # endif #endif DDA_PAD_OR_RAW } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% CLEAR_LCN() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine clears an X25 circuit and releases any buffers */ /* queued for transmission. */ /* */ /* Call: clear_lcn(ds, dc) */ /* Argument: ds: pointer to dev control block struct */ /* dc: pointer to the Logical Channel control */ /* block structure */ /* Returns: nothing */ /* Called by: ddatimer() */ /* Calls to: IF_DEQUEUE() */ /* m_freem() */ /* send_supr() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void clear_lcn(ds, dc) struct dda_softc *ds; struct dda_cb *dc; { register struct mbuf *m; register int s; #ifdef DDADEBUG if (DDADBCH(23, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: clear_lcn(%d)\n", ds->dda_if.if_unit, dc->dc_lcn DDAELOG; } #endif DDADEBUG if (dc->dc_state == LC_CLR_PENDING) { /* Unfortunately, we can't * display the destination's * IP address, as we cleared * it when we entered * clear-pending state (to * prevent new data from * being queued to this * channel). */ DMESG(ds->dda_if.if_unit, 37, (DDALOG(LOG_ERR) "dda%d: Clear request lost -- lcn %d\n", ds->dda_if.if_unit, dc->dc_lcn DDAELOG)); return; } hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_CLR_PENDING); dc->dc_state = LC_CLR_PENDING; /* set state */ dc->dc_timer = TMO_CLR_PENDING; /* start clear timer */ dc->dc_inaddr.s_addr = 0; /* clear associated address */ dc->dc_key.key_addr.s_addr = 0; dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; /* * Raise priority whenever dc_oq is touched. */ s = splimp(); while (dc->dc_oq.ifq_len) { /* drop any pending data */ IF_DEQUEUE(&dc->dc_oq, m); m_freem(m); } splx(s); send_supr(ds, CLEARLC, (int) dc->dc_lcn * 2, 0); /* send clear msg */ } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% SEND_RESTART() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine marks all LCNs as being in a restarting state */ /* and sends a restart command to X25. */ /* */ /* Call: send_restart(ds) */ /* Argument: ds: pointer to dev control block struct */ /* Returns: nothing */ /* Called by: ddatimer() */ /* supr_msg() */ /* Calls to: IF_DEQUEUE() */ /* m_freem() */ /* send_supr() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void send_restart(ds) struct dda_softc *ds; { register struct dda_cb *dc; register int lcn; register int maxlcn; #ifdef DDADEBUG if (DDADBCH(24, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: send_restart()\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG dc = ds->dda_cb; /* LCNLINK */ maxlcn = nddach[ds->dda_if.if_unit]; for (lcn = 0; lcn <= maxlcn; lcn++) { /* for all LCN's */ dc->dc_state = LC_RESTART; /* set state */ dc->dc_timer = TMO_RESTART; /* start restart timeout */ dc->dc_inaddr.s_addr = 0; /* forget address */ dc->dc_key.key_addr.s_addr = 0; dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; dc++; } hist_all_lcns(ds->dda_if.if_unit, LC_RESTART); abort_io(ds->dda_if.if_unit, ALL_CHANS); send_supr(ds, RESTART, 0, 0); /* send restart msg */ } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% SEND_SUPR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine is used to send short (4 bytes only) supervisor */ /* commands, except that longer ANSWER messages may be sent. */ /* */ /* Call: send_supr(ds, cmd, p1, p2) */ /* Argument: ds: pointer to dev control block struct */ /* cmd: type of command */ /* p1: 2nd byte of supervisor message */ /* p2: 3rd byte of supervisor message */ /* Returns: nothing */ /* Called by: supr_msg() */ /* clear_lcn() */ /* send_restart() */ /* Calls to: MGET() */ /* DDALOG() */ /* mtod() */ /* IF_ENQUEUE() */ /* dda_start() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void send_supr(ds, cmd, p1, p2) struct dda_softc *ds; int cmd, p1, p2; { struct mbuf *m; register u_char *cp; u_char *savcp, *fp, *svcp; int i, faclen; MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) { DMESG(ds->dda_if.if_unit, 23, (DDALOG(LOG_ERR) "dda%d: failed to get supr msg bfr!\n", ds->dda_if.if_unit DDAELOG)); return; } cp = savcp = mtod(m, u_char *); /* build supervisor message */ *cp++ = (u_char) cmd; *cp++ = (u_char) p1; *cp++ = (u_char) p2; *cp++ = 0; m->m_len = 4; if (cmd == ANSWER) { register struct dda_cb *dc; /* for answer messages p1 is (lcn * 2) */ dc = &(ds->dda_cb[p1 / 2]); *cp++ = 0; /* zero length called address */ *cp++ = 0; /* zero length calling address */ *cp++ = 0; /* zero length protocol */ /* check and copy facilities */ faclen = 0; svcp = cp++; for (i = 0, fp = &ddacb_facilities[1]; i < ddacb_facilities[0];) { switch (*fp & 0xc0) { case 0x00: /* single octet parameter field */ i += 2; fp += 2; break; case 0x40: /* double octet parameter field */ /* * Note that this code can in some cases attempt to negotiate * the packet size or window away from the default, which * appears to violate the X.25 spec. In fact, the FEP * examines these values and bounds them between the * requested value and the default value thus satisfying X.25 */ switch (*fp) { case X25_FACIL_PKTSIZE: /* 0x42, packet size */ *cp++ = X25_FACIL_PKTSIZE; if (ds->dda_firmrev < 0x21) { *cp++ = PKTSIZE_DEF; /* Set incoming and outgoing */ *cp++ = PKTSIZE_DEF; /* packet size to default */ dc->dc_pktsizein = dc->dc_pktsizeout = PKTSIZE_DEF; } else { *cp++ = *(fp + 1); /* Answer with requested */ *cp++ = *(fp + 2); /* facilities */ dc->dc_pktsizeout = *(fp + 1); dc->dc_pktsizein = *(fp + 2); } faclen += 3; break; case X25_FACIL_WINSIZE: /* 0x43, window size */ *cp++ = X25_FACIL_WINSIZE; if (ds->dda_firmrev < 0x21) { *cp++ = WINSIZE_DEF; /* Set incoming and outgoing */ *cp++ = WINSIZE_DEF; /* window size to default */ dc->dc_wsizein = dc->dc_wsizeout = WINSIZE_DEF; } else { *cp++ = *(fp + 1); /* Answer with requested */ *cp++ = *(fp + 2); /* facilities */ dc->dc_wsizeout = *(fp + 1); dc->dc_wsizein = *(fp + 2); } faclen += 3; break; } i += 3; fp += 3; break; case 0x80: /* triple octet parameter field */ i += 4; fp += 4; break; case 0xc0: /* variable-length parameter field */ fp++; i += 2 + *fp; fp += 1 + *fp; break; /* Note: No other cases (i.e., default) possible */ } } if (faclen) { /* Found facilities to negotiate! */ *svcp = faclen; /* facility length <- faclen */ *cp++ = 0; /* user data length <- 0 */ *(savcp + 3) = cp - savcp - 4; /* set supv message length */ m->m_len = cp - savcp; /* set mbuf message length */ } } /* (end of answer message case) */ # ifdef DDADEBUG if (DDADBCH(25, ds->dda_if.if_unit)) { prt_bytes(ds->dda_if.if_unit, "send_supr", savcp, m->m_len); } #endif DDADEBUG start_supr(ds, m); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% START_SUPR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Start i/o on the supervisor channel, checking for queue full. */ /* Added to revision 2.0 so that "queue full" checking would be */ /* applied uniformly to all supervisory channel output. */ /* */ /* Call: start_supr(ds, m) */ /* Argument: ds: softc structure for board */ /* m: mbuf holding message */ /* Returns: nothing */ /* Called by: send_supr(), send_config(), make_x25_call() */ /* Calls to: DDALOG(), dda_start(), IF_ENQUEUE() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void start_supr(ds, m) struct dda_softc *ds; struct mbuf *m; { register int s; #ifdef DDADEBUG if (DDADBCH(27, ds->dda_if.if_unit)) DDALOG(LOG_DEBUG) "dda%d: start_supr\n", ds->dda_if.if_unit DDAELOG; #endif DDADEBUG if (IF_QFULL(&(ds->dda_cb[0].dc_oq))) { DMESG(ds->dda_if.if_unit, 27, (DDALOG(LOG_ERR) "dda%d: supervisory channel overflow (maxlen=%d)\n", ds->dda_if.if_unit, ds->dda_cb[0].dc_oq.ifq_maxlen DDAELOG)); ds->dda_cb[0].dc_oq.ifq_maxlen += ds->dda_cb[0].dc_oq.ifq_maxlen; } /* * Raise priority whenever you touch dc_oq. * We do not want to be interrupted in the middle of adding * an mbuf to the output queue because the interrupt may indicate * a condition that will cause the mbuf to be freed. * (The mbufs are freed on receipt of a line status msg, restart, * clear, or reset.) */ s = splimp(); #ifdef DDA_PAD_OR_RAW m->m_dat[MLEN - 1] = 0; #endif IF_ENQUEUE(&(ds->dda_cb[0].dc_oq), m); splx(s); dda_start(ds, &(ds->dda_cb[0])); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% ABORT_IO() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Abort outstanding I/O upon receipt of a line status message, */ /* restart, clear, or reset. */ /* The contents of the output queue (dc_oq) is cleared for each */ /* lcn; all I/O queued on either the read or write queue */ /* (dc_rchan and dc_wchan) is marked invalid; all I/O queued on */ /* the sioq is marked invalid; */ /* */ /* Call: abort_io() */ /* Argument: none */ /* Returns: nothing */ /* Called by: */ /* Calls to: IF_DEQUEUE() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void abort_io(unit, lcn) int unit, lcn; { register struct dda_cb *dc; register struct dda_softc *ds = &dda_softc[unit]; register struct hdx_chan *hc; register struct mbuf *m; register int lchan; register int s; register struct hdx_chan *ptr; int start, end; /* set up range of lcns affected */ if (lcn == ALL_CHANS) { start = 1; end = nddach[unit]; } else start = end = lcn; #ifdef DDADEBUG if (DDADBCH(28, unit)) DDALOG(LOG_DEBUG) "dda%d: abort_io on lcn's %d - %d\n", unit, start, end DDAELOG; #endif DDADEBUG s = splimp(); /* * Invalidate writes on the sioq for specified channel(s) */ if (ptr = ds->dda_sioq.sq_head) for (; ptr; ptr = ptr->hc_next) /* scan sioq */ if ((ptr->hc_chan & 0x01) && ((lcn == ALL_CHANS) || (lcn == ptr->hc_chan >> 1)) && (ptr->hc_chan != 1)) { #ifdef DDADEBUG if (DDADBCH(28, unit)) DDALOG(LOG_DEBUG) "dda%d: abort_io--invalidating sioq lcn %d\n", unit, ptr->hc_chan >> 1 DDAELOG; #endif DDADEBUG ptr->hc_inv |= INVALID_MBUF; } /* * For each selected lcn, clear the output queue and * add an hdx struct to the sioq that will generate an * abort. */ for (lchan = start; lchan <= end; lchan++) { /* for selected LCNs */ dc = &dda_softc[unit].dda_cb[lchan]; hc = &dc->dc_wchan; while (dc->dc_oq.ifq_len) { IF_DEQUEUE(&dc->dc_oq, m); m_freem(m); } if (hc->hc_mbuf && !(hc->hc_inv & INVALID_MBUF)) { if (dc->dc_flags & DC_OBUSY) { /* output pending */ #ifdef DDADEBUG if (DDADBCH(28, unit)) DDALOG(LOG_DEBUG) "dda%d: abort_io--queueing abort: lcn %d\n", unit, lchan DDAELOG; #endif DDADEBUG hc->hc_inv |= INVALID_MBUF; hc->hc_func = DDAABT; /* * Add to the sioq */ dda_wrq(ds, hc, DDAABT); } } } splx(s); } #ifdef DDADEBUG /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PRT_BYTES() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine is used to print a label, followed by the contents of */ /* a buffer in hex, 16 bytes per line. Each line is preceded by */ /* the device name and unit number. */ /* */ /* Call: prt_bytes(unit, label, bp, cnt) */ /* Argument: unit: dda unit number to be displayed */ /* label: pointer to string to be displayed */ /* bp: pointer to the buffer to be dumped */ /* cnt: number of bytes in buffer */ /* Returns: nothing */ /* Called by: dda_data() */ /* dda_supr() */ /* supr_msg() */ /* Calls to: DDALOG() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void prt_bytes(unit, label, bp, cnt) int unit; char *label; u_char *bp; int cnt; { char hexbuf[50]; /* (worst case: 3 * 16 + 1 = 49 bytes) */ char *p; int i; static char hex[] = "0123456789abcdef"; DDALOG(LOG_DEBUG) "dda%d: %s\n", unit, label DDAELOG; while (cnt > 0) { i = (cnt > 16) ? 16 : cnt; cnt -= i; p = hexbuf; while (--i >= 0) { *p++ = ' '; *p++ = hex[*bp >> 4]; *p++ = hex[*bp++ & 0x0f]; } *p++ = '\0'; DDALOG(LOG_DEBUG) "dda%d: %s\n", unit, hexbuf DDAELOG; } } #endif /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% FMT_X25() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine is used to format an X.25 address for inclusion in */ /* an error message. The previous return value is invalidated each */ /* time the function is called, as it is stored in a static buffer */ /* Note: The X.25 address is apparently sometimes stored in */ /* BCD, and other times (PDN mode) in ASCII. So we mask */ /* off the high order bits to make ourselves immune. */ /* Call: fmt_x25(bp, cnt) */ /* Argument: bp: pointer to the string */ /* cnt: number of bytes (usually from address[0]) */ /* Returns: pointer to an internal buffer containing the string; */ /* string is 1 to 15 digits, null-terminated. */ /* Called by: make_x25_call() */ /* supr_msg() */ /* convert_x25_addr() */ /* Calls to: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE char * fmt_x25(bp, cnt) register u_char *bp; register int cnt; { char *p; static char x25buf[20]; /* worst case is 15 digits plus trailing null */ /* (Don't put this on the stack!) */ p = x25buf; if (cnt >= sizeof(x25buf)) cnt = sizeof(x25buf) - 1; /* (oops!) */ while (cnt--) *p++ = (*bp++ & 0x0f) + '0'; *p++ = '\0'; return (x25buf); } #ifdef DDA_HISTOGRAM /*----------------------- HISTOGRAM SUPPORT ---------------------------------*/ /* the histogram array */ struct timeval histogram[NDDA][HISTSIZE]; /* these two structures save the time of the last change in the state of the * lcn table or the board status. */ struct timeval last_lcn_time[NDDA] = {0L, 0L}; struct timeval last_brd_time[NDDA] = {0L, 0L}; /* h_lcn_level: the current number of active lcns */ int h_lcn_level[NDDA] = {0}; /*#define DDA_HIST_DEBUG 1 /* set this to debug history features */ /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% HIST_INIT() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine initializes the histogram facility when coming up or */ /* after a reset. */ /* Call: hist_init(unit,reset) */ /* Argument: unit - board number to initialize. */ /* reset - set to 1 to force an init. */ /* Returns: nothing. */ /* Called by: ddaioctl() */ /* Calls to: microtime() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void hist_init(unit, reset) int unit; int reset; { int s; register int i; struct dda_cb *dc; if (last_lcn_time[unit].tv_sec != 0L && !reset) return; /* histogram for this unit already enabled */ bzero(histogram[unit], sizeof(struct timeval) * HISTSIZE); h_lcn_level[unit] = 0; dc = dda_softc[unit].dda_cb; s = splimp(); for (i = 0; i < NDDACH + 1; i++) { if (dc++->dc_state == LC_DATA_IDLE) h_lcn_level[unit]++; } splx(s); microtime(&histogram[unit][H_START]); #ifdef DDA_HIST_DEBUG DDALOG(LOG_DEBUG) "hist_init: starting at level %d\n", h_lcn_level[unit] DDAELOG; #endif } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% HIST_LCN_STATE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine changes the histogram depending on how the state of */ /* a channel has changed. */ /* Call: hist_lcn_state(unit, old_state, new_state) */ /* Argument: old_state: the old state of the lcn. */ /* new_state: the state the lcn is changing to. */ /* unit: unit this applies to */ /* Returns: nothing. */ /* Called by: */ /* Calls to: timevalsub(), timevaladd(), microtime() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void hist_lcn_state(unit, old_state, new_state) int unit; u_char old_state; u_char new_state; { struct timeval tv, tmpv; /* * this structure for determining state transitions is much more general * than is necessary right now. However it allows easy changes to the * state transition table for the histogram so I will leave it in until * it settles down */ switch (old_state) { case LC_DATA_IDLE: switch (new_state) { case LC_DATA_IDLE: break; default: /* all other states */ microtime(&tv); tmpv = tv; timevalsub(&tv, &last_lcn_time[unit]); #ifdef DDA_HIST_DEBUG DDALOG(LOG_DEBUG) "hist_lcn_state: adding %ld.%ld to level %d--\n", tv.tv_sec, tv.tv_usec, h_lcn_level[unit] DDAELOG; #endif timevaladd(&histogram[unit][h_lcn_level[unit]], &tv); last_lcn_time[unit] = tmpv; if (--h_lcn_level[unit] < 0) /* safety net for driver * errors */ h_lcn_level[unit] = 0; break; } break; default: switch (new_state) { case LC_DATA_IDLE: microtime(&tv); tmpv = tv; timevalsub(&tv, &last_lcn_time[unit]); #ifdef DDA_HIST_DEBUG DDALOG(LOG_DEBUG) "hist_lcn_state: adding %ld.%ld to level %d++\n", tv.tv_sec, tv.tv_usec, h_lcn_level[unit] DDAELOG; #endif timevaladd(&histogram[unit][h_lcn_level[unit]], &tv); last_lcn_time[unit] = tmpv; if (++h_lcn_level[unit] > NDDACH) /* safety net for driver * errors */ h_lcn_level[unit] = NDDACH; break; default: /* all other states */ break; } break; } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% HIST_ALL_LCNS() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine changes the histogram when the state of all the lcns */ /* are changed as a group. */ /* Call: hist_lcn_state(unit, state) */ /* Argument: state: state that all lcn are going to. Currently not*/ /* used. */ /* unit: unit this applies to */ /* Returns: nothing. */ /* Called by: */ /* Calls to: timevalsub(), timevaladd(), microtime() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void hist_all_lcns(unit, state) int unit, state; { struct timeval tmpv, tv; #ifdef lint state = state; #endif if (last_brd_time[unit].tv_sec == 0L || last_lcn_time[unit].tv_sec == 0L) return; /* see if we have initialized yet */ microtime(&tv); tmpv = tv; timevalsub(&tv, &last_lcn_time[unit]); #ifdef DDA_HIST_DEBUG DDALOG(LOG_DEBUG) "hist_all_lcns: adding %ld.%ld to level %d\n", tv.tv_sec, tv.tv_usec, h_lcn_level[unit] DDAELOG; #endif timevaladd(&histogram[unit][h_lcn_level[unit]], &tv); last_lcn_time[unit] = tmpv; h_lcn_level[unit] = 0; } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% HIST_LINK_STATE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine changes the histogram depending on how the state of */ /* the link has changed. */ /* Call: hist_link_state(old_state, new_state) */ /* Argument: old_state: the old state of the link. */ /* new_state: the state the link is changing to. */ /* unit: unit this applies to */ /* Returns: nothing. */ /* Called by: */ /* Calls to: timevalsub(), timevaladd(), microtime() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void hist_link_state(unit, old_state, new_state) int unit; u_char old_state; u_char new_state; { struct timeval tv, tmpv; /* * this structure for determining state transitions is much more general * than is necessary right now. However it allows easy changes to the * state transition table for the histogram so I will leave it in until * it settles down */ switch (old_state) { case S_LINK_UP: switch (new_state) { case S_LINK_UP: break; default: /* all other states */ #ifdef DDA_HIST_DEBUG DDALOG(LOG_DEBUG) "hist_link_state: link down\n" DDAELOG; #endif microtime(&tv); tmpv = tv; timevalsub(&tv, &last_lcn_time[unit]); timevaladd(&histogram[unit][h_lcn_level[unit]], &tv); tv = tmpv; timevalsub(&tv, &last_brd_time[unit]); timevaladd(&histogram[unit][H_LINK_UP], &tv); last_brd_time[unit].tv_sec = 0L; break; } break; default: /* all other states */ switch (new_state) { case S_LINK_UP: #ifdef DDA_HIST_DEBUG DDALOG(LOG_DEBUG) "hist_link_state: link up\n" DDAELOG; #endif microtime(&last_brd_time[unit]); /* * reset last_lcn_time so 0 entry will not accumulate the time * that we were down */ last_lcn_time[unit] = last_brd_time[unit]; break; default: break; } break; } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% HIST_READ() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine prepares the histogram table for reading by making */ /* all entries current. */ /* Call: hist_read(unit) */ /* Argument: unit : board to use. */ /* Returns: nothing */ /* Called by: ddaioctl() */ /* Calls to: timevalsub(), timevaladd(), microtime() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void hist_read(unit) int unit; { struct timeval tmpv, tv; microtime(&tv); tmpv = tv; histogram[unit][H_END] = tmpv; histogram[unit][H_TMO].tv_sec = tmo_data_idle * DDA_TIMEOUT; histogram[unit][H_TMO].tv_usec = 0L; if (last_brd_time[unit].tv_sec) { timevalsub(&tv, &last_lcn_time[unit]); #ifdef DDA_HIST_DEBUG DDALOG(LOG_DEBUG) "hist_read: adding %ld.%ld to level %d\n", tv.tv_sec, tv.tv_usec, h_lcn_level[unit] DDAELOG; #endif timevaladd(&histogram[unit][h_lcn_level[unit]], &tv); last_lcn_time[unit] = tmpv; tv = tmpv; timevalsub(&tv, &last_brd_time[unit]); timevaladd(&histogram[unit][H_LINK_UP], &tv); last_brd_time[unit] = tmpv; } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% HIST_COPYOUT() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine prepares the histogram table for reading by making */ /* all entries current. */ /* Call: hist_copyout(unit, to) */ /* Argument: unit : board to use. */ /* to : address in user space to copy to. */ /* Returns: return value from copyout */ /* Called by: ddaioctl() */ /* Calls to: copyout() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE int hist_copyout(unit, to) int unit; caddr_t to; { return ((copyout(histogram[unit], to, sizeof(struct timeval) * HISTSIZE))); } #endif DDA_HISTOGRAM #ifdef DDA_PAD_OR_RAW #if ACC_BSD > 42 # include "uba.h" # include "bk.h" # include "conf.h" # include "proc.h" # include "tty.h" # include "map.h" # include "vm.h" # include "bkmac.h" # include "clist.h" # include "file.h" # include "uio.h" #endif #if ACC_BSD == 42 || ACC_ULTRIX > 00 # include "bk.h" # include "../h/conf.h" # include "../h/proc.h" # include "../h/tty.h" # include "../h/map.h" # include "../h/vm.h" # if ACC_ULTRIX > 12 # include "uba.h" # endif # include "../h/bk.h" # ifdef SIMULATION # include "Clist.h" # else # include "../h/clist.h" # endif # include "../h/file.h" # include "../h/uio.h" #endif PRIVATE int dda_decode_type(ds, p) struct dda_softc *ds; u_char *p; { register u_char *cp; int i, usrlen; #ifdef DDADEBUG if (DDADBCH(20, ds->dda_if.if_unit)) { printf(" dda_decode_type(): p[0]= %x ", *p); } #endif DDADEBUG switch (p[0]) { case LINE_STATUS: /* link status msg */ case RESTART: /* restart received */ case RSTRT_ACK: /* restart ack */ case STATRESP: /* Statistics Response from FEP */ case CLEARVC: /* clear by VCN */ return (0); case RESET: /* X25 reset */ return (1); case ANSWER: case CLEARLC: case INTERRUPT: case INTR_ACK: i = p[1] / 2; /* get lcn */ if (ds->dda_cb[i].dc_flags & (DC_X29 | DC_X29W)) return (1); else if (ds->dda_cb[i].dc_flags & (DC_RAW)) return (2); else return (0); } if (p[0] != RING) { /* let standard dda handle it */ return (0); } cp = p + 4; /* skip over code, lcn, vcn and count in * (ring?) answer message */ /* cp now points to length of called address */ cp += *cp + 1; /* skip over called address and length byte */ /* cp now points to length of calling address */ cp += *cp + 1; /* skip over calling address and length byte */ /* cp now points to length of protocol */ if (*cp == 0) return (0); usrlen = *cp++; if (usrlen) { #ifdef DDA_RAWOPT if (pi_circuit_to_handle_protocol(*cp)) return (2); #endif #ifdef DDADEBUG if (DDADBCH(20, ds->dda_if.if_unit)) { printf(" dda_decode_type(): return value = %x ", *cp); } #endif DDADEBUG switch (*cp) { case X25_PROTO_IP: return (0); case X25_PROTO_X29: return (1); default: return (2); } } else return (0); } #endif DDA_PAD_OR_RAW #ifdef SIMULATION # ifdef DDA_PADOPT # include "if_x29.c" # endif # ifdef DDA_RAWOPT # include "if_pi.c" # endif #else # ifdef DDA_PADOPT # if ACC_VMS > 00 # include "../vaxif/if_vmsx29.c" # else # include "../vaxif/if_x29.c" # endif # endif # ifdef DDA_RAWOPT # include "../vaxif/if_pi.c" # endif #endif #ifdef DDA_MSGQ u_char ddamsgq[MSGQSIZE]; PRIVATE u_char *mqptr = 0; #define MSGQEND (ddamsgq+MSGQSIZE) dda_mqstr(s) char *s; { if (mqptr == 0) mqptr = ddamsgq; while (*s) { *mqptr++ = *s++; if (mqptr >= MSGQEND) mqptr = ddamsgq; } *mqptr = '\0'; } dda_mqnum(num, type) int num, type; { if (mqptr == 0) mqptr = ddamsgq; if ((mqptr + sizeof(int) + 2) >= MSGQEND) mqptr = ddamsgq; *mqptr++ = type; *((int *) mqptr) = num; mqptr += sizeof(int); *mqptr = '\0'; } #endif DDA_MSGQ /* link in support for steve's test-jig */ #ifdef SIMULATION #include "if_dda_sim.c" #endif /* Revision History: 18-Dec-87: V3.0 - Brad Engstrom Added the -t flag to acpconfig and the 't' case in ddaioctl to allow setting of the idle circuit timeout. The constant TMO_DATA_IDLE was changed to a variable called tmo_data_idle. 11-Mar-88: V3.0 - Brad Engstrom Modified the history routine to return the current value of the timeout. Also fixed bug so that level 0 records amount of time 0 circuits were in use only when link is up. 11-Mar-88: V3.0 - Brad Engstrom Changed handling of supervisor channel overflows to double the max q length each time it overflows. This Will prevent a flood of console messages while still notifying the user that there has been an overflow. 21-Mar-88: V3.0 - Brad Engstrom Fixed bug in writing the facilities field for packet and window size negotiation. This was in the routine make X.25 call. Previously constants were used to index into the facilities buffer now offsets from the current facilities length are used. 12-Apr-88: V3.0 - Brad Engstrom Added ability to handle class b and class c addressing. The changes affect locate_x25_lcn, convert_x25_addr, and convert_ip_addr. The modifications came from fixes sent to Wollongong by Lars Poulson. 12-Apr-88: V3.0 - Brad Engstrom Made modifications so the driver will work under Ultrix or BSD. In cases where there are differences between 4.3 and 4.2 bsd (shown by #ifdef BSD4_3) Ultrix 1.2 is exactly like a 4.2 system. Ultrix 2.0 is like 4.3 in most cases. New macros were added to distinquish between systems. These are BSD4_2 and BSD43_OR_ULTRIX20. 13-Apr-88: V3.0 - Brad Engstrom ddareset() was called from ddaintb without arguments. This could cause ddareset to return without doing anything. Proper arguments were inserted. In ddaioctl the priority level s may be used without being set. This was fixed. 18-Apr-88: V3.0 - Brad Engstrom Added the use of a key field in the dda_cb structure. Previously the dc_inaddr field was used both for printing the ip address (-l command) and for searching for circuits that were open to a destination. Using this for a cicuit matching address means that the network and local host fields needed to be masked off, thus making this field less usefull for printing. Now two fields are used dc_inaddr is used for printing. dc_key is used for circuit matching. In PDN mode the full ip address is used as the key. In DDN mode just the imp number and host(port) number are used. 18-Apr-88: V3.0 - Brad Engstrom Made histogram facilities a compile time option. The histogram is enabled if DDA_HISTOGRAM is defined. The facilities are always disabled when using 4.2 or ULTRIX 1.2 as the kernel does not have the proper support routines available. 22-Apr-88: V3.0 - Brad Engstrom Added new option to -v command to set the dda_db_unit variable. 22-Apr-88: V3.0 - Brad Engstrom Added the DMESG macro and the msgbits array to allow selective disabling of driver error messages. To enable or disable an error message the -c command of acpconfig is used. The msgbits array holds info about whether each message is enabled or disabled. Setting a bit to 1 disables a message. Clearing a bit to 0 enables a message. All messages start as enabled. 22-Apr-88: V3.0 - Brad Engstrom Added check for DDAMAINT_BRD in probe routine. If DDAMAINT_BRD is defined then assume we are using a maintenence board so don't try to find the firmware id because it won't be there. Fake info that was supposed to be contained in the firmware id. 25-Apr-88: V3.0 - Brad Engstrom Added check in locate_x25_lcn to see if state of lc is LC_CALL_PENDING or LC_DATA_IDLE in the loop that looks for an already open lc. This will prevent an address of 0.0.0.0 from matching a circuit that is not in use. If the address is invalid then the imp will kick it out. 26-Apr-88: V3.0 - Brad Engstrom Changed the -n command case so that a command of the form "-n 0" will return the number of channels currently available. This will be used by the -l command and possible by the -h command to determine the number of available circuits. 10-May-88: V3.0 - Brad Engstrom Made all occurences of the length of and X.25 address refer to the constants MAXADDRLEN and MINADDRLEN defined in if_ddavar.h. These constants include the 1 byte for encoding the length. 02-Jun-88: V3.0 - Brad Engstrom Change the check for the firmware revision level to 2.2 for the -e command. This command will crash [56]250s that don't have at least v2.2 firmware. 12-Jul-88: V3.0 - Brad Engstrom Deleted case for class_b_c addressing. 20-Jul-88: V3.0 - Brad Engstrom Fixed bug in parsing facilities that would cause the kernel to hang. The bug was not incrmenting pointers when an urecognized 2 octet facility was seen. Fixes were applied to send_supr() and decode_answer() 30-Aug-88: V4.0 - Brad Engstrom Modified driver to support X.29 and a programmers interface. Includes files if_x29.c, if_pi.c, and if_pivar.h 30-Aug-88: V4.0 - Brad Engstrom Added support for debug logging under the control of the DDA_MSGQ define. Information is extracted using the new -p command of acpconfig. 30-Aug-88: V4.0 - Brad Engstrom Modified start_chan to check the ready bit before touching the comregs. Also modified dda_rrq and dda_wrq to raise ipl before touching the sioq. These changes fixed a bug where the FE was losing I/O requests. 20-Oct-88: V4.0 - Steve Johnson Added SIMULATION #ifdef for simulation support 08-Jan-89: V4.1 - Steve Johnson MERGE 4.0 and 3.1 10-Oct-88: V3.1 - Charles Carvalho Replace prt_x25 with fmt_x25, which returns a pointer to a formatted message instead of printing its data; this allows error messages to be output with a single call to DDALOG (or syslog). Move prt_addr inline, for same reason. Add IP address to some error messages; trim excess text from some error messages. Allocate channels for incoming calls from lowest channel up; we do linear searches of the lcn table, so it's to our advantage to use the lowest numbers for all active circuits. (The lcn is not related to the virtual circuit number, so there is no need to allocate incoming channels from the top down.) Modify prt_bytes to take unit number and descriptive string to be printed along with the buffer and byte count; it now formats up to 16 bytes at a time and prints a full line with each call to DDALOG rather than calling DDALOG for each byte. 17-Oct-88: V3.1 - Charles Carvalho Add definitions for DDALOG and DDAELOG, which translate into a call to DDALOG() or log(). 26-Oct-88: V3.1 - Charles Carvalho Change index for 'v' ioctl to preserve compatibility with previous versions. Restrict maximum window size to 127, not 128. 7-Nov-88: V3.2 - Charles Carvalho Fix check for no free circuits when processing RING 17-Feb-89: V4.3.0 - Paul Traina Added TGV changes for Multinet. 8-Mar-89: V4.3.1 - Steve Johnson Installed 'Q' ioctl to support obtaining an internal trace log used for debugging only -- not documented for general user. acpconfig dda0 -q 2 dumps 256 bytes from the dda_debug_silo[] array 13-Mar-89: V4.3.2 - Paul Traina Updated Multinet support. 17-Apr-89: V4.3.3 - Steve Johnson Split bus and simulation related code out to included files for first shot at 7000 and tahoe design. Don't reset timeout counter in dda_data() unless link really is in idle state. 28-Apr-89: V4.3.4 - Paul Traina Modified changes of 17-Apr-89, added minimal tahoe support until driver modified to use 4.3uba transfers. Fixed timeout fix of 17-Apr-89 to do what was intended. Fixed code dealing with maintenance board, reformatted with indent to repair readablility. 09-May-89: V4.3.5 - Paul Traina Minimal tahoe support completed, based on BSD4_3TAHOE define which must be uncommented manually. Finalizing for ECO. 24-May-89: V4.3.6 - Paul Traina Ultrix 3.0 support added. Revised 4.3 tahoe support for automatic invocation. *** NOTE: one of the three OS defines (ACC_BSD, ACC_ULTRIX, ACC_VMS) in if_dda.c must be set to a non-zero value for the driver to compile. Attempting multiple-os support based upon weird variables from include files is not acceptable with the latest proliferation of OS versions. 20-Jun-89: V4.3.7 - Paul Traina Removed crufty old debug stuff and integrated it with the log-message code. Now X29 and PI modules can be debuged properly (no #if 0's!). 22-Jun-89: - Paul Traina Diddled ring-decode logic to check for proper ring packet decoding before attempting to find a free lcn. This will make it easier to deal with the race condition with find_free_lcn(). Modified ACC os specific equates to be set as options in the config file. This way, most users won't ever edit if_dda.c. 18-Jul-89: - Paul Traina Driver will no longer return errors if duplicate address-translation entries are made. Errors will only happen if a redefiniton is attempted. Moved dc_key.ttyline out of union, creating dc_line. 26-Jul-89: - Paul Traina f/Brad Engstrom Added support for called user-data field (two new params to make_x25_call) to support extended pad mode in the X.29 module. 01-Aug-89: - Paul Traina Made ddamsgs uninitialized -- it gets inited in ddaattach now. 03-Aug-89: - Paul Traina Changed hist_copyout definition to PRIVATE. 15-Aug-89: - Paul Traina Made dda_softc and dda_iobuf non-private. 18-Aug-89: - Paul Traina Somehow, ddareset was removed from the 'z' ioctl. 28-Aug-89: - Paul Traina Changed make_x25_call so that it checks length of data to be stuffed into the mbuf before actually copying data in. Removed udlen and ud parameters to the routine, as the public areas will be plugged with data before being called. (May need to splimp()). 22-Sep-89: - Paul Traina The order of the 'v' ioctl parameters was screwed up. This caused window and packet size setting to fail. 23-Oct-89: - Paul Traina Added further support for Steve's yetchy simulation. Updated main module to work with BI version of dda board. 29-Oct-89: - Paul Traina Acpconfig inconsistancy (again): removed the 'p', and 'Q' ioctls. Since all of these are queries, I placed them under the 'q' ioctl with a new switch. Some day we should just scrap the whole mess and design a proper ioctl interface. 11-Nov-89: - Paul Traina Moved rrq/wrq routines into bus modules because we can do several queue reads and writes when working with the BI. */ #endif NDDA > 0 prints a full line with each call to DDALOG rather than calling DDALOG for each byte. 17-Oct-88: V3.1 - Charles Carvdriver/if_dda_uqbus.c 444 540 24 151731 4535572350 10246 /*************************************************************************/ /* */ /* */ /* ________________________________________________________ */ /* / \ */ /* | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* \________________________________________________________/ */ /* */ /* Copyright (c) 1986 by Advanced Computer Communications */ /* 720 Santa Barbara Street, Santa Barbara, California 93101 */ /* (805) 963-9431 */ /* */ /* */ /* File: if_dda_uqbus.c */ /* Unibus & Q22bus support routines for dda */ /* */ /* Project: DDN-X.25 Network Interface Driver for ACP 5250 */ /* and ACP 6250 */ /* */ /* revision history at the end of if_dda.c */ /*************************************************************************/ #ifndef SIMULATION #include "../vaxif/if_uba.h" #include "../vaxuba/ubareg.h" #include "../vaxuba/ubavar.h" #else #include "vaxif/if_uba.h" #include "vaxuba/ubareg.h" #include "vaxuba/ubavar.h" #endif PRIVATE u_short ddastd[] = {0767000, 0}; /* standard addresses */ struct uba_device *ddainfo[NDDA]; /* ptrs to device info */ struct uba_driver ddadriver = /* device driver info */ { ddaprobe, /* device probe routine */ 0, /* slave probe routine */ ddaattach, /* device attach routine */ 0, /* "dmago" routine */ ddastd, /* device address */ "dda", /* device name */ ddainfo /* ptr to device info ptrs */ }; /* figure out if this machine is a QBUS machine */ #if defined(MVAX) || defined(VAX3400) || defined(VAX3600) || defined(DS5400) #define QBUS #endif /* * The following definitions declare the structure of the UNIBUS/QBUS * mapped pages (which are 512 bytes long). In previous versions we * assumed that the bus pages were the same size as the memory pages, * but the mips PMAX cpu uses 4k memory pages but 512 byte bus pages. */ #define BUS_NBPG 512 /* bytes/unibus mapped page */ #define BUS_PGOFSET (BUS_NBPG-1) /* offset into bus page */ #define BUS_PGSHIFT 9 /* number of bits to shift for page */ /* * If we're not running on a PMAX cpu, we don't need to do write buffer * pipeline flushes nor do we need to declare I/O space pointers to * be volatile. */ #ifndef mips #define wbflush() #define volatile register #endif /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAPROBE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine probes the device to obtain the UNIBUS interrupt */ /* vector. Since the ACP is a soft vector device, we obtain an */ /* unused vector from the uba structure and return that. The ACP */ /* is given the vector and the board is reset. In order to save */ /* the vector in the device info structure, we place it in a */ /* static temporary where the attach routine can find it and save */ /* it in the device info structure. This is necessary because */ /* probe only provides a pointer to the device and we have no */ /* idea which unit is being referenced. This works in 4.2BSD */ /* because the attach routine is called immediately after a */ /* successful probe. */ /* */ /* Call: ddaprobe(reg, ui) */ /* Argument: reg: caddr_t address in virtual memory of the */ /* control-status register */ /* ui: pointer to device data structure, used */ /* for TWG environment */ /* Returns: length of register structure for ACP device */ /* Called by: network software, part of autoconfiguration on */ /* the VAX, the address of this routine is one of */ /* the fields of the uba_driver structure */ /* Calls to: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ static int savevec; /* static variable for vector */ static int savefirmrev; /* firmware rev has same problem as vector */ ddaprobe(reg, ui) caddr_t reg; struct uba_device *ui; /* TWG VAX/VMS ONLY! */ { /* In 2.0 ULTRIX and newer, br and cvec are global (ubavar.h) */ /* while multinet needs them external */ #if !defined(MULTINET) && ACC_ULTRIX < 20 register int br, cvec; /* r11, r10 value-result */ #endif #ifdef MULTINET extern int br, cvec; /* must define external */ #endif volatile struct ddaregs *addr = (struct ddaregs *) reg; #ifdef lint br = 0; cvec = br; br = cvec; # if ACC_VMS == 00 ui = ui; # endif #endif #if ACC_VMS > 00 cvec = savevec = ui->ui_flags & 0x1f8; /* flags from config file */ #else cvec = savevec = ((uba_hd[numuba].uh_lastiv - 8) & ~7); uba_hd[numuba].uh_lastiv = cvec; #endif /* return a vector aligned on a */ /* QUADWORD boundary */ /* cvec is the interrupt vector */ /* address on the UNIBUS */ #ifdef QBUS br = 0x17; /* bus level for MicroVAX */ #else br = 0x15; /* bus level for VAX */ #endif /* check that the device is really a 5250/6250 and save away */ /* the firmware revision level for version dependent processing */ /* If we just booted, the diagnostics may still be running as we */ /* probe the device - it's still OK to read the ID and VERSION */ /* numbers, which come valid within milliseconds after power is */ /* applied to the board - so say the firmware gurus */ dda_hasmaint = !(addr->csr & CSR_MAINT); savefirmrev = addr->sys_vers; switch (addr->sys_id) { case 0x8: dda_product = "ACP6250"; break; case 0x28: dda_product = "ACP5250"; break; case 0x2C: dda_product = "ACP5250-W"; break; default: if (!dda_hasmaint) return 0; savefirmrev = 0xff; dda_product = "ACPx250"; break; } /* clear communications registers */ addr->req_flags = 0; /* I/O request flags */ addr->cmp_flags = 0; /* I/O completion flags */ addr->xfr_flags = 0; /* transfer request/grant flags */ addr->req_chan = 0; /* FDX channel number */ addr->req_adx = 0; /* address bits 17-16 */ addr->req_addr = 0; /* address bits 15-00 */ addr->req_cnt = 0; /* byte count */ addr->req_fcn = 0; /* I/O function */ addr->req_sbf = 0; /* I/O subfunction */ addr->cmp_chan = 0; /* FDX channel number */ addr->cmp_unused = 0; /* address bits 17-16 */ addr->cmp_cnt = 0; /* byte count */ addr->cmp_stat = 0; /* I/O status */ addr->cmp_sbst = 0; /* I/O substatus */ addr->xfr_chan = 0; /* FDX channel number */ addr->xfr_adx = 0; /* address bits 17-16 */ addr->xfr_addr = 0; /* address bits 15-00 */ addr->xfr_cnt = 0; /* byte count */ addr->sys_stat = 0; /* system status */ addr->sys_vect = cvec >> 2; /* pass interrupt vector to ACP */ addr->csr = CSR_RESET; /* reset the board */ addr->csr |= CSR_IENB; /* enable status intr */ return (sizeof(struct ddaregs)); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAATTACH() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine attaches the device to the network software. The */ /* network interface structure is filled in. The device will be */ /* initialized when the system is ready to accept packets. The */ /* dda_init initialization/service flag is zeroed, DDN standard */ /* X.25 service is implemented by default unless otherwise */ /* specified by the user via the acpconfig program. */ /* */ /* Call: ddaattach(ui) */ /* Argument: ui: ptr to the uba_device data structure */ /* Returns: nothing */ /* Called by: network software, part of network system */ /* configuration, identification to the network */ /* software, the address of this routine is one */ /* of the fields of the uba_driver structure */ /* Calls to: if_attach() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ ddaattach(ui) struct uba_device *ui; { register struct dda_softc *ds = &dda_softc[ui->ui_unit]; static long initmsgs[] = { 0, 0, 0, 0, -1, -1, -1, -1, -2 }; ds->dda_init = DDA_STANDARD;/* init/service flag <- default */ ds->dda_vector = savevec; /* save vector from probe() */ ds->dda_firmrev = savefirmrev; /* save firmware rev level */ ds->dda_net_id = 0; /* default */ ds->dda_if.if_unit = ui->ui_unit; /* set unit number */ ds->dda_if.if_name = "dda"; /* set device name */ ds->dda_if.if_mtu = DDAMTU; /* set max msg size */ ds->dda_if.if_init = ddainit; /* set init routine addr */ ds->dda_if.if_ioctl = ddaioctl; /* set ioctl routine addr */ ds->dda_if.if_output = ddaoutput; /* set output routine addr */ ds->dda_if.if_reset = ddareset; /* set reset routine addr */ ds->dda_if.if_watchdog = ddatimer; /* set timer routine addr */ bcopy((char *) initmsgs, (char *) ddamsgs[ui->ui_unit], sizeof(initmsgs)); if_attach(&ds->dda_if); /* attach new network device */ } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDARESET() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Reset of interface after UNIBUS reset. If interface is on */ /* specified uba, reset its state. Free mbufs if there is */ /* queued output data. */ /* */ /* Call: ddareset(unit, uban) */ /* Arguments: unit: ACP device unit number */ /* uban: Unibus Adapter # (do not use!) */ /* Returns: nothing */ /* Called by: network software, address of routine is */ /* defined in dda_if network interface struct */ /* Calls to: DDALOG() */ /* IF_DEQUEUE() */ /* m_freem() */ /* */ /* NOTE: the uban parameter is NOT USED, and may be garbage under */ /* some circumnstances. It is there because the kerenel */ /* expects to pass two parameters to ddareset() */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ ddareset(unit, uban) int unit, uban; { volatile struct ddaregs *addr; register struct uba_device *ui; register struct dda_cb *dc; register struct dda_softc *ds = &dda_softc[unit]; register int lcn; ui = (struct uba_device *) ddainfo[unit]; if (unit >= NDDA || (ui == 0 || ui->ui_alive == 0)) { DMESG(unit, 0, (DDALOG(LOG_ERR) "dda%d: ddareset: invalid unit\n", unit DDAELOG)); return; } DMESG(unit, 0, (DDALOG(LOG_ERR) "dda%d: reset\n", unit DDAELOG)); ds->dda_if.if_flags &= ~IFF_UP; hist_link_state(unit, ds->dda_state, S_DISABLED); ds->dda_state = S_DISABLED; addr = (struct ddaregs *) ui->ui_addr; addr->cmp_flags = 0; /* I/O completion flags */ addr->xfr_flags = 0; /* transfer request/grant flags */ addr->req_chan = 0; /* FDX channel number */ addr->req_adx = 0; /* address bits 17-16 */ addr->req_addr = 0; /* address bits 15-00 */ addr->req_cnt = 0; /* byte count */ addr->req_fcn = 0; /* I/O function */ addr->req_sbf = 0; /* I/O subfunction */ addr->cmp_chan = 0; /* FDX channel number */ addr->cmp_unused = 0; /* address bits 17-16 */ addr->cmp_cnt = 0; /* byte count */ addr->cmp_stat = 0; /* I/O status */ addr->cmp_sbst = 0; /* I/O substatus */ addr->xfr_chan = 0; /* FDX channel number */ addr->xfr_adx = 0; /* address bits 17-16 */ addr->xfr_addr = 0; /* address bits 15-00 */ addr->xfr_cnt = 0; /* byte count */ addr->sys_stat = 0; /* system status */ addr->req_flags = 0; /* clear handshake flags, mailbox */ /* pass interrupt vector to ACP */ addr->sys_vect = dda_softc[unit].dda_vector >> 2; addr->csr = CSR_RESET; /* reset the board */ dda_softc[unit].dda_flags = 0; /* clear ACP operational flag */ ds->dda_init &= ~DDA_INTCLOCK; /* reset internal-clocking-set bit */ nddach[unit] = NDDACH_DEFAULT; /* reset SVC limit */ dc = dda_softc[unit].dda_cb;/* flush any queued output data */ /* LCNLINK */ for (lcn = 0; lcn <= NDDACH; lcn++) { /* for all LCN's ... */ dc->dc_inaddr.s_addr = 0; /* clear remote internet addr */ dc->dc_key.key_addr.s_addr = 0; /* must save for x29 */ dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; dc->dc_state = LC_IDLE; /* init LCN state */ dc->dc_timer = TMO_OFF; /* turn LCN timer off */ dc->dc_flags = 0; #ifdef DDADEBUG dc->dc_out_t = TMO_OFF; /* turn FE completion timer off */ #endif dc++; } hist_all_lcns(unit, LC_IDLE); #ifdef DDA_RAWOPT pi_init(unit, 1); #endif #ifdef DDA_PADOPT x29_init(unit, 1); #endif addr->csr |= CSR_IENB; wbflush(); /* flush write cache pipeline */ } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAINIT() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine initializes the interface for operation. The */ /* device control blocks are initialized, UNIBUS resources are */ /* allocated and an initialization message is sent to the ACP. */ /* */ /* Note that interrupt "b" is enabled here to avoid a possible */ /* race condition at power up time - it was previously done in */ /* the probe and reset routines. */ /* */ /* Call: ddainit(unit) */ /* Argument: unit: ACP device unit number */ /* Returns: nothing */ /* Called by: network software, address of this routine is */ /* defined in dda_if network interface struct */ /* ddaioctl() */ /* ddaintb() */ /* Calls to: in_netof() return the network number from */ /* internet address */ /* spl6() */ /* uballoc() */ /* ddatimer() */ /* btoc() */ /* splimp() */ /* dda_rrq() */ /* splx() */ /* if_rtinit() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ ddainit(unit) int unit; { volatile struct ddaregs *addr; register struct dda_softc *ds = &dda_softc[unit]; register struct dda_cb *dc; register struct uba_device *ui; #if ACC_BSD == 42 || ACC_ULTRIX == 12 struct sockaddr_in *sin; #endif int lcn, s; ui = (struct uba_device *) ddainfo[unit]; addr = (struct ddaregs *) ui->ui_addr; #ifdef DDADEBUG if (DDADBCH(0, unit)) { DDALOG(LOG_DEBUG) "dda%d: ddainit()\n", unit DDAELOG; } #endif DDADEBUG /* if we have no internet addr if device not operational don't init yet */ #if ACC_BSD == 42 || ACC_ULTRIX == 12 sin = (struct sockaddr_in *) & ds->dda_if.if_addr; if ((in_netof(sin->sin_addr) == 0) || ((ds->dda_flags & DDAF_OK) == 0)) #else if (ds->dda_if.if_addrlist == (struct ifaddr *) 0 || ((ds->dda_flags & DDAF_OK) == 0)) #endif return; /* enable intrpt "b" */ addr->csr |= CSR_IENB; if ((ds->dda_if.if_flags & IFF_RUNNING) == 0) { dc = ds->dda_cb; /* setup ptr to first LCN cntl block */ /* LCNLINK */ for (lcn = 0; lcn <= NDDACH; lcn++) { /* for all LCN's ... */ dc->dc_lcn = lcn; /* record LCN */ dc->dc_inaddr.s_addr = 0; /* clear remote internet addr */ dc->dc_key.key_addr.s_addr = 0; dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; dc->dc_state = LC_DOWN; /* init LCN state */ dc->dc_timer = TMO_OFF; /* turn LCN timer off */ #ifdef DDADEBUG dc->dc_out_t = TMO_OFF; /* turn FE completion timer off */ #endif /* init LCN output queue */ s = splimp(); dc->dc_oq.ifq_head = (struct mbuf *) 0; dc->dc_oq.ifq_tail = (struct mbuf *) 0; splx(s); dc->dc_oq.ifq_len = 0; dc->dc_oq.ifq_maxlen = DDA_OQMAX; dc->dc_oq.ifq_drops = 0; /* init HDX channels */ dc->dc_rchan.hc_next = (struct hdx_chan *) 0; dc->dc_rchan.hc_chan = lcn * 2; dc->dc_wchan.hc_next = (struct hdx_chan *) 0; dc->dc_wchan.hc_chan = (lcn * 2) + 1; dc->dc_rchan.hc_mbuf = (struct mbuf *) 0; dc->dc_rchan.hc_curr = (struct mbuf *) 0; dc->dc_wchan.hc_mbuf = (struct mbuf *) 0; dc->dc_wchan.hc_curr = (struct mbuf *) 0; dc->dc_flags = 0; /* initialize flags */ dc++; /* point at next cntl blk */ } hist_all_lcns(unit, LC_DOWN); /* allocate UNIBUS mapping registers, uballoc() */ /* returns ubinfo, CLBYTES = (CLSIZE * NBPG) */ /* rev 2.0 and above: we are now attempting to handle interrupt */ /* B by resetting the board and bringing it back on line. So */ /* have to deal with the possibility that we already did this. */ /* It's more serious to let mapping registers "leak" than mbufs */ /* (see above) and it's easy to handle too so THIS we do check. */ #ifndef MULTINET s = spl6(); #endif if (ds->dda_mapreg == 0) /* don't have one yet, allocate */ ds->dda_mapreg = uballoc(ui->ui_ubanum, (char *) &dda_softc[unit], CLBYTES, 0); #ifndef MULTINET splx(s); #endif if (ds->dda_mapreg == 0) { DMESG(unit, 1, (DDALOG(LOG_ERR) "dda%d: failed getting UBA resources for lcn %d\n", unit, lcn DDAELOG)); ds->dda_if.if_flags &= ~(IFF_RUNNING | IFF_UP); hist_link_state(unit, ds->dda_state, S_DISABLED); ds->dda_state = S_DISABLED; return; } /* leave the UNIBUS mapping register */ #ifdef UBAI_MR /* if the macro exists, use it, because it's always right */ ds->dda_mapreg = UBAI_MR(ds->dda_mapreg); #else ds->dda_mapreg = (ds->dda_mapreg >> BUS_PGSHIFT) & BUS_PGOFSET; #endif ds->dda_sioq.sq_head = (struct hdx_chan *) 0; ds->dda_sioq.sq_tail = (struct hdx_chan *) 0; ds->dda_if.if_flags |= IFF_RUNNING; } s = splimp(); dc = ds->dda_cb; /* setup ptr to first LCN cntl block */ for (lcn = 0; lcn <= nddach[unit]; lcn++) { /* issue reads on all LCNs */ dda_rrq(ds, &(dc->dc_rchan)); dc++; } splx(s); ddatimer(unit); /* start timers */ #if ACC_BSD == 42 || ACC_ULTRIX == 12 if_rtinit(&ds->dda_if, RTF_UP); /* initialize the routing table */ /* for network, give address of ifnet structure and RTF_UP */ /* which means route is useable */ #endif #ifdef DDA_RAWOPT pi_init(unit, 0); /* initialize progammer interface */ #endif #ifdef DDA_PADOPT x29_init(unit, 0); #endif } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAINTA() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This is the interrupt handler for I/O interrupts (interrupt */ /* "a") from the ACP device. The I/O mailboxes are scanned for */ /* handshake events to process. Three types of interrupts are */ /* processed: Transfer Request, I/O request acknowledge, and */ /* I/O completion. The interrupting HDX channel and interrupt */ /* type are obtained. If the interrupt is Transfer Request, */ /* quit if there is no data, otherwise supply values for the */ /* Transfer Request Mailbox. Note the mapping of the system */ /* page table entry, pte, and the UNIBUS Mapping Register, */ /* ddamapreg. The mapping algorithm is different for the TWG */ /* Eunice/IPTCP environments because Sysmap is not present as */ /* it is in UNIX 4.2 BSD. If interrupt is an I/O request */ /* acknowledge the next I/O request is passed to the ACP */ /* device. If the interrupt is an I/O completion, check for */ /* errors, if ok process according to whether supervisory or */ /* data channel. */ /* */ /* Call: ddainta(unit) */ /* Arguments: unit: ACP device unit number */ /* Returns: nothing */ /* Called by: network software, address of this routine */ /* is defined in af_inet network interface */ /* data structure */ /* Calls to: DDALOG() */ /* btop() - byte to page w/o rounding ( >> 9 ) */ /* btoc() */ /* start_chn() */ /* dda_data() */ /* dda_supr() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #ifdef WINS extern struct pte *mmg$gl_sptbase; /* base of system page table */ extern struct ADP *ioc$gl_adplist; /* Adapter control blocks */ #endif WINS #ifdef MULTINET extern struct pte *vms_sptbase; #define mmg$gl_sptbase vms_sptbase #endif MULTINET ddainta(unit) int unit; { volatile struct ddaregs *addr = (struct ddaregs *) ddainfo[unit]->ui_addr; register struct dda_softc *ds = &dda_softc[unit]; register struct hdx_chan *hc; register struct uba_device *ui; register struct uba_hd *uh = &uba_hd[ddainfo[unit]->ui_ubanum]; struct pte *pte; /* page table entry pointers */ int chan, cc, subcc, cnt, npf, uadr, pgoff; register int page_to_map; #if ACC_VMS > 00 int temp; register struct ADP *adp; /* TWG or TGV, UNIBUS adapter */ int *io; #else struct pte *io; /* page table entry pointers */ #endif #ifdef DDADEBUG if (DDADBCH(5, unit)) { DDALOG(LOG_DEBUG) "dda%d: ddainta()\n", unit DDAELOG; } #endif DDADEBUG ui = (struct uba_device *) ddainfo[unit]; addr = (struct ddaregs *) ui->ui_addr; /***********************************************************************/ /* Check Transfer Request Mailbox (note VAXVMS dependent code) */ /***********************************************************************/ if (addr->xfr_flags & FLAGS_RDY) { /* Transfer Request Mailbox */ #ifdef DDA_MSGQ dda_mqstr("(tx)"); #endif /* * Get logical channel information. */ if ((chan = addr->xfr_chan) > nddach[unit]) { DMESG(unit, 4, (DDALOG(LOG_ERR) "dda%d: unknown transfer channel, lcn=%d\n", unit, chan DDAELOG)); return; } if (addr->xfr_flags & FLAGS_DIR) hc = &(ds->dda_cb[chan].dc_wchan); /* write channel */ else hc = &(ds->dda_cb[chan].dc_rchan); /* read channel */ cnt = addr->xfr_cnt; /* Transfer Request byte count */ if (hc->hc_mbuf == 0) { DMESG(unit, 5, (DDALOG(LOG_ERR) "dda%d: transfer request lcn %d: no mbuf\n", unit, chan DDAELOG)); addr->xfr_cnt = 0; /* Abort the transfer */ return; } pgoff = (int) hc->hc_addr & PGOFSET; #if ACC_VMS > 00 /* System page table entry for VMS */ temp = (((int) hc->hc_addr & (~PG_V & ~(NBPG - 1))) / NBPG); pte = (struct pte *) ((int) mmg$gl_sptbase + (4 * temp)); /* * Find the adapter */ # ifdef WINS adp = ioc$gl_adplist; while (adp) { if ((adp->adp$w_adptype == AT$_UBA) && (adp->adp$b_number == ui->ui_ubanum)) break; adp = adp->adp$l_link; } if (!adp) return; /* adp not found */ io = (int *) ((int) adp->adp$l_csr + 0x800 + (4 * (ds->dda_mapreg))); # endif # ifdef MULTINET adp = (struct ADP *) 0; while (1) { char **vector; int number, csr; /* * Get the next adapter */ adp = (struct ADP *) vms_next_uba(adp, &vector, &number, &csr, 0); if (!adp) return; /* * Is this the one we are looking for? */ if (number == ui->ui_ubanum) { /* yes */ io = (int *) ((int) csr + 0x800 + (4 * (ds->dda_mapreg))); break; } } # endif #else /* not VMS */ # ifdef kvtopte /* if the macro exists, use it for compatibility */ /* this will be in 4.3tahoe and newer */ pte = kvtopte(hc->hc_addr); io = &uh->uh_mr[ds->dda_mapreg]; # else # ifdef svtopte /* under ultrix, kvtopte is svtopte */ /* but the map registers are in the right place */ pte = svtopte(hc->hc_addr); io = &uh->uh_uba->uba_map[ds->dda_mapreg]; # else /* not 4.3tahoe or a new ultrix flavor */ pte = &Sysmap[btop((int) hc->hc_addr & ~PG_V)]; io = &uh->uh_uba->uba_map[ds->dda_mapreg]; # endif /* not 4.3tahoe or a new ultrix flavor */ # endif #endif /* pte now points to system memory page table entry */ /* io points to UNIBUS/QBUS mapping register */ #ifndef SIMULATION /* calculate number of page frames */ npf = (hc->hc_cnt + BUS_PGOFSET) >> BUS_PGSHIFT; /* page_to_map is all but nine bits of the physical address in other words, the 512-byte page the bus wants us to map */ page_to_map = ((pte++)->pg_pfnum << (PGSHIFT - BUS_PGSHIFT)) | (pgoff >> BUS_PGSHIFT); while (npf--) { /* UBAMR_MRV = mapping register valid */ *(int *) io++ = page_to_map++ | UBAMR_MRV; /* when page_to_map crosses a memory page boundary, we need to get the new physical address from the next pte (on a vax, this happens every time, on a mips, only when the lower 3 bits go from 111 to 000) */ if (page_to_map & (PGOFSET >> BUS_PGSHIFT) == 0) /* we know page offset is now going to be zero, so don't bother adding it in (like we had to do above) */ page_to_map = (pte++)->pg_pfnum << (PGSHIFT - BUS_PGSHIFT); } *(int *) io++ = 0; /* invalidate last UMR */ /* supply values for Transfer Request Mailbox */ addr->xfr_chan = chan; /* data path number */ uadr = (ds->dda_mapreg << BUS_PGSHIFT) + (pgoff & BUS_PGOFSET); #ifdef QBUS addr->xfr_adx = (uadr & 0x3f0000) >> 16; /* ext address bits 23-16 */ #else QBUS addr->xfr_adx = (uadr & 0x30000) >> 16; /* ext address bits 18-16 */ #endif QBUS #else SIMULATION uadr = (int) hc->hc_addr; /* just use the direct address */ addr->xfr_adx = (uadr & 0xFF0000) >> 16; /* ext ms address bits */ #endif SIMULATION addr->xfr_addr = uadr & 0xffff; /* grnt transfer address bits 15-0 */ hc->hc_cnt -= cnt; hc->hc_addr += cnt; addr->xfr_flags = (addr->xfr_flags & ~FLAGS_RDY) | FLAGS_DON; addr->csr |= CSR_INTRA; /* enable interrupt "a" */ } /***********************************************************************/ /* Check I/O Request Mailbox */ /***********************************************************************/ if (addr->req_flags & FLAGS_DON) { /* I/O Request Mailbox */ #ifdef DDA_MSGQ dda_mqstr("(rx)"); #endif addr->req_flags &= ~FLAGS_DON; /* dequeue old request by copying link to queue head */ /* and start next I/O request if queue is not empty */ if (ds->dda_sioq.sq_head = ds->dda_sioq.sq_head->hc_next) start_chn(ds); } /***********************************************************************/ /* Check I/O Completion Mailbox */ /***********************************************************************/ if (addr->cmp_flags & FLAGS_RDY) { /* I/O Completion Mailbox */ #ifdef DDA_MSGQ dda_mqstr("(cx)"); #endif /* * Get logical channel information. */ if ((chan = addr->cmp_chan) > nddach[unit]) { DMESG(unit, 6, (DDALOG(LOG_ERR) "dda%d: unknown completion channel, lcn=%d\n", unit, chan DDAELOG)); return; } if (addr->cmp_flags & FLAGS_DIR) hc = &(ds->dda_cb[chan].dc_wchan); /* write channel */ else hc = &(ds->dda_cb[chan].dc_rchan); /* read channel */ cc = addr->cmp_stat; /* Mailbox I/O completion status */ subcc = addr->cmp_sbst; /* Mailbox I/O completion substatus */ cnt = addr->cmp_cnt; /* Mailbox I/O completion byte count */ #ifdef mips /* if it was a read completion, invalidate the mbuf data portion */ /* NOTE: the direction check has been commented out because it seemed like we got it backwards and forgot to check it later. This is now believed to be correct, but is left commented out until a beta site checks it for us. */ /* if (!(hc->hc_chan & 1)) */ { int phys_begin = svtophy(mtod(hc->hc_cur, u_char *)); clean_dcache(PHYS_TO_K0(phys_begin), cnt); } #endif switch (cc) { /* check for unsuccessful I/O completion status */ case DDAIOCABT: /* probably VCN flush */ if (LOG_ABT) DDALOG(LOG_ERR) "dda%d: abort completed on chan %d\n", unit, hc->hc_chan DDAELOG; break; case DDAIOCERR: DMESG(unit, 7, (DDALOG(LOG_ERR) "dda%d: program error ", unit DDAELOG)); goto daterr; case DDAIOCOVR: DMESG(unit, 8, (DDALOG(LOG_ERR) "dda%d: overrun error ", unit DDAELOG)); goto daterr; case DDAIOCUBE: DMESG(unit, 9, (DDALOG(LOG_ERR) "dda%d: transfer count = 0 ", unit DDAELOG)); goto daterr; case DDAIODMAE: DMESG(unit, 10, (DDALOG(LOG_ERR) "dda%d: DMA completion error (%x) ", unit, addr->cmp_sbst DDAELOG)); goto daterr; case DDAIOLCOL: DMESG(unit, 11, (DDALOG(LOG_ERR) "dda%d: listen collision ", unit DDAELOG)); goto daterr; case DDAIOFUNC: DMESG(unit, 12, (DDALOG(LOG_ERR) "dda%d: invalid function ", unit DDAELOG)); goto daterr; case DDAIODPN: DMESG(unit, 13, (DDALOG(LOG_ERR) "dda%d: invalid dpn ", unit DDAELOG)); goto daterr; daterr: DMESG(unit, 14, (DDALOG(LOG_ERR) "lcn=%d func=%x\n", chan, hc->hc_func DDAELOG)); if (hc->hc_func & DDARDB) ds->dda_if.if_ierrors++; else ds->dda_if.if_oerrors++; } /* was it supervisor or data traffic? */ if (chan > DDA_SUPR) { #ifdef DDA_PADOPT if (ds->dda_cb[chan].dc_flags & DC_X29) x29_data(ds, hc, cc, cnt, subcc); else #endif #ifdef DDA_RAWOPT if (ds->dda_cb[chan].dc_flags & DC_RAW) pi_data(ds, hc, cc, cnt, subcc); else #endif dda_data(ds, hc, cc, cnt); } else dda_supr(ds, hc, cc, cnt); /* * Ack the interrupt. Fix the Mailbox Ready and Done bits: set DON * bits, and clear RDY bits so mailbox may be reused. */ addr->cmp_flags = (addr->cmp_flags & ~FLAGS_RDY) | FLAGS_DON; addr->csr |= CSR_INTRA; /* enable interrupt "a" */ } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAINTB() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Service interrupt "b", system interrupt, from the ACP device. */ /* If the ACP device is operational, interrupt is unexpected, */ /* disable the device. If the interrupt indicates a powerup */ /* diagnostic failure, disable the device. Otherwise, set ACP */ /* flag for device operational, enable interrupt a, enable DMA, */ /* and perform initialization tasks. */ /* */ /* Call: ddaintb(unit) */ /* Argument: unit: DDA device unit number */ /* Returns: nothing */ /* Called by: network software, address of this routine is */ /* defined in af_inet network interface struct */ /* Calls to: DDALOG() */ /* ddainit() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ ddaintb(unit) int unit; { volatile struct ddaregs *addr; struct uba_device *ui; register struct dda_softc *ds = &dda_softc[unit]; register struct dda_cb *dc; register u_char diag_stat; register int lcn; register struct mbuf *m; register struct hdx_chan *hc; ui = (struct uba_device *) ddainfo[unit]; addr = (struct ddaregs *) ui->ui_addr; #ifdef DDADEBUG if (DDADBCH(6, unit)) { DDALOG(LOG_DEBUG) "dda%d: ddaintb()\n", unit DDAELOG; } #endif DDADEBUG if (ds->dda_flags & DDAF_OK) { /* * Change to rev2.0 and above driver: attempt to handle an unexpected * B interrupt sanely by reinitializing the world. We turn off all * the flavors of UP and OK flags and call ddareset(). This will * restart the diagnostics and enable interrupt B. After the * diagnostics run, we'll come back here to the B interrupt handler * and with DDAF_OK off will enter the "else" below. With luck the * board will report success and we'll call ddainit() to bring the * link back on line. */ DMESG(unit, 15, (DDALOG(LOG_ERR) "dda%d: asynchronous restart, status = %d\n", unit, addr->sys_stat DDAELOG)); ds->dda_flags = 0; ds->dda_if.if_flags &= ~(IFF_RUNNING | IFF_UP); hist_link_state(unit, ds->dda_state, S_DISABLED); ds->dda_state = S_DISABLED; ddareset(unit, ui->ui_ubanum); } else { diag_stat = addr->sys_stat; if ((diag_stat & DDASTAT_ERR) != 0) { DMESG(unit, 16, (DDALOG(LOG_ERR) "dda%d: Diagnostic failure = %d\n", unit, addr->sys_stat DDAELOG)); addr->csr = 0; } else if (diag_stat == DDASTAT_NMC) { DMESG(unit, 17, (DDALOG(LOG_ERR) "dda%d: No Microcode Present!\n", unit DDAELOG)); addr->csr = 0; } else if (diag_stat == DDASTAT_OK) { /* * For each lcn, clear the output queue and free hdx read and write mbufs */ for (lcn = 0; lcn <= NDDACH; lcn++) { /* for all LCNs */ dc = &dda_softc[unit].dda_cb[lcn]; while (dc->dc_oq.ifq_len) { IF_DEQUEUE(&dc->dc_oq, m); if (m) m_freem(m); } hc = &dc->dc_rchan; if (hc->hc_mbuf) { m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } hc = &dc->dc_wchan; if (hc->hc_mbuf) { m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } } ds->dda_flags |= DDAF_OK; addr->csr |= (CSR_IENA | CSR_DMAEN); ddainit(unit); } } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDA_WRQ() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Process write requests. Put I/O request values in */ /* half-duplex control channel structure: set function code */ /* for write to ACP with Transfer Grant set. If there are no */ /* more mbufs in chain, mark DDAEOS for end of stream. Set */ /* count from data length (byte count) in mbuf, and subfunction */ /* is zero. If the COMREGs are busy, queue for start later. */ /* */ /* Call: dda_wrq(ds, hc, abt) */ /* Argument: ds: pointer to device control block struct */ /* hc: pointer to half-duplex channel cntl blk */ /* abt: indication of whether request is an */ /* abort request. */ /* Returns: nothing */ /* Called by: dda_start() */ /* dda_data() */ /* Calls to: mtod() */ /* start_chn() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void dda_wrq(ds, hc, abt) struct dda_softc *ds; register struct hdx_chan *hc; u_char abt; { register struct mbuf *m; register int s; /* set channel info */ s = splimp(); m = hc->hc_curr; hc->hc_addr = mtod(m, u_char *); /* point to data in mbuf */ hc->hc_cnt = m->m_len; /* byte count */ splx(s); /* set Transfer Request Write, mark DDAEOS if end of stream */ /* note: non-unibus products ignore the address invalid (DDAXFR) bit */ if (abt) hc->hc_func = DDAABT; else if (m->m_next == 0) hc->hc_func = DDAWRT + DDAEOS + DDAXFR; else hc->hc_func = DDAWRT + DDASTR + DDAXFR; #ifdef DDADEBUG if (DDADBCH(15, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: dda_wrq: chan=%d func=%x\n", ds->dda_if.if_unit, hc->hc_chan, hc->hc_func DDAELOG; } #endif DDADEBUG s = splimp(); /* * If ACP comm regs busy, queue start i/o for later. */ if (ds->dda_sioq.sq_head) { (ds->dda_sioq.sq_tail)->hc_next = hc; ds->dda_sioq.sq_tail = hc; hc->hc_next = 0; splx(s); return; } /* start i/o on channel now */ ds->dda_sioq.sq_head = hc; ds->dda_sioq.sq_tail = hc; hc->hc_next = 0; splx(s); (void) start_chn(ds); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDA_RRQ() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Process read requests. Quit if attempt to get an mbuf is */ /* unsuccessful. Put I/O request values in half-duplex control */ /* channel structure: set function code for read from ACP with */ /* Transfer Grant set, set count from data length (byte count) */ /* in mbuf, and subfunction is zero. */ /* */ /* Call: dda_rrq(ds, hc) */ /* Argument: ds: pointer to device control block struct */ /* hc: pointer to half-duplex control chan */ /* Returns: nothing */ /* Called by: ddainit() */ /* dda_data() */ /* dda_supr() */ /* Calls to: MGET() */ /* DDALOG() */ /* start_chn() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void dda_rrq(ds, hc) struct dda_softc *ds; register struct hdx_chan *hc; { register struct mbuf *m; register int s; #ifdef DDADEBUG if (DDADBCH(16, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: dda_rrq()\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) { DMESG(ds->dda_if.if_unit, 25, (DDALOG(LOG_ERR) "dda%d: couldn't get buffer for read\n", ds->dda_if.if_unit DDAELOG)); return; } m->m_len = 0; /* new mbuf, doesn't contain data yet */ /* hc_mbuf set to zero during initialization */ s = splimp(); if (hc->hc_mbuf == 0) { hc->hc_mbuf = m; hc->hc_curr = m; } else { hc->hc_curr->m_next = m; hc->hc_curr = m; m->m_next = 0; } splx(s); hc->hc_func = DDARDB + DDASTR + DDAXFR; hc->hc_sbfc = 0; /* hc_cnt determines size of read request, addr->req_cnt */ /* MLEN is 112 bytes, the data portion of a small mbuf */ hc->hc_cnt = MLEN; /* just got the mbuf */ hc->hc_addr = mtod(m, u_char *); /* point to mbuf data */ s = splimp(); /* * If ACP comm regs busy, queue start i/o for later. */ if (ds->dda_sioq.sq_head) { (ds->dda_sioq.sq_tail)->hc_next = hc; ds->dda_sioq.sq_tail = hc; hc->hc_next = 0; splx(s); return; } /* start i/o on channel now */ ds->dda_sioq.sq_head = hc; ds->dda_sioq.sq_tail = hc; hc->hc_next = 0; splx(s); (void) start_chn(ds); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% START_CHN() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine copies ACP I/O requests into the ACP */ /* Communications Registers (COMREGs) and notifies the ACP. */ /* If the channel number is odd, indicating write, then the */ /* direction flag is set to indicate a transfer from the host */ /* to the front end. */ /* */ /* Call: start_chn(ds) */ /* Argument: ds: pointer to device control block struct */ /* Returns: nothing */ /* Called by: ddainta() */ /* dda_rrq() */ /* dda_wrq() */ /* Calls to: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE int start_chn(ds) struct dda_softc *ds; { volatile struct ddaregs *addr; register struct hdx_chan *hc; register int s; struct uba_device *ui; ui = (struct uba_device *) (ddainfo[ds->dda_if.if_unit]); addr = (struct ddaregs *) ui->ui_addr; #ifdef DDADEBUG if (DDADBCH(17, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: start_chn()\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG /* * Scan sioq for invalid writes */ s = splimp(); hc = (struct hdx_chan *) (ds->dda_sioq.sq_head); if (hc && ((addr->req_flags & FLAGS_RDY) == 0)) { for (; hc; hc = hc->hc_next) { /* scan sioq */ if ((hc->hc_chan & 0x01) && (hc->hc_chan != 1) && (hc->hc_inv & INVALID_MBUF)) { if (ds->dda_cb[hc->hc_chan >> 1].dc_flags & DC_OBUSY) goto send; /* send an abort */ else { if (hc->hc_mbuf) { /* free pending request */ m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; hc->hc_inv &= ~INVALID_MBUF; ds->dda_sioq.sq_head = ds->dda_sioq.sq_head->hc_next; } /* * Restart Output */ /* * not needed ds->dda_cb[hc->hc_chan>>1].dc_flags &= * ~DC_OBUSY; */ /* * this should be changed to drop ipl before calling * dda_start */ dda_start(ds, &ds->dda_cb[hc->hc_chan >> 1]); } } else { /* Read or Write request is valid */ /* * Set up comm regs. */ send: addr->req_chan = hc->hc_chan >> 1; addr->req_cnt = hc->hc_cnt; addr->req_fcn = hc->hc_func; addr->req_sbf = hc->hc_sbfc; if (hc->hc_chan & 1) { /* write */ #ifdef DDADEBUG struct dda_cb *dc = &ds->dda_cb[hc->hc_chan >> 1]; if (DDADBCH(17, ds->dda_if.if_unit)) DDALOG(LOG_DEBUG) "dda%d: start_chn: WRITE on lcn %d func %x\n", ds->dda_if.if_unit, hc->hc_chan >> 1, hc->hc_func DDAELOG; if (dc->dc_lcn) /* don't start timer on lcn 0 */ dc->dc_out_t = TMO_RESTART; /* Wait 90 sec for * completion */ #endif addr->req_flags = FLAGS_RDY | FLAGS_DIR; } else addr->req_flags = FLAGS_RDY; #ifdef DDADEBUG if (DDADBCH(28, ds->dda_if.if_unit)) { if (hc->hc_func == DDAABT) DDALOG(LOG_DEBUG) "dda%d: start_chn: aborting chan %d\n", ds->dda_if.if_unit, hc->hc_chan DDAELOG; } # ifdef DDA_MSGQ dda_mqstr("(sc "); dda_mqnum(hc->hc_chan >> 1, MQHEX); dda_mqstr(" "); dda_mqnum(addr->req_flags, MQHEX); dda_mqstr(" "); dda_mqnum(hc->hc_cnt, MQHEX); dda_mqstr(" "); dda_mqnum(hc->hc_func, MQHEX); dda_mqstr(" "); dda_mqstr(")"); # endif DDA_MSGQ #endif DDADEBUG addr->csr |= CSR_INTRA; /* interrupt FE */ splx(s); return; } } } splx(s); return (1); /* no valid requests found */ } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% BUFRESET() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Reset of interface after UNIBUS reset. If interface is on */ /* specified uba, reset its state. Free mbufs if there is */ /* queued output data. */ /* */ /* Call: bufreset(unit) */ /* Arguments: unit: ACP device unit number */ /* Returns: nothing */ /* Called by: network software, address of routine is */ /* defined in dda_if network interface struct */ /* Calls to: DDALOG() */ /* IF_DEQUEUE() */ /* m_freem() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void bufreset(unit) int unit; { volatile struct ddaregs *addr; register struct uba_device *ui; register struct dda_cb *dc; register struct dda_softc *ds = &dda_softc[unit]; register int lcn; ui = (struct uba_device *) ddainfo[unit]; if (unit >= NDDA || (ui == 0 || ui->ui_alive == 0)) return; DMESG(unit, 38, (DDALOG(LOG_ERR) "dda%d: buffer size reset\n", unit DDAELOG)); ds->dda_if.if_flags &= ~IFF_UP; hist_link_state(unit, ds->dda_state, S_DISABLED); ds->dda_state = S_DISABLED; addr = (struct ddaregs *) ui->ui_addr; addr->cmp_flags = 0; /* I/O completion flags */ addr->xfr_flags = 0; /* transfer request/grant flags */ addr->req_chan = 0; /* FDX channel number */ addr->req_adx = 0; /* address bits 17-16 */ addr->req_addr = 0; /* address bits 15-00 */ addr->req_cnt = 0; /* byte count */ addr->req_fcn = 0; /* I/O function */ addr->req_sbf = 0; /* I/O subfunction */ addr->cmp_chan = 0; /* FDX channel number */ addr->cmp_unused = 0; /* address bits 17-16 */ addr->cmp_cnt = 0; /* byte count */ addr->cmp_stat = 0; /* I/O status */ addr->cmp_sbst = 0; /* I/O substatus */ addr->xfr_chan = 0; /* FDX channel number */ addr->xfr_adx = 0; /* address bits 17-16 */ addr->xfr_addr = 0; /* address bits 15-00 */ addr->xfr_cnt = 0; /* byte count */ addr->sys_stat = 0; /* system status */ addr->req_flags = 0; /* clear handshake flags, mailbox */ /* pass interrupt vector to ACP */ addr->sys_vect = dda_softc[unit].dda_vector >> 2; dda_softc[unit].dda_flags = 0; /* clear ACP operational flag */ ds->dda_init &= ~DDA_INTCLOCK; /* reset internal-clocking-set bit */ nddach[unit] = NDDACH_DEFAULT; /* reset SVC limit */ /* LCNLINK */ dc = dda_softc[unit].dda_cb;/* flush any queued output data */ for (lcn = 0; lcn <= NDDACH; lcn++) { /* for all LCN's ... */ dc->dc_inaddr.s_addr = 0; /* clear remote internet addr */ dc->dc_key.key_addr.s_addr = 0; dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; dc->dc_state = LC_IDLE; /* init LCN state */ dc->dc_timer = TMO_OFF; /* turn LCN timer off */ dc->dc_flags = 0; #ifdef DDADEBUG dc->dc_out_t = TMO_OFF; /* turn FE completion timer off */ #endif dc++; } hist_all_lcns(unit, LC_IDLE); #ifdef DDA_RAWOPT pi_init(unit, 1); #endif #ifdef DDA_PADOPT x29_init(unit, 1); #endif addr->csr |= CSR_IENB; wbflush(); /* flush write pipeline cache */ } /* * Disable all interrrupts and forget about board */ PRIVATE void dda_disable(unit) { volatile struct ddaregs *addr; register struct uba_device *ui; ui = (struct uba_device *) ddainfo[unit]; addr = (struct ddaregs *) (ui->ui_addr); addr->csr = 0; wbflush(); /* flush write pipeline cache */ } /* Revision History: 13-Jul-1989: PST Used PRIVATE convention on functions 01-Aug-1989: PST Added initialization of ddamsgs in attach routine. 01-Sep-1989: PST Ignore uban parameter to ddareset. 14-Nov-1989: PST Added support for MIPS PMAX architecture and fixed a QBUS bug (we were not using all 22 addr bits). Moved rrq and wrq routines here. 30-Nov-1989: PST Moved unibus defines here. 01-Dec-1989: PST Fixed typos. */ Returns: nothing driver/if_ddareg.h 444 540 24 16373 4535335127 7513 /*************************************************************************/ /* */ /* */ /* ________________________________________________________ */ /* / \ */ /* | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* \________________________________________________________/ */ /* */ /* Copyright (c) 1986 by Advanced Computer Communications */ /* 720 Santa Barbara Street, Santa Barbara, California 93101 */ /* (805) 963-9431 */ /* */ /* */ /* File: ddareg.h */ /* When this file is copied to the /sys/vaxif */ /* directory, it is renamed 'if_ddareg.h'. */ /* */ /* Project: UNIX 4.n BSD DDA-X.25 Network Interface Driver */ /* for ACP 5250 and ACP 6250 */ /* */ /* Function: This file contains definitions of the hardware */ /* interface of the ACP 5250/6250. */ /* */ /* Revision History at end of file */ /* */ /*************************************************************************/ /* ACP device Communication Register layout */ #ifdef SIMULATION /* device registers */ struct ddaregs { u_short csr; /* control and status register */ /* I/O request mailbox */ u_short req_chan; /* FDX channel number */ u_char req_flags; /* mailbox flags */ u_char req_adx; /* address bits 17-16 */ u_short req_addr; /* address bits 15-00 */ u_short req_cnt; /* byte count */ u_char req_sbf; /* I/O subfunction */ u_char req_fcn; /* I/O function */ /* I/O completion mailbox */ u_short cmp_chan; /* FDX channel number */ u_char cmp_flags; /* mailbox flags */ u_char cmp_unused; /* address bits 17-16 */ u_short cmp_cnt; /* byte count */ u_char cmp_sbst; /* I/O substatus */ u_char cmp_stat; /* I/O status */ /* Transfer request mailbox */ u_short xfr_chan; /* FDX channel number */ u_char xfr_flags; /* mailbox flags */ u_char xfr_adx; /* address bits 17-16 */ u_short xfr_addr; /* address bits 15-00 */ u_short xfr_cnt; /* byte count */ /* System status mailbox */ u_char sys_id; /* system identification */ u_char sys_vers; /* system version number */ u_char sys_stat; /* system status */ u_char sys_vect; /* interrupt vector base */ }; #else /* device registers */ struct ddaregs { u_short csr; /* control and status register */ /* I/O request mailbox */ u_short req_chan; /* FDX channel number */ u_char req_adx; /* address bits 17-16 */ u_char req_flags; /* mailbox flags */ u_short req_addr; /* address bits 15-00 */ u_short req_cnt; /* byte count */ u_char req_fcn; /* I/O function */ u_char req_sbf; /* I/O subfunction */ /* I/O completion mailbox */ u_short cmp_chan; /* FDX channel number */ u_char cmp_unused; /* address bits 17-16 */ u_char cmp_flags; /* mailbox flags */ u_short cmp_cnt; /* byte count */ u_char cmp_stat; /* I/O status */ u_char cmp_sbst; /* I/O substatus */ /* Transfer request mailbox */ u_short xfr_chan; /* FDX channel number */ u_char xfr_adx; /* address bits 17-16 */ u_char xfr_flags; /* mailbox flags */ u_short xfr_addr; /* address bits 15-00 */ u_short xfr_cnt; /* byte count */ /* System status mailbox */ u_char sys_vers; /* system version number */ u_char sys_id; /* system identification */ u_char sys_vect; /* interrupt vector base */ u_char sys_stat; /* system status */ }; #endif /* defines for CSR */ #define CSR_BIT15 0x8000 #define CSR_BIT14 0x4000 #define CSR_MAINT 0x2000 #define CSR_HALT 0x1000 #define CSR_IBPEND 0x0800 #define CSR_IAPEND 0x0400 #define CSR_IBREQ 0x0200 #define CSR_IAREQ 0x0100 #define CSR_BIT7 0x0080 #define CSR_BIT6 0x0040 #define CSR_INTRB 0x0020 /* ACP CPU Interrupt A Request */ #define CSR_INTRA 0x0010 /* ACP CPU Interrupt B Request */ #define CSR_IENB 0x0008 /* enable UNIBUS interrupt b */ #define CSR_IENA 0x0004 /* enable UNIBUS interrupt a */ #define CSR_DMAEN 0x0002 #define CSR_RESET 0x0001 /* mailbox handshake flags, these flags are used with the req_flags, */ /* cmp_flags, and xfr_flags to indicate current state of events */ #define FLAGS_RDY 0x80 /* indicates ready */ #define FLAGS_DON 0x40 /* indicates done */ #define FLAGS_DIR 0x20 /* indicates write (host to ACP) */ /* I/O request function code definitions */ #define DDARDB 0x01 /* read from ACP */ #define DDAWRT 0x02 /* write to ACP */ #define DDASTR 0x10 /* stream flag */ #define DDAEOS (0x20|DDASTR) /* end of stream flag */ #define DDAABT 0x04 /* abort flag */ #define DDAXFR 0x40 /* indicates transfer request */ /* The UNIBUS address in req_addr is */ /* invalid. The ACP device must issue */ /* a Transfer Request in order to */ /* obtain the UNIBUS address */ #define DDASWP 0x80 /* Swap host high/low bytes */ /* The ACP device views UNIBUS memory */ /* as if it were MC68000 memory: the */ /* MS byte of a word is even-addressed */ /* byte and the LS byte is the odd- */ /* addressed byte. */ #define FCN_MASK 0x07 #define DDA_BITS \ "\10\20UER\17NXM\16PER\15ZRUN\14ZGO\10MBLK\7SRV\6MAIN\5DMA\4WRT\3IEN\2RST\1NMI" /* Host Request Mailbox Completion Status */ #define DDAIOCOK 0x01 /* successful completion */ #define DDAIOCOKP 0x02 /* successful completion, more data pending */ #define DDAIOCABT 0xff /* i/o aborted */ #define DDAIOCERR 0xfe /* program error */ #define DDAIOCOVR 0xfd /* overrun error */ #define DDAIOCUBE 0xfc /* Transfer count = 0 */ /* Either program error (byte count on I/O */ /* request equals 0) or driver error (driver */ /* granted byte count = 0 in response to */ /* Transfer Request) */ #define DDAIODMAE 0xfb /* DMA completion error: Completion sub- */ /* status equals error bits from the ACP DMA */ /* status register */ #define DDAIOLCOL 0xf9 /* Listen Collision: Both sides of a DPN in */ /* the same direction have Listen requests */ /* pending. Both requests are terminated */ /* with this status code. */ #define DDAIOFUNC 0xf8 /* Invalid function: The function specified */ /* in a request is invalid. */ #define DDAIODPN 0xf7 /* Invalid DPN: The DPN specified in a */ /* request is out of the range handled by */ /* CRI (Communications Register Interface) */ #ifdef __GNU__ #define ddaregs ddaregs volatile #endif __GNU__ /* Revision History: 26-Mar-1986: V1.0 Clair Russ First generated. ??-???-1988: V1.1 Steve Johnson Added code for simulation 19-Mar-1989: V4.3.1 Paul Traina Added Multinet / GCC support for structure padding 28-May-1989: V4.3.6 Paul Traina Cosmetic changes only */ X channel number */ u_char cmp_unused; /* address bits 17-16 */ u_char cmp_flags; /* mailbox flags */ u_short cmp_cnt; /* byte count */ u_char cmp_stat; /* I/O status */ u_char cmp_sbst; /* I/O substatus */ /* Transfer request mailbox *driver/if_ddavar.h 444 540 24 61453 4535335126 7524 /*************************************************************************/ /* */ /* */ /* ________________________________________________________ */ /* / \ */ /* | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* \________________________________________________________/ */ /* */ /* Copyright (c) 1986 by Advanced Computer Communications */ /* 720 Santa Barbara Street, Santa Barbara, California 93101 */ /* (805) 963-9431 */ /* */ /* */ /* File: ddavar.h */ /* When this file is copied to the /sys/vaxif */ /* directory, it is renamed 'if_ddavar.h'. */ /* */ /* Project: UNIX 4.n BSD DDA-X.25 Network Interface Driver */ /* for ACP 5250 and ACP 6250 */ /* */ /* Function: This file contains definitions used to control */ /* and track the status of the ACP 5250/6250. */ /* The values for the Set System Parameter Messages */ /* are located here. Note that the values are the */ /* same as those for ACP 625 for compatibility. */ /* */ /* Revision History at end of file */ /*************************************************************************/ #if defined(DDA_PADOPT) || defined(DDA_RAWOPT) # define DDA_PAD_OR_RAW #endif #define NDDACH 126 /* maximum number of channels */ #define NDDACH_DEFAULT 64 /* default number of channels */ /* path numbers (also called logical channel numbers, or lcns) */ #define DDA_SUPR 0x00 /* supervisory path for control */ #define ALL_CHANS 0xff /* denotes all LCNs */ /* the following 2 defines (MAXADDRLEN and MINADDRLEN) give the max and min * length X25 addresses. These numbers include 1 byte to hold the length * of the address. Thus the normal value of MAXADDRLEN is 15, for 14-digit * X.25 addresses; MAXADDRLEN can never be greater than 16 (15-digit addr). */ #define MAXADDRLEN 16 /* max length of an X.25 address */ #define MINADDRLEN 1 /* min length of an X.25 address */ #define TRANSPAC 1 /* network type for TRANSPAC (def=0) */ #define DDAMTU 1006 /* maximum IP msg length */ #define DDA_OQMAX 8 /* max IP msgs on LCN output q */ #define DDA_TIMEOUT 10 /* dda timer interval, in seconds */ /* definitions for the DC_FLAGS byte of the dda_cb structure */ /* bits in dc_flags */ #define DC_OBUSY 0x01 #define DC_CLIENTS 0x0e #define DC_IP 0x00 /* this channel is being used for IP traffic */ #define DC_X29 0x02 /* this channel is being used for X29 traffic */ #define DC_X29W 0x04 /* this channel waiting for x29 connection */ #define DC_RAW 0x08 /* this is a raw X25 channel */ #define DC_IPEND 0x10 /* input pending */ /* bits in hc_sbfc (which is the wrong place) */ #define INVALID_MBUF 0x01 /* Used to clear outstanding I/O */ typedef unsigned char byte; typedef int boolean; /* X25 LCN state definitions (in dc_state) */ #define LC_DOWN 0 /* X25 circuit down */ #define LC_RESTART 1 /* X25 circuit restarting */ #define LC_IDLE 2 /* X25 circuit not in use */ #define LC_CALL_PENDING 3 /* X25 circuit call pending */ #define LC_DATA_IDLE 4 /* X25 circuit open */ #define LC_CLR_PENDING 5 /* X25 circuit clear pending */ /* Timeout definitions (in dc_timer and dc_out_t) */ #define TMO_OFF 0 /* timer off */ #define TMO_RESTART ( 90/DDA_TIMEOUT) /* restart timeout */ #define TMO_CALL_PENDING (180/DDA_TIMEOUT) /* call timeout */ #define TMO_DATA_IDLE (600/DDA_TIMEOUT) /* idle circuit timeout */ #define TMO_CLR_PENDING (380/DDA_TIMEOUT) /* clear timeout */ /* Link status codes (third byte of LINE_STATUS message) */ #define LINK_DOWN 0x00 /* Link layer is down */ #define LINK_UP 0x01 /* Link layer is up */ #define LINK_DISABLED 0x02 /* Link layer is disabled */ /* The following parameter modification commands such as BAUD_CNTL, */ /* or WATCHDOG, are one-byte values containing size and ID followed */ /* by 0-2 bytes of parameter information. The number of bytes of */ /* parameter information is specified in the most significant 2 bits */ /* of the command, the other 6 bits are the ID. A 00, 01, or 10 */ /* specify respectively 0, 1, or 2 bytes of parameter information */ /* follow. */ /* Line control codes (in body of LINE_CNTL message) */ #define LINK_DISABLE 0x00 /* Disable link layer */ #define LINK_ENABLE 0x01 /* Enable link layer */ #define LINK_LOOPBACK 0x42 /* Link layer loopback mode */ #define LOOP_NONE 0x00 /* Loopback off */ #define LOOP_EXTERNAL 0x01 /* Loopback external */ #define LOOP_INTERNAL 0x03 /* Loopback internal */ #define DTE_DCE_MODE 0x43 /* DTE/DCE Mode Parameter */ #define DTE 0x00 /* operate as DTE */ #define DCE 0x01 /* operate as DCE */ #define DTE_ADDRESS 0x44 /* DTE Address Parameter */ #define DTE_ADRVAL 0x03 /* DTE Address value */ #define DCE_ADDRESS 0x45 /* DCE Address Parameter */ #define DCE_ADRVAL 0x01 /* DCE Address value */ #define IFRAME_TIMEO 0x46 /* I-Frame Timeout Parameter */ #define IFRAME_TOVAL 0x03 /* I-Frame Timeout value 3s */ #define POLL_TIMEO 0x47 /* Poll Timeout Parameter */ #define POLL_TOVAL 0x03 /* Poll Timeout value 3s */ #define ADM_TIMEO 0x48 /* ADM Timeout Parameter */ #define ADM_TOVAL 0x03 /* ADM Timeout value 3s */ #define RETRY_LIMIT 0x4a /* Retry Limit Parameter */ #define RETRY_VAL 0x14 /* 20 (decimal) retries */ #define WATCHDOG 0x4b /* Watchdog Timeout Parameter */ #define WATCHDG_VAL 0x03 /* Watchdog Timeout value 3s */ #define BAUD_CNTL 0xa9 /* Baud Rate Parameter */ #define CLOCK_CNTL 0x6a /* Select Clock Source */ #define EXTERNAL_CLOCK 0x00 /* clock generated externally */ #define INTERNAL_CLOCK 0x01 /* clock generated internally */ #define IDLE_POLL 0x4d /* Idle Poll Parameter */ #define IDLE_POLL_ON 0x01 /* Idle Polling on */ #define IDLE_POLL_OFF 0x00 /* Idle Polling off */ #define FRAME_WINDOW 0x4e /* Frame Window Parameter */ #define FWINDW_VAL 0x07 /* Frame Window value 7 */ #define PKT_WINDOW 0x4f /* Packet Window Parameter */ #define PWINDW_VAL 0x02 /* Packet Window value 2 */ #define PKT_SIZE 0x90 /* Packet Size Parameter */ #define PVAL_BYTE1 0x08 /* least significant byte */ #define PVAL_BYTE2 0x00 /* most significant byte */ #define MAX_PKT_SZ 0xbe /* Max Packet Size */ #define MAX_PKT_WN 0x7c /* Max Packet Window Size */ #define PKT_OPTIONS 0x77 /* Supported 1984 options */ #define HIGH_THRESH 0x51 /* High Buffer Threshold Param */ #define HTRSH_VAL 0x08 /* High Threshold value */ #define LOW_THRESH 0x52 /* Low Buffer Threshold Param */ #define LTRSH_VAL 0x08 /* Low Threshold value */ #define QUEUED_BUFS 0x53 /* Max Number of Queued Buffers */ #define QBUF_VAL 0x08 /* Queued Buffer value */ #define QUEUED_IFRAMES 0x54 /* Max Number Queued I-Frames */ #define QIFRAME_VAL 0x08 /* Queued I-Frame value */ #define FRAME_SIZE 0x95 /* Maximum Frames Size Parametr */ #define FRAME_SIZE1 0x95 /* least significant byte */ #define FRAME_SIZE2 0x01 /* most significant byte */ /* for value of 405 (decimal) */ #define LCGN 0x56 /* Logical Channel Group Number */ #define LCGN_VAL 0x00 /* lcgn value */ #define SVC_LIMIT 0x57 /* Switched Virtual Circuit */ #define SVC_VAL 0x20 /* SVC value 32 (decimal) */ #define DDAF_OK 0x0001 /* ACP operation flag */ #define DDASTAT_OK 0x00 /* ACP system status returned */ /* on interrupt "b" */ #define DDASTAT_ERR 0x80 #define DDASTAT_NMC 0x7F /* flag values for ds->dda_init */ #define INIT_OK 0x10 /* ok to call x25_init() */ #define DDA_STANDARD 0x01 /* DDN standard X.25 service */ #define DDA_BASIC 0x02 /* DDN basic X.25 service */ #define DDA_PDN 0x04 /* Public Data Network X.25 */ #define DDA_INTCLOCK 0x08 /* internal clocking is set */ #define DDA_CLASS_A_B 0x20 /* Standard service/table lookup */ #define DDA_PKTNEG 0x40 /* Packet size negotiation flag */ #define DDA_WNDNEG 0x80 /* Window size negotiation flag */ /* the following offsets are for ddainit_msg, the set systems parameters */ /* message */ #define LOOP_OFFSET 6 /* set system parms, loopback */ #define DTE_OFFSET 8 /* set system parms, dte/dce mode */ #define BAUD_OFFSET 10 /* set system parms, dte/dce mode */ #define CLOCK_OFFSET 18 /* set system parms, dte/dce mode */ #define DOWN_OFFSET 19 /* set system parms, line down */ #define MSG_LENGTH 3 /* offset for message length */ #define MSG_OFFSET 4 /* offset for start of parameters */ #define MSGS_BAUD 3 /* msg size for baud rate parms */ /* X25 supervisor message codes */ #define CALL 0x00 /* outgoing call */ #define RING 0x01 /* incoming call */ #define CLEARVC 0x02 /* clear by VCN */ #define ANSWER 0x03 /* answer call */ #define CLEARLC 0x04 /* clear by LCN */ #define RESET 0x20 /* reset LCN */ #define RESET_ACK 0x21 /* reset ack */ #define INTERRUPT 0x22 /* X25 interrupt */ #define READY 0x23 /* flow control ready */ #define INTR_ACK 0x24 /* interrupt ack */ #define RESTART 0x40 /* X25 restart */ #define RSTRT_ACK 0x41 /* restart ack */ #define SYS_STATUS 0x42 /* system status msg */ #define LINE_CNTL 0x60 /* link control cmnd */ #define LINE_STATUS 0x61 /* link status resp */ #define SET_BFR_SIZE 0x62 /* set firmware buffer size */ #define STATQUERY 0217 /* statistics query */ #define STATRESP 0216 /* statistics response */ /* X25 facilities */ #define X25_FACIL_PKTSIZE 0x42 /* CCITT packet size negotiation*/ #define X25_FACIL_WINSIZE 0x43 /* CCITT window size negotiation*/ #define DDN_FACIL_MARKER 0 /* two of these mark DDN private*/ #define X25_FACIL_DDA 0x04 /* DDA mode facility */ #define FAC_DDASTD 0x01 /* DDA standard mode */ #define PKTSIZE_LARGE 12 /* lg2 (4096) for calls */ #define WINSIZE_LARGE 7 /* Large (7) window for calls */ #define PKTSIZE_DEF 7 /* Default packet size */ #define WINSIZE_DEF 2 /* Default window size */ /* X25 protocols */ #define X25_PROTO_IP 0xcc /* X25 IP protocol type code */ #define X25_PROTO_X29 0x01 /* X29 over X25 protocol type code */ /* DMESG(unit,value,statement) e.g. DMESG(0,27,printf("error message")) */ /* v should be a constant (no side effects) */ /* (v) >> 5 == (v) / 32 - find which word the bit is in. */ /* (v) & 0x1f == (v) % 32 - find the number of the bit to check */ /* DMESGSET - set a msg bit to 1 */ /* DMESGCLR - set a msg bit to 0 */ /* DMESGTOG - toggle a msg bit */ /* DMESGVAL - the value of a message bit. 0 or non-zero */ #define DMESGSET(u,v) (ddamsgs[(u)][(int)((v)>>5)] |= (1 << ((v) & 0x1f))) #define DMESGCLR(u,v) (ddamsgs[(u)][(int)((v)>>5)] &= ~(1 << ((v) & 0x1f))) #define DMESGTOG(u,v) (ddamsgs[(u)][(int)((v)>>5)] ^= (1 << ((v) & 0x1f))) #define DMESGVAL(u,v) (ddamsgs[(u)][(int)((v)>>5)] & (1 << ((v) & 0x1f))) #define DMESG(u,v,s) (DMESGVAL(u,v) ? 0 : (s)) #ifdef VAXVMS /* always enable debugging under VMS */ #define DDADEBUG #endif VAXVMS #ifdef DDADEBUG #define DDADBCH(n, unit) (!DMESGVAL(unit, n + 128)) /* first 128 are !debug*/ #endif /* macros to test for call logging -- only use unit 0 space for now */ #define LOG_BUSY (!DMESGVAL(0, 256)) /* "all circuits in use" * and "no circuits available" */ #define LOG_CALLS (!DMESGVAL(0, 257)) /* calls and clears */ #define LOG_ABT (!DMESGVAL(0, 258)) /* I/O aborts */ /* values in dda_state */ #define S_DISABLED 0 /* link is disabled */ #define S_COMING_UP 1 /* enable issued, waiting for response */ #define S_LINK_UP 2 /* link operational */ #define S_GOING_DOWN 3 /* disable issued, waiting for response */ struct trtab /* This stuff should ALSO be in ddareg.h */ { /* Address Translation Table IOCTL Data */ unsigned char func; unsigned char x25addr[MAXADDRLEN]; unsigned long ipaddr; }; struct ddactl /* used for -m (message), -q (query), -n (svc_count) */ { unsigned char func; unsigned char nothing[3]; int drval; char msg[MLEN]; }; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% The download structure is the template for communication %%*/ /*%% with the ACP7000 when it is running diagnostic roms. The %%*/ /*%% will initially place the SYSGEN_VALID entry in the BIIC GPR0 %%*/ /*%% when in the probe routine. The IOCTL mechanism has been %%*/ /*%% extended to support a *very* limited download feature which %%*/ /*%% enables us to load programs into the 7000 and then execute %%*/ /*%% them. Versabug has been modified to examine GPR0 for the %%*/ /*%% SYSGEN_DLOAD value. GPR1 will contain the physical address of %%*/ /*%% our standard sysgen block. The driver will then place messages %%*/ /*%% in the request queue. These messages will be read by the 7000 %%*/ /*%% diagnostic firmware and placed into memory as needed. After %%*/ /*%% all code has been loaded, a final "EXEC" command may be sent %%*/ /*%% to the 7000 giving an execution start address. The 7000 will %%*/ /*%% then execute the system code (which should wait for GPR0 to %%*/ /*%% contain the standard SYSGEN_VALID pattern before continuing. %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #ifdef ACP_BI #define SYSGEN_VALID 0x12345678 /* normal operation mode */ #define SYSGEN_DLOAD 0x31415927 /* download operation mode */ #define DN_LCMD_SETUP 0 /* reset board, alloc buffers, setup shm */ #define DN_LCMD_FEOP 1 /* send command and data to front end */ #define DN_LCMD_CLEANUP 2 /* dealloc buffers and restore shm to norm */ #define DN_TYPE_ID 1 /* identification record */ #define DN_TYPE_VER 2 /* version record */ #define DN_TYPE_COPY 3 /* copyright record */ #define DN_TYPE_DATA 4 /* data record */ #define DN_TYPE_XFR 5 /* start address transfer record */ struct dda_dnload /* code download structure */ { unsigned char func; /* ioctl function code (will be 'L') */ unsigned char lcommand; /* driver load command (setup/op/cleanup) */ unsigned short padding; /* null padding for dest addr */ unsigned short len; /* length of entire record */ unsigned short type; /* type of record passed */ unsigned int dest; /* destination address */ char *data; /* address of data record in user space */ }; #endif ACP_BI /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% Histogram support declarations and defines. Slots 0-NDDACH %%*/ /*%% record the amount of time n channels were being used. The %%*/ /*%% following define uses of locations that come after the %%*/ /*%% first NDDACH entries. %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #define H_LINK_UP (NDDACH+1) /* entry that records the time link is*/ /* up 0-126 */ #define H_START (NDDACH+2) /* starting time */ #define H_END (NDDACH+3) /* ending time */ #define H_TMO (NDDACH+4) /* current value of idle timer */ #define HISTSIZE (H_TMO+1) /* size of the histogram table */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% Information for each device unit is maintained in an %%*/ /*%% array of structures named dda_softc[]. The array is %%*/ /*%% indexed by unit number. Each entry includes the network %%*/ /*%% interface structure (dda_if) used by the routing code to %%*/ /*%% locate the interface, an array of Logical Channel %%*/ /*%% control blocks which maintain information about each of %%*/ /*%% the Logical Channels (LCNs) through which X.25 virtual %%*/ /*%% calls are established, a queue of I/O requests pending %%*/ /*%% for the ACP, the UNIBUS interrupt vector for the unit %%*/ /*%% and misc flags. The Logical Channel Control blocks %%*/ /*%% maintain information about the state of each LCN, a %%*/ /*%% queue of outbound data, Half Duplex Channel (HDX) blocks %%*/ /*%% used for queuing I/O requests to the ACP and an ifuba %%*/ /*%% structure which records the UNIBUS resources being held %%*/ /*%% by the LCN. %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ struct sioq /* Start I/O queue head */ { struct hdx_chan *sq_head; /* queue head */ struct hdx_chan *sq_tail; /* queue tail */ }; struct hdx_chan /* HDX channel block */ { struct hdx_chan *hc_next; /* link to next HDX channel */ struct mbuf *hc_mbuf; /* mbuf chain */ struct mbuf *hc_curr; /* current mbuf in chain */ unsigned char *hc_addr; /* address bits 15-00 */ unsigned short hc_cnt; /* byte count */ unsigned char hc_func; /* I/O function */ unsigned char hc_sbfc; /* I/O subfunction */ unsigned char hc_chan; /* HDX channel number */ unsigned char hc_inv; /* place to store various bits */ #ifdef vax11c /* VAX C compiler (and GCC running in VAXC native mode) does not pad structures to longword boundaries, so we must pad any structures that are passed between ACPCONFIG and the driver */ unsigned char _vaxcfill[2]; /* pad structure to longword boundary */ #endif }; union dc_key { struct in_addr key_addr; unsigned long key_val; }; struct dda_cb /* Logical Channel control block */ { #if defined(DDA_PADOPT) && defined(VAXVMS) int ptyucb; /* to store the np unit control blk addr */ int ttyflags; /* flags for the VMS x29 driver */ #endif int dc_line; /* index into tty structure / minor number */ struct in_addr dc_inaddr; /* remote Internet address */ union dc_key dc_key; /* circuit destination key */ unsigned char dc_lcn; /* LCN number */ unsigned char dc_state; /* LCN state */ unsigned short dc_timer; /* LCN timer */ struct ifqueue dc_oq; /* LCN output queue */ struct hdx_chan dc_rchan; /* LCN read HDX channel */ struct hdx_chan dc_wchan; /* LCN write HDX channel */ short dc_next; /* LCN next index. Long so padding will work */ unsigned char dc_wsizeout; /* negotiated outgoing window size */ unsigned char dc_wsizein; /* negotiated ingoing window size */ unsigned char dc_pktsizeout;/* negotiated outgoing packet size */ unsigned char dc_pktsizein; /* negotiated ingoing packet size */ unsigned short dc_flags; /* misc flags, DC_OBUSY */ unsigned short dc_out_t; /* DEBUG output completion timer per lcn */ #ifdef vax11c /* VAX C compiler (and GCC running in VAXC native mode) does not pad structures to longword boundaries, so we must pad any structures that are passed between ACPCONFIG and the driver */ unsigned char _vaxcfill[2]; /* pad structure to longword boundary */ #endif }; struct dda_softc /* device control structure */ { struct ifnet dda_if; /* network-visible interface */ struct dda_cb dda_cb[NDDACH + 1]; /* Logical Channel cntl blks */ struct sioq dda_sioq; /* start I/O queue */ int dda_vector; /* UNIBUS interrupt vector */ int dda_mapreg; /* UNIBUS mapping register */ unsigned char dda_flags; /* ACP operational flag (intr b) */ unsigned char dda_init; /* flag for init, X.25 service */ unsigned char dda_state; /* state of link (see below) */ unsigned char dda_firmrev; /* firmware revision level byte */ struct in_addr dda_ipaddr; /* local IP address */ int dda_net_id; /* net type -- for TRANSPAC */ }; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% The dc_flags field in the dda_cb structure is used to %%*/ /*%% indicate that output is busy, DC_OBUSY. In ddainit(), %%*/ /*%% the flag is set to zero. In dda_start(), return if the %%*/ /*%% flag is set DC_OBUSY; otherwise if output isn't active %%*/ /*%% an attempt is made to send another packet. The packet %%*/ /*%% is dequeued, and the flag is set for DC_BUSY on. In %%*/ /*%% both dda_data and dda_supr, if a write completion is %%*/ /*%% indicated, DC_BUSY is turned off before firing up a write %%*/ /*%% via a call to dda_start(). %%*/ /*%% %%*/ /*%% In order to modify packet level parameters like packet %%*/ /*%% window, packet level must be restarted. Doing this %%*/ /*%% reliably requires the driver know four possible states %%*/ /*%% for the FEP: DISABLED (down and not attempting to bring %%*/ /*%% the link up); COMING UP (processed a "-u N" for N != 0, %%*/ /*%% but have not exchanged restarts yet); UP (data transfer %%*/ /*%% state); GOING DOWN (processed "-u 0", but the other end %%*/ /*%% has not yet agreed). The dda_state variable tracks these %%*/ /*%% four states. No other data structure provides this level %%*/ /*%% of resolution. In particular, IFF_UP is always on when %%*/ /*%% dda_state is UP, but sometimes on when dda_state is %%*/ /*%% COMING UP or GOING DOWN. %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #ifdef DDA_MSGQ #define MSGQSIZE 2048 #define MQHEX 1 /* marks the start of a hex number */ #define MQDEC 2 /* marks the start of a decimal number */ #define MQEND 0 /* marks the end of message queue data */ #define MSGQNAME "_ddamsgq" /* the text name of the message q */ #endif /* Revision History: 26-Mar-1986: V1.0 - First generated. Clare Russ. 05-Sep-1986: V1.1 - Add definitions for modified ACPCONFIG interface Clare Russ. 27-Oct-1986: V1.2 - Change TMO_DATA_IDLE from 30 to 600 sec. Lars Poulsen and Randy Graves. 30-Jan-1987: V1.3 - Added DDASTAT_NMC Jeff Berkowitz and Stephanie Price 30-Mar-1987: V1.4 - Added new #defines for X.25 facilities Jeff Berkowitz 09-Sep-1987: V2.1 - Moved all major structure and constant declarations from the driver to this file. Added new #defines for X.25 options and facilities Note that you must now edit this file to turn on debugging and logging. Stephanie Price 18-Mar-1988: V3.0 Brad Engstrom Added four fields to the dda_cb structure. These fields are used to track the negotiated packet and window size for both incoming and outgoing directions. This data will be displayed by the -l option of acpconfig. 12-Apr-1988: V3.0 Brad Engstrom Moved all initialized variables to if_dda.c. This will allow this file to work under BSD or ULTRIX because the ULTRIX C compiler gags on variables declared in header files. 15-Apr-1988: V3.0 Brad Engstrom Added a key field for doing searches for a matching destination. This is used to see if there is already an open circuit to a particular destination. 15-Apr-1988: V3.0 Brad Engstrom Added a next pointer to the dda_cb structure. This will allow lcn structures to be linked into free and active lists. The pointer is actully just an offset to the next element in the list. This allows programs such as acpconfig to traverse a copy of the structure in memory. 15-Apr-1988: V3.0 Brad Engstrom Got rid of conditional compilation of dc_out_t field in the dda_cb structure. This will always be included in the structure, but will only be used if DDA_DEBUG is on. This releaves problems of padding size when adding new fields to the dda_cb structure. 22-Apr-1988: V3.0 Brad Engstrom Added new macro DMESG. This is used to conditionally print driver console messages. 22-Apr-1988: V3.0 Brad Engstrom Added a new define DDAMAINT_BRD. If this is defined then it is assumed that the maintenance board is being used so don't do checks for the board id in the probe routine. 10-May-1988: V3.0 Brad Engstrom Added the constant MAXADDRLEN which is the maximum lenth of an X.25 address. Also added MINADDRLEN to specify the minimum X25 address length. 26-Oct-1988: V3.1 Charles Carvalho Added documentation. 15-Aug-1988: V4.0 Brad Ensgtrom added support for X.29 and Programmers Interface 02-Sep-1988: V4.0 Brad Ensgtrom added new field hc_inv to the hc structure. Someone used the hc_sbfc field to store the invalid bit. Now that the PI will use the subfunction field the invalid bit must be stored someplace else. 09-Jan-1989: V4.1 SAJ -- Merge 4.0, 3.1 relaxed MAXADDRLEN & MINADDRLEN to allow for transpac installed padding changes from TGV for vax11c 17-Feb-1989: Paul Traina Merged SAJ's changes for DDA_DEBUG 28-May-1989: Paul Traina Changed structure padding for TGV 20-Jun-1989: Paul Traina Eliminated old debug logic... call loging is next on the hit list. New driver is not compatible with old acpconfig! 18-Jul-1989: Paul Traina Moved dc_key.ttyline out of union, creating dc_line. This is to stop it from getting clobbered on restarts et al. 25-Oct-1989: Paul Traina Added download structure for ACP7000 downloading and code execution. */ ture provides this level %%*/ /*%% of resolution. In particular, IFF_UP is always on when %%*/ /*%% dda_state is UP, but sometimes on when dda_state is %%*/ /*%% COMING UP or GOING DOWN. %%*/ /*%% driver/if_pi.c 444 540 24 132531 4535335167 6707 /*************************************************************************/ /* */ /* */ /* ________________________________________________________ */ /* / \ */ /* | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* \________________________________________________________/ */ /* */ /* Copyright (c) 1989 by Advanced Computer Communications */ /* 720 Santa Barbara Street, Santa Barbara, California 93101 */ /* (805) 963-9431 */ /* */ /* */ /* Files: if_pi.c, if_pivar.c */ /* ACP_PI (Programmer Interface) for 4.3bsd and */ /* Ultrix 2.0 (and newer) */ /* */ /* Author: ??? (Steve or Charles) */ /* */ /* Project: Programmers Interface for 6250 software */ /* */ /* Function: To enable network connections on ACP_PI to */ /* communicate with UNIX. */ /* */ /* Configuration Entry: */ /* */ /* device dda0 at uba? csr 0166740 vector ddainta ddaintb */ /* */ /* */ /* Revision History at end of file */ /*************************************************************************/ /*************************************************************************/ /* */ /* Usage Notes: */ /* */ /* - make devices in /dev for those pi */ /* devices which you want in your configuration */ /* */ /* System Notes: */ /* */ /* Refer to the installation instructions, readme.txt, which */ /* are included on the pi driver distribution medium. */ /* */ /* Design Overview: */ /* */ /* Data flows in two directions through the PI interface. This section */ /* covers how data gets in and out of the driver. */ /* */ /* OUTBOUND DATA (user process -> FE): */ /* */ /* 1) The user process issues an ioctl(2) call with pi_dblock structure */ /* which gives the address and length of the user data as well as */ /* the lcn that the data is to go out on. */ /* 2) The piioctl routine in the case XIOWRITE checks the validity of */ /* the specified lcn the calls pi_write. */ /* 3) The pi_write routine allocates a small mbuf. If the user data */ /* is longer than MLEN (112) bytes then a page cluster is allocated */ /* to convert the small mbuf into a large mbuf. The user data is */ /* then copied to the mbuf. The mbuf is put on the output queue for */ /* the lcn (dc->dc_oq) using the IF_ENQUEUE macros. If the queue is */ /* full then the process will sleep on dc->dc_oq. The wakeup will */ /* come from the pi_data routine when it sees a write completion. */ /* After the data is queue the dda_start is called to try to send the */ /* data. */ /* 4) The dda_start routine dequeues the data from the per lcn ouput */ /* data queue and hooks the mbuf onto the write side of the hdx_chan */ /* structure for the lcn. The routine dda_wrq is called passing this */ /* structure. */ /* 5) The routine dda_wrq takes the hc structure and puts it on the */ /* global output queue ds->dda_sioq. It then calls start_chn(). */ /* 6) start_chn() set up the comregs and request an A interrupt. The */ /* A interrupt routine will handle the transfer grant and write */ /* completion. */ /* */ /* INBOUND DATA (FE -> user process): */ /* */ /* A) interrupt side---- */ /* 1) Initially a read is posted for every logical circuit. When */ /* data comes in the interrupt A routine will handle doing the */ /* transfer grant. When the read completes then the type of */ /* channel is determined (IP,X.29 or PI). For PI data the routine */ /* pi_data() is called with the mbuf and read completion status. */ /* 2) In the routine pi_data() if the read completion status is */ /* DDAIOCOK then the the packet is complete so the routine */ /* pi_queue_data is called to queue the data for the user process */ /* to read. If the status is DDAIOCOKP then the packet has not */ /* been completely read. In this case the routine dda_rrq is */ /* called to allocate a new mbuf and chain it to the end of the */ /* mbufs that have already been filled from this packet. When the */ /* packet is exhausted then the whole chain of mbufs will be */ /* passed to pi_queue_data. */ /* 3) The routine pi_queue_data uses a small array of pi_qmem */ /* structures to form a queue of input data. Each of these */ /* structures has a pointer to an mbuf. This may point to a */ /* single mbuf or a chain of mbufs depending on how many mbufs it */ /* took to read the packet. Pi_queue_data will copy the important */ /* information to the pi_qmem structure and then wakeup any */ /* processes that have slept waiting for data to come in. */ /* */ /* B) system call side---- */ /* 1) The user process issues the XIOREAD ioctl with a pi_dblock */ /* structure describing the target buffer. */ /* 2) the piioctl routine using the XIOREAD case calls the routine */ /* pi_rem_qdata to get the next data item. If there are no data */ /* items ready then pi_rem_qdata will return -1 and the process */ /* will sleep waiting for data to come in. The wakeup will be done*/ /* in pi_queue_data. When a data item is dequeued from a channel */ /* that is not the supervisor channel then a new read is issued on */ /* that logical circuit. This means that at most 1 data item will */ /* be present in the input queue for a particular data circuit. */ /* This excludes the supervisory circuit 0. */ /* 3) the pi_rem_qdata routine will dequeue an mbuf and copy the */ /* data to the user buffer along with other information such as */ /* the lcn and read completions statuses. If several mbufs were */ /* used to hold a single packet then the entire chain will be */ /* scanned and all the data placed in the user buffer. */ /*************************************************************************/ #ifdef SIMULATION #include "if_pivar.h" #else #include "../vaxif/if_pivar.h" #endif /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL FUNCTIONS %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void pi_init (); PRIVATE void pi_init_single (); PRIVATE void pi_clear_piq (); PRIVATE void pi_clear_single (); PRIVATE void pi_free_all_lcns (); PRIVATE void pi_queue_data (); PRIVATE int pi_rem_qdata (); PRIVATE int pi_find_chan_for_ring (); PRIVATE int pi_write (); /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL VARIABLES %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* PILINES: number of channels (minor device numbers) allocated per board */ /* PIQLEN: max number of messages that can be queued for a channel */ #define PILINES 32 #define PIQLEN 8 #define NPILINES (NDDA * PILINES) #ifdef DDADEBUG int pi_placemark = 0; #define PI_PLACEMARK(n) (pi_placemark = (n)) #else #define PI_PLACEMARK(n) #endif /* the pi_qmem structure is used to hold essential information about * incomming data until the user process has a chance to issue a read. * These structures form a queue of input data. All fields should be 0 * when the structure is not actively part of the queue. */ struct pi_qmem { struct mbuf *mb; u_char stat; u_char substat; u_char lcn; }; /* the pi_info structure describes a data channel which corresponds to a * minor device. The values in the protocols fields are only valid when * the PI_ACCEPT_RING bit is set in flags. The 0 element is the lowest * protocol, The 1 element is the highest. */ struct pi_info { u_short flags; u_int pgrp; /* process group */ u_int signal; /* signal to use upon data ready */ struct mbuf *msav; /* place to hang mbuf waiting to go out */ struct pi_qmem pi_q[PIQLEN]; /* queue of pending input data */ u_char firstq, lastq; /* index of first and last queue * members */ u_char pi_qlen; /* length of the input queue */ u_char protocols[2]; /* low and high protocol range */ }; /* bits for the flags field */ #define PI_ACCEPT_RING 0x1 #define PI_ACCEPT_ANY 0x2 struct pi_info pi_info[NPILINES]; /* tty structures */ /* this macro gives the channel (minor device number) associated with * an active lcn. */ #define CHANNEL(lcn,ds) ((ds)->dda_cb[(lcn)].dc_line) /* * macro to translate a device number to the unit (i.e. ACP_PI) * with which it is associated, M001 use V7 major and minor macros */ #define PI_UNIT(x) (minor(x) / PILINES) /* ACP_PI controlling this line */ #define PI_LINE(x) (minor(x) % PILINES) /* Line number */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% GLOBAL ROUTINES %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PIIOCTL() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Process ioctl request. */ /* */ /* Call: piioctl(dev, cmd, data, flag) */ /* Argument: dev: device */ /* cmd: ioctl command */ /* data: pointer to data */ /* flag: ignored */ /* Returns: 0 for sucess, else nonzero error code */ /* Called by: kernel software software, this routine is in */ /* the cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ piioctl (dev, cmd, data, flag) dev_t dev; caddr_t data; { struct dda_cb *dc; struct dda_softc *ds; int l, unit, maxlcn; l = PI_LINE (dev); unit = PI_UNIT (dev); maxlcn = nddach[unit]; ds = &dda_softc[unit]; switch (cmd) { case XIOWRITE: { struct pi_dblock *pd; pd = (struct pi_dblock *) data; if (pd->lcn > maxlcn) { DMESG(unit, 66, (DDALOG(LOG_ERR) "XIOWRITE: invalid lcn %d\n", pd->lcn DDAELOG)); return (EINVAL); } if (pd->lcn && (CHANNEL (pd->lcn, ds) != l)) { DMESG (unit, 67, (DDALOG(LOG_ERR) "XIOWRITE: lcn %d not associated with channel %d.\n", pd->lcn, l DDAELOG)); return (EINVAL); } return (pi_write (ds, l, pd)); } case XIOREAD: { int ret; struct pi_dblock *pd; pd = (struct pi_dblock *) data; while (ret = pi_rem_qdata (l, pd)) { if (ret > 0) goto newread; else if (pd->flags & DB_NONBLOCK) return (EWOULDBLOCK); /* read already posted */ else { #ifdef DDA_MSGQ dda_mqstr ("(r sl) "); #endif sleep (pi_info[l].pi_q, TTIPRI); #ifdef DDA_MSGQ dda_mqstr ("(r wo) "); #endif } } newread: #ifdef DDA_MSGQ dda_mqstr ("(r go) "); #endif if (pd->lcn) /* issue new read for the lcn */ dda_rrq (ds, &ds->dda_cb[pd->lcn].dc_rchan); return (ret); } case XIORPEND: { int s; s = splimp (); *((int *) data) = pi_info[l].pi_qlen; splx (s); break; } case XIOACCRING: { struct proto_range *pr; int s; pr = (proto_range *) data; s = splimp (); pi_info[l].flags |= PI_ACCEPT_RING; pi_info[l].protocols[0] = pr->lower; pi_info[l].protocols[1] = pr->upper; splx (s); break; } case XIOANYPROTO: pi_info[l].flags |= PI_ACCEPT_ANY; break; case XIOFREELCN: { u_char lcn; lcn = *((u_char *) data); if (lcn > maxlcn) return EINVAL; dc = &ds->dda_cb[lcn]; if (CHANNEL (lcn, ds) != l) return EINVAL; else { abort_io (unit, lcn); dc->dc_state = LC_IDLE; dc->dc_line = -1; dc->dc_inaddr.s_addr = 0; /* forget address */ dc->dc_key.key_addr.s_addr = 0; dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; dc->dc_flags = 0; } break; } case XIOABORT: { u_char lcn; lcn = *((u_char *) data); if (lcn > maxlcn) return EINVAL; dc = &ds->dda_cb[lcn]; if (CHANNEL (lcn, ds) != l) return EINVAL; else abort_io (ds->dda_if.if_unit, lcn); break; } case XIOGETLCN: /* * find a free lcn. Init the line field for this minor device. stuff * lcn number in struct. init the circuit state. */ if (dc = find_free_lcn (ds)) { dc->dc_flags = DC_RAW; dc->dc_state = LC_DATA_IDLE; dc->dc_line = l; *data = dc->dc_lcn; } else *data = 0; break; case XIOCLRCHAN: pi_clear_piq (ds, &pi_info[l]); break; case XIONORING: pi_info[l].flags &= ~(PI_ACCEPT_ANY | PI_ACCEPT_RING); break; case XIORSIG: pi_info[l].signal = *((int *) data); break; /* case XIOABORTIO: do an abort-io on the channel */ default: return (ENOTTY); } return (0); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PIOPEN() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Open a line. */ /* */ /* Call: piopen(dev, flag) */ /* Argument: dev: device */ /* flag: indicates type of open, "nonblocking" */ /* "or block if in use" */ /* Returns: 0 for success, else nonzero error code */ /* Called by: kernel software software, this routine is in */ /* the cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ piopen (dev, flag) dev_t dev; int flag; { int unit, d; unit = PI_UNIT (dev); d = PI_LINE (dev); if (d >= NPILINES) return (ENXIO); /* wait for interface to come up */ while (dda_softc[unit].dda_state != S_LINK_UP) sleep (&dda_softc[unit].dda_state, TTIPRI); if (pi_info[d].pgrp == 0) pi_info[d].pgrp = u.u_procp->p_pid; return (0); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PICLOSE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Close a line. */ /* */ /* Call: piclose(dev, flag) */ /* Argument: dev: device */ /* flag: unused */ /* Returns: nothing */ /* Called by: kernel software software, this routine is in */ /* the cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ piclose (dev, flag) dev_t dev; int flag; { pi_clear_single (&dda_softc[PI_UNIT (dev)], &pi_info[PI_LINE (dev)], 0); pi_free_all_lcns (PI_UNIT (dev), PI_LINE (dev)); } /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL FUNCTIONS %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_SUPR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine processes received supervisor messages. */ /* Depending on the message type, the appropriate action is */ /* taken. */ /* */ /* Call: pi_supr(ds, mb) */ /* Arguments: ds: pointer to dev control block struct */ /* mb: pointer to mbuf */ /* containing the supervisor message */ /* Returns: nothing */ /* Called by: dda_supr() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void pi_supr (ds, mb) struct dda_softc *ds; struct mbuf *mb; { u_char *p; register int lcn; register int chan; int unit; unit = ds->dda_if.if_unit; #ifdef DDADEBUG if (DDADBCH (21, unit)) DDALOG(LOG_ERR) "dda%d:(pi) pi_supr()\n", unit DDAELOG; #endif DDADEBUG p = mtod (mb, u_char *); switch (p[0]) { case LINE_STATUS: /* link status msg */ case RESTART: /* restart received */ case RSTRT_ACK: /* restart ack */ case STATRESP: /* Statistics Response from FEP */ case CLEARVC: /* clear by VCN */ DMESG (unit, 64, (DDALOG(LOG_ERR) "dda%d:(pi) pi_supr: unexpected msg type 0x%x\n", unit, p[0] DDAELOG)); break; case RING: /* incoming call */ if ((chan = pi_find_chan_for_ring (p)) >= 0) pi_queue_data (unit, chan, 0, 0, 0, mb); else /* if no willing channel's */ { /* reject the call */ send_supr (ds, CLEARVC, p[2], 0); /* clear call */ if (LOG_CALLS) DDALOG(LOG_ERR) "dda%d:(pi) Call REJECTED VC 0x%x\n", unit, p[1] DDAELOG; } break; case ANSWER: /* call answered */ case CLEARLC: /* clear by LCN */ case RESET: /* X25 reset */ case INTERRUPT: /* X25 interrupt */ case INTR_ACK: lcn = p[1] / 2; /* get LCN */ pi_queue_data (unit, CHANNEL(lcn, ds), 0, 0, 0, mb); break; default: DMESG(unit, 65, (DDALOG(LOG_ERR) "dda%d:(pi) supervisor error (%x %x %x %x)\n", unit, p[0], p[1], p[2], p[3] DDAELOG)); } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_DATA() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine is called when a data channel I/O completes. */ /* If the completion was for a write, an attempt is made to */ /* start output on the next packet waiting for output on that */ /* LCN. If the completion was for a read, the received packet */ /* is sent to the pi input queue (if no error). */ /* */ /* Call: pi_data(ds, hc, cc, cnt, subcc) */ /* Argument: ds: device control block */ /* hc: half duplex channel control block */ /* cc: Mailbox I/O completion status */ /* cnt: byte count */ /* subcc: Mailbox I/O completion substatus */ /* Returns: nothing */ /* Called by: ddainta() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void pi_data (ds, hc, cc, cnt, subcc) register struct dda_softc *ds; register struct hdx_chan *hc; int cc, cnt, subcc; { register struct dda_cb *dc = &(ds->dda_cb[hc->hc_chan / 2]); int unit; unit = ds->dda_if.if_unit; #ifdef DDADEBUG if (DDADBCH (18, unit)) DDALOG(LOG_ERR) "dda%d:(pi) pi_data: chan=0x%x cc=0x%x cnt=0x%x subcc=0x%x\n", unit, hc->hc_chan, cc, cnt, subcc DDAELOG; #endif DDADEBUG #ifdef DDA_MSGQ dda_mqstr("(dt"); dda_mqnum(hc->hc_chan,MQHEX); dda_mqstr(" "); dda_mqnum(cc,MQHEX); dda_mqstr(" "); dda_mqnum(cnt,MQHEX); dda_mqstr(" "); dda_mqstr(")"); #endif if (hc->hc_chan & 0x01) /* was it read or write? */ { /* write, fire up next output */ #ifdef DDADEBUG dc->dc_out_t = TMO_OFF; /* turn off output completion timer */ #endif if ((hc->hc_func != DDAABT) && (hc->hc_curr = hc->hc_curr->m_next)) dda_wrq (ds, hc, 0); else { m_freem (hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; if (hc->hc_func == DDAABT) { hc->hc_func &= ~DDAABT; hc->hc_sbfc &= ~INVALID_MBUF; } else ds->dda_if.if_opackets++; dc->dc_flags &= ~DC_OBUSY; #ifdef DDA_MSGQ dda_mqstr ("(w wa) "); #endif wakeup (&(dc->dc_oq)); /* wake up anyone sleeping on output */ dda_start (ds, dc); /* and try to output */ dc->dc_timer = TMO_OFF; /* reset timer */ } } else /* read, process rcvd packet */ { #ifdef DDADEBUG #ifdef notdef if (DDADBCH (19, unit)) { u_char *p; DDALOG(LOG_ERR) "dda%d:(pi) ", unit DDAELOG; p = mtod (hc->hc_curr, u_char *); prt_bytes (p, (cnt < 56 ? cnt : 56)); DDALOG(LOG_ERR) "\n" DDAELOG; } #endif #endif DDADEBUG if (cc == DDAIOCOK) { /* Queue good packet for input */ hc->hc_curr->m_len += cnt; /* update byte count */ ds->dda_if.if_ipackets++; pi_queue_data (unit, dc->dc_line, dc->dc_lcn, cc, subcc, hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } else if (cc == DDAIOCOKP) /* more data pending in this packet */ { hc->hc_curr->m_len += cnt; /* update byte count */ dda_rrq (ds, hc); } else { m_freem (hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; dda_rrq (ds, hc); } /* don't hang new data read. This is done in the user read ioctl */ } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_INIT() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* This function initializes the pi_info structure. The */ /* active flag is non-zero if the interface is active and got */ /* a reset. In this case pi_clear_single is called which */ /* free any resources held and send a hangup to the */ /* controlling process. */ /* */ /* Call: pi_init(unit, active) */ /* Argument: unit: unit to be initialized. */ /* active: non-zero if the interface is active */ /* when the reset is issued. */ /* Returns: nothing. */ /* Called by: ddareset(), ddainit(), bufreset(). */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void pi_init (unit, active) int unit; int active; { struct pi_info *pi, *end; struct dda_softc *ds; ds = &dda_softc[unit]; end = pi_info + ((unit + 1) * PILINES); for (pi = end - PILINES; pi < end; pi++) if (active) pi_clear_single (ds, pi, 1); else pi_init_single (pi); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_INIT_SINGLE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* Initialize a pi_info structure. For now all this means is */ /* zeroing out everything. This routine is here to make future*/ /* enhancements easier. */ /* */ /* Call: pi_init_single (pi) */ /* Argument: pi: pointer to pi_info structure */ /* Returns: nothing. */ /* Called by: pi_init(), pi_clear_single(). */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void pi_init_single (pi) struct pi_info *pi; { bzero ((char *) pi, sizeof (struct pi_info)); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_CLEAR_PIQ() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* This routine will free up all the mbufs that are queued on */ /* the pi_q. Note that this routine assumes that any pi_q */ /* element is zeroed when not in use. */ /* */ /* IMPORTANT: this routine reissues reads for all the lcn */ /* whose data is removed from the queue. This parallels what */ /* is done when the data is dequeue normally by XIOREAD */ /* */ /* Call: pi_clear_piq (ds, pi) */ /* Argument: ds: pointer to softc used to call dda_rrq. */ /* pi: pointer to pi_info structure. */ /* Returns: nothing */ /* Called by: pi_clear_single(), piioctl() case XIOCLRCHAN */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void pi_clear_piq (ds, pi) struct dda_softc *ds; struct pi_info *pi; { register int i; for (i = 0; i < PIQLEN; i++) if (pi->pi_q[i].mb) { m_freem (pi->pi_q[i].mb); if (pi->pi_q[i].lcn)/* issue new read for the lcn */ dda_rrq (ds, &ds->dda_cb[pi->pi_q[i].lcn].dc_rchan); } bzero ((char *) pi->pi_q, sizeof (pi->pi_q)); pi->pi_qlen = 0; } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_CLEAR_SINGLE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* this routine will re-initialize a single pi_info structure.*/ /* If hangup is non-zero then a hangup signal will be sent to */ /* the controlling process. */ /* */ /* Call: pi_clear_single (ds, pi, hangup) */ /* Argument: ds: pointer to dda_softc structure */ /* pi: pointer to pi_info structure */ /* hangup: non-zero if hangup is to be sent. */ /* Returns: nothing. */ /* Called by: piclose(), pi_init(). */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* reset a pi_info structure that is active. This includes freeing any * queue mbufs and sending a hangup to the controlling process */ PRIVATE void pi_clear_single (ds, pi, hangup) struct dda_softc *ds; struct pi_info *pi; int hangup; /* non-zero if a hangup is to be sent to * controlling process */ { pi_clear_piq (ds, pi); if (hangup && pi->pgrp) { gsignal (pi->pgrp, SIGHUP); gsignal (pi->pgrp, SIGCONT); } if (pi->msav) m_freem (pi->msav); pi_init_single (pi); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_FREE_ALL_LCNS () %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* this routine will free all logical circuits associated with*/ /* a particular channel. This should return all mbufs that */ /* Are used by the channel. */ /* */ /* Call: pi_free_all_lcns (unit,chan) */ /* Argument: chan: channel number. */ /* unit: unit number. */ /* Returns: nothing. */ /* Called by: piclose() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void pi_free_all_lcns (unit, chan) int unit; int chan; { register int i, maxlcn; struct dda_cb *dc; maxlcn = nddach[unit]; dc = &dda_softc[unit].dda_cb[1]; for (i = 1; i <= maxlcn; i++, dc++) if ((dc->dc_flags & DC_RAW) && dc->dc_line == chan) { abort_io (unit, dc->dc_lcn); dc->dc_state = LC_IDLE; dc->dc_line = -1; dc->dc_inaddr.s_addr = 0; /* forget address */ dc->dc_key.key_addr.s_addr = 0; dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; dc->dc_flags = 0; } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_QUEUE_DATA() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* This routine queue up supervisory and data messages. This */ /* messages will be dequeued when the user does a read using */ /* the XIOREAD ioctl. If the user tried to do a read and no */ /* data was ready then the process sleeps on pi_info[chan]pi_q*/ /* This routine wakes up any of the sleeping processes. */ /* */ /* Call: pi_queue_data( unit, chan, lcn, cc, subcc, mb) */ /* Argument: unit: unit number (board) */ /* chan: channel for the data to be queue on */ /* lcn: the lcn that the data came in on */ /* cc: the read completion status from the mailbox*/ /* subcc: the read completion substatus */ /* mb: pointer to the mbuf (or mbuf chain) that */ /* contains the data packet. */ /* Returns: 0 for success, else nonzero error code */ /* Called by: pi_data(), pi_supr(). */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void pi_queue_data (unit, chan, lcn, cc, subcc, mb) int unit; int chan; int lcn; int cc; int subcc; struct mbuf *mb; { int s; struct pi_info *pi; struct pi_qmem *pq; if (chan == -1) { /* invalid channel */ DMESG (unit, 68, (DDALOG(LOG_ERR) "dda%d:(pi) data queue on dead channel: lcn %d\n", unit, lcn DDAELOG)); return; /* don't do it, otherwise panic! */ } pi = &pi_info[chan]; s = splimp (); if (pi->pi_qlen == PIQLEN) /* overflow */ { DMESG(unit, 68, (DDALOG(LOG_ERR) "dda%d:(pi) Data queue full, message dropped chan %d lcn %d\n", unit, chan, lcn DDAELOG)); m_freem (mb); splx (s); return; } else pi->pi_qlen++; if (pi->lastq == (PIQLEN - 1)) pi->lastq = 0; else pi->lastq++; pq = &pi->pi_q[pi->lastq]; pq->mb = mb; pq->lcn = lcn; pq->stat = cc; pq->substat = subcc; splx (s); #ifdef DDA_MSGQ dda_mqstr ("(r wa) "); #endif wakeup (pi->pi_q); /* wakeup any sleepers */ if (pi->signal) gsignal (pi->pgrp, pi->signal); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_REM_QDATA() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* Remove a data item from the input queue and copy the */ /* information to the user pi_dblock structure that was */ /* passed to the XIOREAD case in piioctl. */ /* */ /* Call: pi_rem_qdata(chan, dblk) */ /* Argument: chan: index of the channel to read. */ /* dblk: pointer to user pi_dblock. */ /* Returns: -1 if the input queue is empty. */ /* 0 if pi_dblock was filled in successfully. */ /* error code if there was an error filling in */ /* the pi_dblock. */ /* Called by: piioctl() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE int pi_rem_qdata (chan, dblk) int chan; struct pi_dblock *dblk; { int s, cnt = 0; struct pi_info *pi; struct pi_qmem *pq; struct mbuf *mb, *m; caddr_t usrdata; int error = 0; pi = &pi_info[chan]; s = splimp (); if (pi->pi_qlen == 0) return (-1); else pi->pi_qlen--; if (pi->firstq == (PIQLEN - 1)) pi->firstq = 0; else pi->firstq++; pq = &pi->pi_q[pi->firstq]; mb = pq->mb; dblk->func = pq->stat; dblk->subfunc = pq->substat; dblk->lcn = pq->lcn; bzero ((char *) pq, sizeof (*pq)); /* zero the structure out */ splx (s); for (m = mb, usrdata = dblk->dataptr; m; m = m->m_next) { if (dblk->length < cnt + m->m_len) { error = EINVAL; goto out; } if (copyout (mtod (m, char *), usrdata, m->m_len)) { error = EFAULT; goto out; } cnt += m->m_len; usrdata += m->m_len; } out: dblk->length = cnt; m_freem (mb); return (error); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_FIND_CHAN_FOR_RING() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* This routine picks a channel to handle a ring. It uses */ /* a static int (lastchan) to remember where it left off */ /* during the last search. This will prevent it from always */ /* picking the same channel if there is more than 1 channel */ /* willing to accept the call. If no channel is willing to */ /* accept the protocol type then it will go to a channel that */ /* has indicated that it will accept any protocol type. */ /* Note that the ring will not be given to a channel that */ /* has a full input queue. */ /* This routine should be called a interrupt level. */ /* */ /* Call: pi_find_chan_for_ring (p) */ /* Argument: p: pointer to data containg the ring packet. */ /* Returns: index of a channel willing to accept the call. */ /* -1 is return if no channel can accept */ /* Called by: pi_supr() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE int pi_find_chan_for_ring (p) u_char p[]; /* p is a pointer to a ring packet */ { register u_char *cp; int i, proto, anychan = -1; static int lastchan = 0; struct pi_info *pi; cp = p + 4; /* skip over code, lcn, vcn and count in * answer message */ /* cp now points to length of called address */ cp += *cp + 1; /* skip over called address and length byte */ /* cp now points to length of calling address */ cp += *cp + 1; /* skip over calling address and length byte */ /* cp now points to length of protocol */ if (*cp++ == 0) return (-1); proto = *cp; i = lastchan; do { i = (i + 1) % NPILINES; pi = &pi_info[i]; if (pi->flags & PI_ACCEPT_RING) { if (proto >= pi->protocols[0] && proto <= pi->protocols[1] && pi->pi_qlen < PIQLEN) { lastchan = i; return (i); } } if ((pi->flags & PI_ACCEPT_ANY) && anychan == -1 && pi->pi_qlen < PIQLEN) anychan = i; } while (i != lastchan); if (anychan >= 0) lastchan = anychan; return (anychan); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_CIRCUIT_TO_HANDLE_PROTOCOL() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* This routine find out if there is a pi channel that is */ /* willing to accept a particular protocol. This is needed */ /* because the PI interface has precedence over IP and X.29 */ /* interfaces. This routine is called before the data is */ /* passed to one of the interfaces. */ /* */ /* Call: pi_circuit_to_handle_protocol(proto) */ /* Argument: proto: protocol byte to check. */ /* Returns: 1 if PI interface will handle the call. */ /* 0 otherwise. */ /* Called by: dda_decode_type(). */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE int pi_circuit_to_handle_protocol (proto) u_char proto; { register int i; struct pi_info *pi; for (i = 0, pi = pi_info; i < NPILINES; i++, pi++) { if (pi->flags & PI_ACCEPT_RING) if (proto >= pi->protocols[0] && proto <= pi->protocols[1]) return (1); } return (0); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% PI_WRITE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* This routine copies user data to an mbuf and queue the */ /* mbuf to go out. The process will sleep if the output */ /* queue is full. If the non-block bit is set then the */ /* process will not sleep and EWOULDBLOCK is returned. */ /* The last byte of the built in data area for the mbuf */ /* is used to store the subfunction byte. This byte is then */ /* put in the comregs by start_chn when it sees that this is */ /* a write for a RAW circuit. */ /* */ /* Call: pi_write (ds, l, dblk) */ /* Argument: ds : pointer to dda_softc structure */ /* l : the channel (minor device) number */ /* dblk : pointer to the pi_dblock for user data */ /* Returns: 0 for success, else nonzero error code */ /* Called by: piioctl. */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE int pi_write (ds, l, dblk) struct dda_softc *ds; int l; struct pi_dblock *dblk; { register int length; struct dda_cb *dc; struct mbuf *m; char *cp; struct ifqueue *oq; int s, unit; length = dblk->length; unit = ds->dda_if.if_unit; if (length <= 0 || length > CLBYTES) { DMESG(unit, 69, (DDALOG(LOG_ERR) "XIOWRITE: invalid length %d.\n", length DDAELOG)); return (EINVAL); } m = 0; MGET (m, M_WAIT, MT_DATA); if (m == 0) { DMESG(unit, 70, (DDALOG(LOG_ERR) "XIOWRITE: could not get small mbuf\n" DDAELOG)); return (ENOBUFS); } if (length > (MLEN - 1)) { #if ACC_ULTRIX > 12 struct mbuf *p; MCLGET (m, p); if (p == 0) #else MCLGET (m); if (m->m_len != CLBYTES) #endif { DMESG(unit, 71, (DDALOG(LOG_ERR) "XIOWRITE: could not get page cluster\n" DDAELOG)); m_freem (m); return (ENOBUFS); } } cp = mtod (m, char *); if (copyin (dblk->dataptr, cp, length)) { m_freem (m); return (EFAULT); } m->m_len = length; if (length < (MLEN - 1)) m->m_dat[MLEN-1] = dblk->subfunc; /* store subfunction in last byte */ dc = &ds->dda_cb[dblk->lcn]; oq = &(dc->dc_oq); /* point to output queue */ s = splimp (); while (IF_QFULL (oq)) /* if q full */ { if (dblk->flags & DB_NONBLOCK) { m_freem (m); splx (s); return (EWOULDBLOCK); } else { /* * note that we save the mbuf here. It may happen that the * process dies in its sleep and we need a way to recover the * allocated mbuf */ pi_info[l].msav = m; #ifdef DDA_MSGQ dda_mqstr ("(w sl) "); #endif sleep (&(dc->dc_oq), TTIPRI); #ifdef DDA_MSGQ dda_mqstr ("(w wo) "); #endif } } #ifdef DDA_MSGQ dda_mqstr ("(w go) "); #endif IF_ENQUEUE (oq, m); /* otherwise queue it */ pi_info[l].msav = (struct mbuf *) 0; dda_start (ds, dc); /* and try to output */ dc->dc_timer = TMO_OFF; /* reset timer */ splx (s); return (0); } /* Revision History: 13-Jul-89 PST Modified LOCAL_VOIDs and statics to PRIVATE. 18-Jul-89 PST Moved dc_key.ttyline out of union, creating dc_line. 26-Jul-89 PST Replaced kernel printfs with DDALOG calls. 16-Aug-89 PST Fixed DMESG macros. 29-Oct-89 PST Fixed off-by-one errors where user could allocate but not use last LCN. (Fix from brad@invector) */ t pi_info *pi; for (i = 0, pi = pi_info; i < NPILINES; i++, pi++) { if (pi->flags & PI_ACCEPT_RING) if (proto >= pi->protocols[0] && proto <= pi->protocdriver/if_pivar.h 444 540 24 12411 4535335134 7371 /*************************************************************************/ /* */ /* */ /* ________________________________________________________ */ /* / \ */ /* | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* \________________________________________________________/ */ /* */ /* Copyright (c) 1988 by Advanced Computer Communications */ /* 720 Santa Barbara Street, Santa Barbara, California 93101 */ /* (805) 963-9431 */ /* */ /* */ /* File: if_pivar.h */ /* */ /* Author: Brad Engstrom. */ /* */ /* Project: UNIX DDA-X.25 Network Programmers */ /* Interface for ACP 5250 and ACP 6250 */ /* */ /* Function: This file contains definitions of the PI */ /* data structures and ioctl indicators. */ /* */ /* Revision History: */ /* */ /* 06-Sep-1988: V1.0 - Brad Engstrom */ /* First generated */ /* */ /*************************************************************************/ /* * Ioctl's have the command encoded in the lower word, * and the size of any in or out parameters in the upper * word. The high 2 bits of the upper word are used * to encode the in/out status of the parameter; for now * we restrict parameters to at most 128 bytes. */ /* this structure is used when writing or reading data data */ struct pi_dblock { caddr_t dataptr; /* pointer to user data to be written */ u_int length; /* length of data pointed to by dataptr */ u_short lcn; /* logical channel to send data out on */ u_char func; /* read status , write function value */ u_char subfunc; /* read substatus, write subfunction value */ u_short flags; /* special flags */ }; /* pi_dblock.flags: The follwing values can be used to set bits */ #define DB_NONBLOCK 0x01 /* non-blocking read or write */ typedef struct proto_range { u_char lower; /* lower bound on protocol range (inclusive) */ u_char upper; /* upper bound on protocol range (inclusive) */ } proto_range; /* data write */ #define XIOWRITE _IOWR(t, 1, struct pi_dblock) /* data read */ #define XIOREAD _IOWR(t, 2, struct pi_dblock) /* see if data is ready to be read on a particular channel. * The field will return the number of pending data bytes. */ #define XIORPEND _IOR(t, 3, int) /* allow rings on this minor device. The proto_range structure specifies * the lower and upper bounds on the protocol byte of incomming calls */ #define XIOACCRING _IOW(t, 4, proto_range) /* specify that a protocol not requested by anyone else will be accepted on * this channel. */ #define XIOANYPROTO _IO(t, 5) /* reserve an lcn for use by this channel (minor device). The number of the * lcn is returned the the character passed */ #define XIOGETLCN _IOR(t, 6, u_char) /* free an lcn specified by the u_char parameter. This should be called for all * lcn's obtained with XIOGETLCN before the channel is closed. */ #define XIOFREELCN _IOW(t, 11, u_char) /* clear any data associated with a channel. The lcn is passed. Note that * this does not clear the circut. It only flushes queued data stored in the * driver */ #define XIOCLRCHAN _IO(t, 7) /* disallow incomming calls on this channel */ #define XIONORING _IO(t, 9) /* specify a signal to be sent to the process when there is data ready to * be read on a particular channel. The u_int specifies the signal to be * sent. */ #define XIORSIG _IOW(t, 10, u_int) /* abort all output for the given lcn. */ #define XIOABORT _IOW(t, 12, u_char) /* Revision history: 06-Jun-1988 Brad Initial implementation */ */ /* Revision History: */ /* */ /* 06-Sep-1988: V1.0 - Brad Engstrom */ /driver/if_x29.c 444 540 24 177517 4535335133 6727 /* * * X.29 option for dda driver for UNIX and Ultrix * ________________________________________________________ * / \ * | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | * | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | * | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | * | AAAA AAAA CCCC CCCC | * | AAAA AAAA CCCC CCCC | * | AAAA AAAA CCCC CCCC | * | AAAA AAAA CCCC CCCC | * | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | * | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | * | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | * \________________________________________________________/ * * Copyright (c) 1987 by Advanced Computer Communications * 720 Santa Barbara Street, Santa Barbara, California 93101 * (805) 963-9431 * * File: * if_x29.c * * Author: * * Project: * Development of PAD on 6250 software. * * Function: * To enable network connections on ACP_XX to communicate with UNIX. * * Components: * - files if_x29.c * * Configuration Entry: * * device dda0 at uba? csr 0166740 vector ddainta ddaintb * * Usage Notes: * * - make devices in /dev and edit /etc/ttys for those x29 * devices which you want in your configuration * * System Notes: * * Refer to the installation instructions, readme.txt, which * are included on the driver distribution medium. * * Revision History at end of file */ /* * For efficiency, it is a good idea to modify XXBOARDS when using * less than 4 boards with the X29 option. If using more than 32 * lines per board, you should modify XXBOARDS, XXLPERBRD, LOG2_XXBOARDS * and LOG2_XXLPERBRD. * * Minor numbers are laid out as follows (by default): * (MSB) PBBLLLLL (LSB) * Where P is a flag to determine if the line is outbound (pad) or * inbound (tty). BB is the board number (0-3), and LLLLL is the * X29 line on a board (0-31). Some customers may need more than * 32 lines/board. If there are less than 2 boards, one may shift * the break-point between lines and boards: * * up to 4 boards, 32 lines/board (default) * (MSB) PBBLLLLL (LSB) * XXBOARDS = 4, LOG2_XXBOARDS = 2 * XXLPERBRD = 32, LOG2_XXLPERBRD = 5 * up to 2 boards, 64 lines/board: * (MSB) PBLLLLLL (LSB) * XXBOARDS = 2, LOG2_XXBOARDS = 1 * XXLPERBRD = 64, LOG2_XXLPERBRD = 6 * only 1 board, 128 (actually, 126, as 126 = max svc): * (MSB) PLLLLLLL (LSB) * XXBOARDS = 1, LOG2_XXBOARDS = 0 * XXLPERBRD = 128, LOG2_XXLPERBRD = 7 * * (obviously, these are all powers of two) */ #define XXBOARDS 4 /* # boards running x29 */ #define LOG2_XXBOARDS 2 /* # bits of board info */ #define XXLPERBRD 32 /* # lines per board */ #define LOG2_XXLPERBRD 5 /* # bits of line info */ /* * If you require an 8-bit data path and have no parity misconfigurations, * you may change PARITY_MASKs to 0377. This will leave parity stripping * to the ttdriver. However, the ttdriver won't strip parity when in * raw mode (e.g. at the Password: prompt), so one symptom of a parity * misconfiguration is that users can't login (CR gets received as 0x8D). */ #define INPUT_PARITY_MASK 0177 /* strip off the 8th bit */ #define OUTPUT_PARITY_MASK 0377 /* don't strip off the 8th bit */ /* * macro to translate a device number to the unit (i.e. ACP_n250) * with which it is associated and the port on said unit */ #define UNIT(x) ((minor(x) >> LOG2_XXLPERBRD) & LOG2_XXBOARDS) #define LINE(x) (minor(x) & 0177) /* index into line table */ #define XXSHOW(x) (minor(x) == 255) /* special "show" device */ #define IS_PAD(x) (minor(x) & 0200) /* msb is the pad/tty selector */ #define MAJLINE(x) ((x) & ~0x80) /* major plus corrected minor # */ #define NXXLINES (XXBOARDS * XXLPERBRD) /* number of total x29 lines */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL FUNCTIONS %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void xxcntl(); PRIVATE void xxclear(); PRIVATE void xxshow(); PRIVATE void xxpadhandle(); PRIVATE int xxpadparse(); PRIVATE int xxpadcall(); PRIVATE void xxpadmsg(); PRIVATE void xx_qbit_msg(); PRIVATE void xx_tp_hangup(); PRIVATE void x29_init(); PRIVATE void x29_dhandle(); PRIVATE int x29_break_reply_is_required(); #if ACC_ULTRIX >= 30 static int ttbreakc(); /* always keep this private */ #endif /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL VARIABLES %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #define SET_PAD 2 #define READ_PAD 4 #define SET_READ_PAD 6 #define PAR_INDICATION 0 #define INVITE_CLEAR 1 #define BREAK_INDIC 3 #define PAD_ERROR 5 /* command codes */ #define XX_C_BREAK 001 #define XX_C_PAD 002 #define XX_C_CLOSE 003 #define XX_C_HOST 004 struct tty xx_tty[NXXLINES]; /* tty structures */ #define MODE_UNUSED 0 /* !just for sanity checks only! */ #define MODE_HOST 1 /* port in host mode (incoming) */ #define MODE_PAD 2 /* port in pad mode (outgoing) */ char xxmode[NXXLINES]; /* mode of port */ int xxstart(); typedef struct { char ref; char val; } x29_pad_pair; PRIVATE x29_pad_pair x29_break_ack_params[] = { 8, 0 /* ref 8 -- normal output to terminal */ }; PRIVATE x29_pad_pair x29_callout_params[] = { 1, 0 /* ref 1 -- no recall char */ }; PRIVATE x29_pad_pair x29_callin_setparams[] = { /* these are the preferred paramters when calling in to Unix */ 2, 0, /* ref 2 -- no echo */ 3, 127, /* ref 3 -- forward data on any char */ 8, 0, /* ref 8 -- normal data delivery to terminal */ 9, 0, /* ref 9 -- no padding after carriage return */ 10, 0, /* ref 10 -- no line folding */ 13, 0, /* ref 13 -- no line feed after CR */ 15, 0 /* ref 15 -- no local edit */ }; /****************************************************************************** * PAD CONTROL INFORMATION AND DEFINITIONS ******************************************************************************/ /* definitions for the pad state field p_state */ #define PS_IDLE 0 /* not opened state */ #define PS_COM 1 /* the pad for this line is in command state */ #define PS_PAD 2 /* this line has data passing though the pad */ #define PS_WAIT 3 /* waiting state */ #define PS_XFR 4 /* data transfer state */ #define P_LINELEN 20 #define P_NOBLOCK 0 typedef struct padinfo { short p_state; /* pad state */ char p_line[P_LINELEN]; /* built up line */ char p_idx; /* index into p_line */ int p_flow; /* index into mbuf when flow off, P_NOBLOCK if not flowed off */ struct mbuf *p_msav; /* place to hang mbuf when flow controlled */ struct mbuf *p_mchsav; /* place to save mbuf chain '' '' '' */ } padinfo; padinfo xx_padinfo[NXXLINES]; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% GLOBAL ROUTINES %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXOPEN() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Open a line. */ /* */ /* Call: xxopen(dev, flag) */ /* Argument: dev: device */ /* flag: indicates type of open, "nonblocking" */ /* "or block if in use" */ /* Returns: 0 for success, else nonzero error code */ /* Called by: kernel software software, this routine is in */ /* the cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*ARGSUSED*/ xxopen(dev, flag) dev_t dev; int flag; { register struct tty *tp; register d; register s; int unit, i; #if ACC_ULTRIX > 00 int inuse; /* store inuse bit while sleeping */ #endif unit = UNIT(dev); d = LINE(dev); if (XXSHOW(dev)) { /* minor device 255 */ xxshow(); return (EPIPE); } /* PST NOTE TO SELF: change the test as follows: * make this d >= NXXLINES, then check to see if unit is present, * Keep that sleep() in the thingy below, so we don't get bouncing * gettys eating up cpu time. */ if ((d >= NXXLINES)) return (ENXIO); /* wait for interface to come up */ while (dda_softc[unit].dda_state != S_LINK_UP) sleep(&dda_softc[unit].dda_state, TTIPRI); tp = &xx_tty[d]; if ((tp->t_state & TS_XCLUDE) && u.u_uid != 0) return EBUSY; /* make sure the port isn't already open in a conflicting manner */ /* i.e. can't open /dev/padJ0 and /dev/ttyJ0 at the same time */ if (tp->t_state & (TS_WOPEN | TS_ISOPEN)) { if ((IS_PAD(dev) && (xxmode[d] == MODE_HOST)) || ((!IS_PAD(dev)) && (xxmode[d] == MODE_PAD))) return EBUSY; } #ifdef DDADEBUG if (DDADBCH(96, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) open line %d flag %o in %s mode\n", unit, d, flag, (IS_PAD(dev) ? "pad" : "host") DDAELOG; } #endif DDADEBUG tp->t_oproc = xxstart; tp->t_state |= TS_WOPEN; /* if first open initialize state */ if ((tp->t_state & TS_ISOPEN) == 0) { ttychars(tp); #if ACC_ULTRIX >= 30 /* posix compliant tty driver */ if (tp->t_cflag & CBAUD == 0) { tp->t_iflag = IGNPAR | ICRNL | IXON | IXANY | IXOFF; tp->t_oflag = OPOST | ONLCR; tp->t_cflag = B9600 | CS8 | CREAD | HUPCL; tp->t_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL; tp->t_line = 0; } #else /* v7 tty driver */ if (tp->t_ispeed == 0) { tp->t_ispeed = B9600; tp->t_ospeed = B9600; tp->t_flags = CRMOD | ANYP; } #endif xxparam(dev); } if (IS_PAD(dev)) { tp->t_state |= TS_CARR_ON; xxmode[d] = MODE_PAD; xxcntl(tp, XX_C_PAD, unit); } else { if ((tp->t_state & TS_CARR_ON) == 0) { xxmode[d] = MODE_HOST; xxcntl(tp, XX_C_HOST, unit); tp->t_flags |= ECHO; #if ACC_ULTRIX < 31 /* on everything other than Ultrix 3.1 */ /* on close tell ACP_XX to drop line */ tp->t_state |= TS_HUPCLS; #endif } } /* if xxcntl did not get called (state had carrier off) or xxcntl's * search for a free lcn failed, then t_addr will be 0, so punt */ if (tp->t_addr == 0) { tp->t_pgrp = 0; tp->t_state = 0; xxmode[d] = MODE_UNUSED; return (EBUSY); } xx_padinfo[d].p_flow = P_NOBLOCK; s = splimp(); #if ACC_ULTRIX > 00 if (flag & O_NDELAY) { if (!IS_PAD(dev)) tp->t_state |= TS_ONDELAY; } else #endif while ((tp->t_state & TS_CARR_ON) == 0) { tp->t_state |= TS_WOPEN; #if ACC_ULTRIX > 00 inuse = tp->t_state & TS_INUSE; #endif sleep(&tp->t_rawq, TTIPRI); /* wakeup came from xxclear */ if ((tp->t_state & TS_WOPEN) == 0) { splx(s); return (EPIPE); } #if ACC_ULTRIX > 00 /* if port became "inuse" while we slept, return */ if ((flag & O_BLKINUSE) && (!inuse) && (tp->t_state & TS_INUSE)) { splx(s); return (EALREADY); } #endif } splx(s); i = ((*linesw[tp->t_line].l_open) (dev, tp)); if (tp->t_pgrp == 0) tp->t_pgrp = u.u_procp->p_pid; return (i); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXCLOSE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Close a line. */ /* */ /* Call: xxclose(dev, flag) */ /* Argument: dev: device */ /* flag: unused */ /* Returns: nothing */ /* Called by: kernel software, this routine is in the */ /* cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*ARGSUSED*/ xxclose(dev, flag) dev_t dev; int flag; { register struct tty *tp; register d; d = LINE(dev); tp = &xx_tty[d]; #ifdef DDADEBUG if (DDADBCH(97, UNIT(dev))) { DDALOG(LOG_DEBUG) "dda%d:(x29) closing line %d\n", UNIT(dev), d DDAELOG; } #endif DDADEBUG /* PST NOTE TO SELF: * Add the 629 driver code for timing out the close below, * because the line could be flowed off and it would hang * forever */ (*linesw[tp->t_line].l_close) (tp); #if ACC_ULTRIX >= 31 if ((tp->t_cflag & HUPCL) || ((tp->t_state & TS_ISOPEN) == 0)) { #else if ((tp->t_state & TS_HUPCLS) || ((tp->t_state & TS_ISOPEN) == 0)) { #endif #ifdef DDADEBUG if (DDADBCH(97, UNIT(dev))) { DDALOG(LOG_DEBUG) "dda%d:(x29) close: tp->t_state = %x\n", UNIT(dev), tp->t_state DDAELOG; } #endif DDADEBUG if (tp->t_state & TS_CARR_ON) xxcntl(tp, XX_C_CLOSE, UNIT(dev)); tp->t_state &= ~TS_CARR_ON; xxmode[d] = MODE_UNUSED; } ttyclose(tp); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXREAD() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Read from a line. */ /* */ /* Call: xxread(dev, uio) */ /* Argument: dev: device */ /* uio: pointer to uio structure */ /* Returns: 0 for success, else nonzero error code */ /* Called by: kernel software, this routine is in */ /* the cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ xxread(dev, uio) dev_t dev; struct uio *uio; { register struct tty *tp; register int l, error; if (dda_softc[UNIT(dev)].dda_state != S_LINK_UP) return (ENXIO); l = LINE(dev); tp = &xx_tty[l]; error = (*linesw[tp->t_line].l_read)(tp, uio); if (xx_padinfo[l].p_flow != P_NOBLOCK) { /* currently blocked? */ if (tp->t_flags & (RAW | CBREAK)) { /* using raw q? */ if (tp->t_rawq.c_cc < TTYHOG / 8) { /* if rawq is low, then * it's time to unblock */ x29_dhandle(&dda_softc[UNIT(dev)], (struct dda_cb *) (tp->t_addr), 1); } /* else cooked mode, different test */ /* canonical q empty? then it's time to unblock */ } else if (tp->t_canq.c_cc == 0) { x29_dhandle(&dda_softc[UNIT(dev)], (struct dda_cb *) (tp->t_addr), 1); } } return (error); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXWRITE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Write on a line. */ /* */ /* Call: xxwrite(dev, uio) */ /* Argument: dev: device */ /* uio: pointer to uio structure */ /* Returns: 0 for success, else nonzero error code */ /* Called by: kernel software software, this routine is in */ /* the cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ xxwrite(dev, uio) dev_t dev; struct uio *uio; { register struct tty *tp; if (dda_softc[UNIT(dev)].dda_state != S_LINK_UP) return (ENXIO); tp = &xx_tty[LINE(dev)]; return (*linesw[tp->t_line].l_write)(tp, uio); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXIOCTL() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Process ioctl request. */ /* */ /* Call: xxioctl(dev, cmd, data, flag) */ /* Argument: dev: device */ /* cmd: ioctl command */ /* data: pointer to data */ /* flag: ignored */ /* Returns: 0 for sucess, else nonzero error code */ /* Called by: kernel software software, this routine is in */ /* the cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #define TIOACCQBIT (int)(0x80800000|('t'<<8)|125) xxioctl(dev, cmd, data, flag) dev_t dev; caddr_t data; { register struct tty *tp; int error; tp = &xx_tty[LINE(dev)]; if (cmd == TIOACCQBIT) { #ifdef DDADEBUG if (DDADBCH(98, UNIT(dev))) { DDALOG(LOG_DEBUG) "dda%d:(x29) ioctl qbit msg: cmd=%x ACC=%x\n", UNIT(dev), cmd, TIOACCQBIT DDAELOG; } #endif DDADEBUG xx_qbit_msg(tp, UNIT(dev), data); return (0); } error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) { if (cmd == TIOCSETP || cmd == TIOCSETN) xxparam(dev); return (error); } switch (cmd) { case TIOCREMOTE: if (xxmode[LINE(dev)] == 0) return (EBUSY); xxcntl(tp, XX_C_PAD, UNIT(dev)); break; case TIOCSBRK: xxcntl(tp, XX_C_BREAK, UNIT(dev)); break; case TIOCCBRK: case TIOCSDTR: case TIOCCDTR: break; default: return (ENOTTY); } return (0); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXPARAM() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Set parameters from open or stty. */ /* This routine is being left in as a dummy in case in the future */ /* there is a mechanism for the host to send information i.e. */ /* "hangup line" to the ACP _XX */ /* */ /* Call: xxparam(dev) */ /* Argument: dev: device */ /* Returns: none */ /* Called by: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*ARGSUSED*/ xxparam(dev) dev_t dev; { } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXSTART() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Start (restart) transmission on a given line. This is the */ /* start routine which is called from above by the tty driver and */ /* from below on a transmission complete interrupt for a given */ /* line. */ /* */ /* Call: xxstart(tp) */ /* Argument: tp: pointer to tty structure */ /* Returns: none */ /* Called by: tty driver */ /* xxreset() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ xxstart(tp) register struct tty *tp; { register struct dda_softc *ds; register int nch, cc, k; register struct dda_cb *dc; register char *cp, *p; struct ifqueue *oq; struct mbuf *m; padinfo *pp; int unit, line, s, j; extern int ttrstrt(); line = tp - xx_tty; unit = UNIT(line); dc = (struct dda_cb *) tp->t_addr; ds = &dda_softc[unit]; pp = &xx_padinfo[line]; s = splimp(); #ifdef DDADEBUG if (DDADBCH(99, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxstart: line %d t_state = %x\n", unit, line, tp->t_state DDAELOG; } #endif DDADEBUG /* If it's currently active, or delaying, no need to do anything. */ if ((tp->t_state & TS_CARR_ON) == 0) { tp->t_state &= ~(TS_TTSTOP | TS_BUSY); ttyflush(tp, FREAD | FWRITE); tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t) &tp->t_outq); goto out; } if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) goto out; /* wait for free */ if (dda_softc[unit].dda_state != S_LINK_UP) { ttyflush(tp, FREAD | FWRITE); DMESG(unit, 96, (DDALOG(LOG_ERR) "dda%d:(x29) xxstart: unit offline\n", unit DDAELOG) ); goto out; } /* If the writer was sleeping on output overflow, wake him when low tide * is reached. */ if (tp->t_outq.c_cc <= TTLOWAT(tp)) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t) &tp->t_outq); } if (tp->t_wsel) { selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); tp->t_wsel = 0; tp->t_state &= ~TS_WCOLL; } } /* restart transmission unless output queue is empty */ if (tp->t_outq.c_cc == 0) goto out; /* if this is an outbound pad line and it's in command mode */ if (pp->p_state == PS_COM) { xxpadhandle(ds, tp, pp); goto out; } /* Allocate an mbuf to stuff the chars into */ m = 0; MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) { DMESG(unit, 97, (DDALOG(LOG_ERR) "dda%d:(x29) xxstart: could not get mbuf\n", unit DDAELOG) ); goto out; } cp = mtod(m, char *); cc = 0; /* copy at most MLEN-1 chars out -- must save one byte for subfunc */ while ((cc < MLEN - 1) && (tp->t_outq.c_cc > 0)) { if (tp->t_flags & (RAW | LITOUT)) nch = ndqb(&tp->t_outq, 0); else { nch = ndqb(&tp->t_outq, 0200); if (nch == 0) { /* if first item was a delay */ (void) getc(&tp->t_outq); /* discard the character */ continue; } } if (nch > (MLEN - 1) - cc) nch = (MLEN - 1) - cc; /* If any characters were set up, start transmission; */ if (nch) { j = q_to_b(&tp->t_outq, cp, nch); #if OUTPUT_PARITY_MASK != 0377 /* strip all characters as desired */ for (p = cp, k = j; k; k--, p++) *p &= OUTPUT_PARITY_MASK; #endif #ifdef DDADEBUG if (DDADBCH(100, unit) && j != nch) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxstart: asked for %d got %d chars\n", unit, nch, j DDAELOG; } #endif DDADEBUG cc += nch; cp += nch; } else break; } #ifdef DDADEBUG if (DDADBCH(101, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxstart: mbuf %x len %d\n", unit, m, m->m_len DDAELOG; } #endif /* if any data was stuffed into the mbuf then send it */ if (cc) { m->m_dat[MLEN - 1] = 0; /* subfunction: no Q-bit */ m->m_len = cc; oq = &(dc->dc_oq); /* point to output queue */ if (IF_QFULL(oq)) { /* if q full */ IF_DROP(oq); /* drop the data */ m_freem(m); ds->dda_if.if_collisions++; /* for netstat display */ splx(s); return (ENOBUFS); } IF_ENQUEUE(oq, m); /* otherwise queue it */ tp->t_state |= TS_BUSY; dda_start(ds, dc); /* and try to output */ } else m_freem(m); out: if (dc->dc_lcn != 0) /* something left in oq? */ dda_start(ds, dc); /* restart output */ splx(s); return (0); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXRESET() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* In response to UNIBUS reset, reset state and restart */ /* transmitters. */ /* */ /* Call: xxreset(uban) */ /* Argument: uban: UNIBUS adaptor number */ /* Returns: none */ /* Called by: kernel software in response to UNIBUS reset */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*ARGSUSED*/ xxreset(uban) int uban; { } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXSTOP() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Dummy stop routine. */ /* */ /* Call: xxstop(tp, flag) */ /* Argument: tp: pointer to tty structure */ /* flag: indicates */ /* Returns: none */ /* Called by: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*ARGSUSED*/ xxstop(tp, flag) struct tty *tp; int flag; { } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXSELECT() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Circumvent bug in our bastardized design which causes ttselect */ /* to fail. */ /* */ /* Call: xxselect(dev, rw) */ /* Argument: dev: device */ /* rw: read or write indicator */ /* Returns: 0 or 1 */ /* Called by: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ xxselect(dev, rw) dev_t dev; int rw; { #ifdef DDADEBUG int unit = UNIT(dev); if (DDADBCH(102, unit)) DDALOG(LOG_DEBUG) "dda%d:(x29) select()\n", unit DDAELOG; #endif DDADEBUG return (ttselect(MAJLINE(dev), rw)); } /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL FUNCTIONS %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% X29_SUPR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine processes received supervisor messages. */ /* Depending on the message type, the appropriate action is */ /* taken. */ /* */ /* Call: x29_supr(ds, p) */ /* Arguments: ds: pointer to dev control block struct */ /* p: pointer to a character array */ /* containing the supervisor message */ /* Returns: nothing */ /* Called by: dda_supr() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void x29_supr(ds, p) struct dda_softc *ds; u_char p[]; { register struct dda_cb *dc; register struct tty *tp; register int lcn; int maxlcn; int line; #ifdef DDADEBUG if (DDADBCH(103, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_supr()\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG switch (p[0]) { case LINE_STATUS: /* link status msg */ case RESTART: /* restart received */ case RSTRT_ACK: /* restart ack */ case STATRESP: /* Statistics Response from FEP */ DMESG(ds->dda_if.if_unit, 98, (DDALOG(LOG_ERR) "dda%d:(x29) x29_supr: unexpected message type\n", ds->dda_if.if_unit DDAELOG)); break; case ANSWER: /* call answered */ lcn = p[1] / 2; dc = &(ds->dda_cb[lcn]); if (dc->dc_state == LC_CALL_PENDING) { /* if a call pending */ decode_answer(p, dc); dc->dc_state = LC_DATA_IDLE; /* set state */ dc->dc_flags = DC_X29; line = dc->dc_line; /* which line are we? */ #ifdef DDADEBUG if (DDADBCH(114, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_supr: answer: line=%d\n", ds->dda_if.if_unit, line DDAELOG; } #endif DDADEBUG if (line == -1) { /* fubar! */ DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR) "dda%d:(x29) x29_supr: answer: line was -1, VC 0x%x\n", ds->dda_if.if_unit, p[1] DDAELOG)); } xx_padinfo[line].p_state = PS_PAD; xxstart(&xx_tty[line]); } else { DMESG(ds->dda_if.if_unit, 108, (DDALOG(LOG_ERR) "dda%d:(x29) x29_supr: unexpected answer on LCN %d\n", ds->dda_if.if_unit, lcn DDAELOG)); } if (LOG_CALLS) { DDALOG(LOG_INFO) "dda%d:(x29) LCN %d: connected\n", ds->dda_if.if_unit, lcn DDAELOG; } break; case RING: /* incoming call */ if (decode_ring(p)) { /* find a free lcn associated with a XX_HOST open */ dc = &ds->dda_cb[1]; maxlcn = nddach[ds->dda_if.if_unit]; for (lcn = 1; lcn <= maxlcn; lcn++) { if (dc->dc_state == LC_IDLE && dc->dc_flags & DC_X29W) break; dc++; } if (lcn > maxlcn) { /* if no free lcn's */ if (LOG_BUSY) { DDALOG(LOG_ERR) "dda%d:(x29) no free X29W lcns, call rejected, vc=0x%x\n", ds->dda_if.if_unit, p[1] DDAELOG; } send_supr(ds, CLEARVC, p[2], 0); /* clear call */ break; /* exit case */ } /* got a good lcn, now use it */ #ifdef DDADEBUG if (DDADBCH(103, ds->dda_if.if_unit)) { DDALOG(LOG_ERR) "dda%d:(x29) supr_msg: call from 0x%0x\n", ds->dda_if.if_unit, (u_long) dc->dc_inaddr.s_addr DDAELOG; } #endif DDADEBUG dc->dc_state = LC_DATA_IDLE; /* set state */ dc->dc_pktsizein = 0; dc->dc_pktsizeout = 0; dc->dc_wsizein = 0; dc->dc_wsizeout = 0; dc->dc_flags = DC_X29; send_supr(ds, ANSWER, lcn * 2, p[2]); /* send answer */ if (LOG_CALLS) { DDALOG(LOG_INFO) "dda%d:(x29) Call accepted LCN %d\n", ds->dda_if.if_unit, dc->dc_lcn DDAELOG; } line = dc->dc_line; #ifdef DDADEBUG if (DDADBCH(114, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_supr: ring: line=%d\n", ds->dda_if.if_unit, line DDAELOG; } #endif DDADEBUG if (line == -1) { /* fubar! */ DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR) "dda%d:(x29) x29_supr: ring: line was -1, VC 0x%x\n", ds->dda_if.if_unit, p[1] DDAELOG)); break; } tp = &xx_tty[line]; xx_padinfo[line].p_state = PS_XFR; wakeup((caddr_t) &tp->t_rawq); tp->t_state |= TS_CARR_ON; #if ACC_ULTRIX > 00 tp->t_state &= ~TS_ONDELAY; #endif /* I would prefer to wait a bit before sending this */ send_x29_param_msg(ds, dc, SET_PAD, x29_callin_setparams, sizeof(x29_callin_setparams)); } else { /* bad decode */ send_supr(ds, CLEARVC, p[2], 0); /* clear call */ DMESG(ds->dda_if.if_unit, 100, (DDALOG(LOG_ERR) "dda%d:(x29) Bad decode, call REJECTED VC 0x%x\n", ds->dda_if.if_unit, p[1] DDAELOG)); } break; case CLEARLC: /* clear by LCN */ lcn = p[1] / 2; /* get LCN */ dc = &(ds->dda_cb[lcn]); if (dc->dc_state != LC_CLR_PENDING) { /* if no clear pending */ send_supr(ds, CLEARLC, p[1], 0); /* ack the clear */ } if (dc->dc_state == LC_CALL_PENDING) /* call is cleared */ DMESG(ds->dda_if.if_unit, 101, (DDALOG(LOG_ERR) "dda%d:(x29) Call cleared LCN %d (%x %x)\n", ds->dda_if.if_unit, dc->dc_lcn, p[2], p[4] DDAELOG)); hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_IDLE); dc->dc_state = LC_IDLE; dc->dc_timer = TMO_OFF; /* stop timer */ dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; abort_io(ds->dda_if.if_unit, lcn); xx_tp_hangup(ds, dc); /* will clear flags */ break; case CLEARVC: /* clear by VCN */ send_supr(ds, CLEARVC, p[1], 0); /* send clear ack */ if (LOG_CALLS) { DDALOG(LOG_INFO) "dda%d:(x29) Network cleared VC %x (%x %x)\n", ds->dda_if.if_unit, p[1], p[2], p[4] DDAELOG; } break; case RESET: /* X25 reset */ send_supr(ds, RESET_ACK, p[1], 0); /* send reset ack */ abort_io(ds->dda_if.if_unit, (int) p[1] / 2); DMESG(ds->dda_if.if_unit, 102, (DDALOG(LOG_ERR) "dda%d:(x29) X25 RESET on LCN %d (%x %x)\n", ds->dda_if.if_unit, p[1] / 2, p[2], p[4] DDAELOG)); break; case INTERRUPT: /* X25 interrupt */ #ifdef INDICATE_BREAK_ON_INTERRUPT lcn = p[1] / 2; dc = &(ds->dda_cb[lcn]); line = dc->dc_line; if (line == -1) { /* fubar! */ DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR) "dda%d:(x29) x29_supr: break: line was -1, VC 0x%x\n", ds->dda_if.if_unit, p[1] DDAELOG)); break; } tp = &xx_tty[line]; if (tp->t_flags & RAW) c = 0; else #if ACC_ULTRIX >= 30 c = tp->c_cc[VINTR];/* else make it the interrupt */ #else c = tp->t_intrc; /* else make it the interrupt */ #endif #if NBK > 0 if (tp->t_line == NETLDISC) { BKINPUT(c, tp); } else #endif (*linesw[tp->t_line].l_rint) (c, tp); /* send_supr (ds, INTR_ACK, p[1], 0); not needed -- done by FE */ #endif break; case INTR_ACK: /* quietly drop the acknowledgement */ break; default: DMESG(ds->dda_if.if_unit, 104, (DDALOG(LOG_ERR) "dda%d:(x29) supervisor error (%x %x %x %x)\n", ds->dda_if.if_unit, p[0], p[1], p[2], p[3] DDAELOG)); } } /* hangup any attached processes */ PRIVATE void xx_tp_hangup(ds, dc) struct dda_softc *ds; register struct dda_cb *dc; { register struct tty *tp; register padinfo *pp; register int line; line = dc->dc_line; if (line == -1) { /* fubar! */ DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR) "dda%d:(x29) xx_tp_hangup: line was -1\n", ds->dda_if.if_unit DDAELOG)); return; } tp = &xx_tty[line]; pp = &xx_padinfo[line]; if (pp->p_flow != P_NOBLOCK) { /* currently blocked? */ register struct hdx_chan *hc; hc = (struct hdx_chan *) & dc->dc_rchan; dda_rrq(ds, hc); /* make sure we hang a read */ } pp->p_flow = P_NOBLOCK; tp->t_state &= ~(TS_CARR_ON | TS_ASLEEP | TS_BUSY); ttyflush(tp, FREAD | FWRITE); gsignal(tp->t_pgrp, SIGHUP); gsignal(tp->t_pgrp, SIGCONT); tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t) &tp->t_outq); xxmode[line] = MODE_UNUSED; tp->t_addr = (caddr_t) NULL; pp->p_state = PS_IDLE; if (pp->p_mchsav) { m_freem(pp->p_mchsav); pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL; } dc->dc_flags &= ~(DC_X29 | DC_X29W); /* release to others */ } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% X29_DATA() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine is called when a data channel I/O completes. */ /* If the completion was for a write, an attempt is made to */ /* start output on the next packet waiting for output on that */ /* LCN. If the completion was for a read, the received packet */ /* is sent to the IP input queue (if no error) and another read */ /* is started on the LCN. */ /* */ /* Call: x29_data(ds, hc, cc, cnt) */ /* Argument: ds: device control block */ /* hc: half duplex channel control block */ /* cc: Mailbox I/O completion status */ /* cnt: byte count */ /* Returns: nothing */ /* Called by: ddainta() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #define QBIT 0x80 PRIVATE void x29_data(ds, hc, cc, cnt, subcc) register struct dda_softc *ds; register struct hdx_chan *hc; int cc, cnt, subcc; { register struct dda_cb *dc = &(ds->dda_cb[hc->hc_chan / 2]); register struct tty *tp; #ifdef DDADEBUG if (DDADBCH(104, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_data: chan=%x cc=%x cnt=%x subcc=%x\n", ds->dda_if.if_unit, hc->hc_chan, cc, cnt, subcc DDAELOG; } #endif DDADEBUG if (hc->hc_chan & 0x01) { /* if write, fire up next output */ #ifdef DDADEBUG dc->dc_out_t = TMO_OFF; /* turn off output completion timer */ #endif if ((hc->hc_func != DDAABT) && (hc->hc_curr = hc->hc_curr->m_next)) dda_wrq(ds, hc, 0); else { /* it is abort | no more data left */ char qbit_indicator; qbit_indicator = hc->hc_mbuf->m_dat[MLEN - 1]; m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; if (hc->hc_func == DDAABT) { hc->hc_func &= ~DDAABT; hc->hc_inv &= ~INVALID_MBUF; } else ds->dda_if.if_opackets++; dc->dc_flags &= ~DC_OBUSY; if (qbit_indicator == QBIT) { /* Q-bit packet? */ dda_start(ds, dc); /* restart output */ } else { tp = &xx_tty[dc->dc_line]; tp->t_state &= ~TS_BUSY; xxstart(tp); /* restart tty output */ } } /* it's a packet coming in from the front end to the host */ } else { #ifdef DDADEBUG dc->dc_flags &= ~DC_IPEND; #endif hc = &dc->dc_rchan; #ifdef DDADEBUG if (DDADBCH(105, ds->dda_if.if_unit)) { u_char *p; DDALOG(LOG_DEBUG) "dda%d:(x29) ", ds->dda_if.if_unit DDAELOG; p = mtod(hc->hc_curr, u_char *); prt_bytes(ds->dda_if.if_unit, "received data", p, (cnt < 64 ? cnt : 64)); } if (DDADBCH(106, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_data: read complete mbuf=%x curr=%x\n", ds->dda_if.if_unit, hc->hc_mbuf, hc->hc_curr DDAELOG; } #endif DDADEBUG if (dc->dc_state != LC_DATA_IDLE) { m_freem(hc->hc_mbuf); /* toss the packet, lcn is dead */ hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } else if (cc == DDAIOCOK || (cc == DDAIOCOKP && !(subcc & QBIT))) { /* Queue up I/O completion OK transfers and I/O OK with more data * pending transfers (as long as it's not a Qbit message). * This algorythm operates differently than the IP handler due * to the fact that we don't need to wait for the entire X.25 * packet to arrive on the host before we assemble it. To do * so should be OK, but unfortunately it seems some brain-dead * PAD's generate packets with the M-bit set if they have more * data in their internal buffers. This can cause the system * to burn up mbufs waiting for us to finally receive a packet * with the M-bit not set. However, we should hold up on processing * packets with both the Q-bit and the M-bit set until we receive * the entire Q-bit message. If we get 30k Q-bit packets, we will * die, but that is obscenely absurd in the first place. * (sigh) -- pst 7-19-89 */ #ifdef DDADEBUG if (DDADBCH(107, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_data: chan=%x DDAIOCOK\n", ds->dda_if.if_unit, hc->hc_chan DDAELOG; } #endif DDADEBUG hc->hc_curr->m_len += cnt; /* update byte count */ ds->dda_if.if_ipackets++; /* HANDLE THE DATA HERE */ if (subcc & QBIT) { int len; char *mcp; mcp = mtod(hc->hc_curr, char *); len = hc->hc_curr->m_len; #ifdef DDADEBUG if (DDADBCH(108, ds->dda_if.if_unit)) prt_bytes(ds->dda_if.if_unit, "(x29) Qbit:", mcp, (len < 64 ? len : 64)); #endif DDADEBUG if (*mcp == BREAK_INDIC) { /* Break indication? */ register struct tty *tp; if (x29_break_reply_is_required(mcp, len)) { /* tell pad to stop discarding output */ send_x29_param_msg(ds, dc, SET_PAD, x29_break_ack_params, 2); } hc->hc_curr->m_len = 1; /* change data to single byte */ tp = &xx_tty[dc->dc_line]; if (tp->t_flags & RAW) /* if port is in raw mode, */ *mcp = 0; /* make the byte a null */ else #if ACC_ULTRIX >= 30 *mcp = tp->t_cc[VINTR]; /* else make it the interrupt */ #else *mcp = tp->t_intrc; /* else make it the interrupt */ #endif x29_dhandle(ds, dc, 0); return; } else if (*mcp & READ_PAD) { if (len == 1) /* just a message, no params? */ send_x29_param_msg(ds, dc, PAR_INDICATION, x29_callout_params, sizeof(x29_callout_params)); else send_x29_param_msg(ds, dc, PAR_INDICATION, mcp + 1, len - 1); m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } else { m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } } else { /* not Qbit data, process normally */ x29_dhandle(ds, dc, 0); return; } } else if (cc == DDAIOCOKP) { /* good completion, more data pending */ hc->hc_curr->m_len += cnt; } else { /* toss packet */ m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } /* hang a new data read */ #ifdef DDADEBUG dc->dc_flags |= DC_IPEND; #endif dda_rrq(ds, hc); } } /* this routine copies chars from the dc_rchan mbuf to the upper * level software. If all the characters are read then the mbuf is * freed and a new read is hung on the channel. * * This routine is called from below by the int A handler and from above * by the device read routine. */ PRIVATE void x29_dhandle(ds, dc, restart) register struct dda_softc *ds; register struct dda_cb *dc; int restart; { register struct tty *tp; register struct hdx_chan *hc; register padinfo *pp; u_char *cp, c; struct mbuf *m2, *m; int s, line; register int j; static int recurse = 0; s = splimp(); if (recurse) { /* don't allow ourselves to be called recursively */ splx(s); return; } else recurse = 1; hc = (struct hdx_chan *) &dc->dc_rchan; line = dc->dc_line; tp = &xx_tty[line]; pp = &xx_padinfo[line]; if (restart) { /* trying to restart input? */ j = pp->p_flow; m = pp->p_mchsav; m2 = pp->p_msav; #ifdef DDADEBUG if (DDADBCH(109, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) flow restart [%d] in %x\n", ds->dda_if.if_unit, j, m DDAELOG; } #endif DDADEBUG } else { j = P_NOBLOCK; m2 = m = hc->hc_mbuf; /* que mbuf chain */ } if (m == 0) { DMESG(ds->dda_if.if_unit, 105, (DDALOG(LOG_ERR) "dda%d:(x29) x29_dhandle: null mbuf\n", ds->dda_if.if_unit DDAELOG)); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) NULL; dda_rrq(ds, hc); goto out; } while (m2) { cp = mtod(m2, u_char *); for (; j < m2->m_len; j++) { c = cp[j] & INPUT_PARITY_MASK; if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= TTYHOG - 2) if (!ttbreakc(c, tp)) continue; /* dump the character */ #if NBK > 0 if (tp->t_line == NETLDISC) { BKINPUT(c, tp); } else #endif (*linesw[tp->t_line].l_rint) (c, tp); /* Block further input iff: Current input > threshold AND input * is available to user program */ if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG / 4 && ((tp->t_flags & (RAW | CBREAK)) || (tp->t_canq.c_cc > 0))) { #ifdef DDADEBUG if (DDADBCH(109, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) flow on [%d] in %x of %d\n", ds->dda_if.if_unit, j, m2, m2->m_len DDAELOG; } #endif DDADEBUG pp->p_flow = j + 1; pp->p_msav = m2; pp->p_mchsav = m; if (restart == 0) hc->hc_mbuf = hc->hc_curr = (struct mbuf *) NULL; goto out; } } m2 = m2->m_next; j = P_NOBLOCK; } if (restart) pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL; m_freem(m); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) NULL; pp->p_flow = P_NOBLOCK; #ifdef DDADEBUG dc->dc_flags |= DC_IPEND; #endif dda_rrq(ds, hc); out: recurse = 0; splx(s); } PRIVATE void xx_qbit_msg(tp, unit, msg) register struct tty *tp; int unit; char *msg; { register struct dda_cb *dc; register struct dda_softc *ds; int s; ds = &dda_softc[unit]; dc = (struct dda_cb *) tp->t_addr; s = splimp(); #ifdef DDADEBUG if (DDADBCH(110, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) xx_qbit_msg: %d %d %d\n", unit, msg[0], msg[1], msg[2] DDAELOG; } #endif DDADEBUG if (msg[1] < (MLEN - 4)) send_x29_param_msg(ds, dc, msg[0], msg + 2, msg[1]); splx(s); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXCNTL() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Do modem control functions on a line. */ /* */ /* Call: xxcntl(tp, c, d) */ /* Argument: tp: pointer to tty structure */ /* c: function code */ /* unit: for unit number */ /* Returns: none */ /* Called by: xxopen() */ /* xxclose() */ /* xxread() */ /* xxint() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void xxcntl(tp, c, unit) register struct tty *tp; int c, unit; { register struct dda_cb *dc; register struct dda_softc *ds; register padinfo *pp; int s, l; l = tp - xx_tty; ds = &dda_softc[unit]; pp = &xx_padinfo[l]; s = splimp(); #ifdef DDADEBUG if (DDADBCH(111, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxcntl: tp=0x%x line=%d\n", unit, tp, l DDAELOG; } #endif DDADEBUG switch (c) { case XX_C_PAD: if (tp->t_addr) break; if (dc = find_free_lcn(ds)) { /* race against locate_x25_lcn */ dc->dc_flags = DC_X29; dc->dc_line = l; pp->p_state = PS_COM; tp->t_addr = (caddr_t) dc; tp->t_flags &= ~ECHO; pp->p_flow = P_NOBLOCK; pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL; pp->p_idx = 0; pp->p_line[0] = '\0'; } else tp->t_addr = (caddr_t) NULL; break; case XX_C_HOST: if (tp->t_addr) break; if (dc = find_free_lcn(ds)) { /* race against locate_x25_lcn */ dc->dc_flags = DC_X29W; dc->dc_line = l; pp->p_state = PS_WAIT; pp->p_flow = P_NOBLOCK; pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL; tp->t_addr = (caddr_t) dc; } else tp->t_addr = (caddr_t) NULL; break; case XX_C_CLOSE: pp->p_state = PS_IDLE; if (pp->p_mchsav) { m_freem(pp->p_mchsav); pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL; } dc = (struct dda_cb *) tp->t_addr; if (dc == 0) break; if (pp->p_flow != P_NOBLOCK) { /* currently blocked? */ register struct hdx_chan *hc; hc = (struct hdx_chan *) &dc->dc_rchan; dda_rrq(ds, hc); /* make sure we hang a read */ } #ifdef DDADEBUG if (DDADBCH(111, unit)) { static char *st[] = { "lcn down", "lcn restart", "idle", "call pending", "data idle", "clear pending" }; DDALOG(LOG_DEBUG) "dda%d:(x29) xxcntl: close state: %s\n", unit, st[dc->dc_state] DDAELOG; } #endif DDADEBUG if (dc->dc_state == LC_DATA_IDLE || dc->dc_state == LC_CALL_PENDING) clear_lcn(ds, dc); /* send clear & set state to clr_pending */ /* timers will convert it to LC_IDLE later */ #ifdef DDADEBUG else if (DDADBCH(111, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxcntl: warning: state not data_idle\n", unit DDAELOG; } #endif dc->dc_flags &= ~(DC_X29 | DC_X29W); /* release to others */ tp->t_addr = (caddr_t) NULL; break; case XX_C_BREAK: /* really should look at X.3 parameters to decide if an interrupt * packet should be sent. instead, we take an action which assumes * PAD parameter 7 has value 21 */ dc = (struct dda_cb *) tp->t_addr; send_supr(ds, INTERRUPT, dc->dc_lcn * 2, 0); send_x29_param_msg(ds, dc, BREAK_INDIC, 0, 0); break; } splx(s); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% X29_INIT() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Software reset, clear lines. */ /* */ /* Call: x29_init(unit, active); */ /* Argument: unit: ACP _XX device */ /* Returns: none */ /* Called by: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void x29_init(unit, active) int unit, active; { register int i; register padinfo *pp; #ifdef DDADEBUG if (DDADBCH(113, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_init() active=%d\n", unit, active DDAELOG; } #endif DDADEBUG if (active) xxclear(unit); else { for (i = 0; i < XXLPERBRD; i++) { xx_tty[unit * XXLPERBRD + i].t_state = PS_IDLE; pp = &xx_padinfo[unit * XXLPERBRD + i]; pp->p_state = PS_IDLE; pp->p_flow = P_NOBLOCK; pp->p_msav = pp ->p_mchsav = (struct mbuf *) NULL; } } } PRIVATE void xxclear(unit) int unit; { register struct tty *tp; register struct dda_softc *ds; register struct dda_cb *dc; int i, state; ds = &dda_softc[unit]; for (i = 0, tp = &xx_tty[unit * XXLPERBRD]; i < XXLPERBRD; i++, tp++) { state = tp->t_state; #ifdef DDADEBUG if (DDADBCH(112, unit) && state) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxclear: line=%d pgrp=%d state=%d\n", unit, i, tp->t_pgrp, state DDAELOG; } #endif DDADEBUG if (state & TS_WOPEN) { tp->t_state &= ~TS_WOPEN; wakeup(&tp->t_rawq); } if (tp->t_state) { dc = (struct dda_cb *) tp->t_addr; if (dc) { xx_tp_hangup(ds, dc); dc->dc_line = -1; /* break correspondence */ } } } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXSHOW() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Show status of each active unit */ /* */ /* Call: xxshow() */ /* Argument: none */ /* Returns: none */ /* Called by: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void xxshow() { register struct tty *tp; register padinfo *pp; int unit, i; static char *st[] = { "idle", " com", " pad", "wait", "xfer" }; for (unit = 0; unit < (NDDA < XXBOARDS ? NDDA : XXBOARDS); unit++) { uprintf("\nACP5250/6250 X29 driver: state of unit %d -\n", unit); uprintf("line\tstate\tlcn\tflow\ttstate\ttflags\n"); for (i = 0, tp = &xx_tty[unit * XXLPERBRD]; i < XXLPERBRD; i++, tp++) { if (tp->t_state) { pp = &xx_padinfo[i]; uprintf("%d:\t%s\t%d\t%d\t%x\t%x\n", i, st[pp->p_state], (struct dda_cb *) (tp->t_addr) - dda_softc[unit].dda_cb, pp->p_flow, tp->t_state, tp->t_flags); } } } uprintf("remaining lines free\n"); } /****************************************************************************** * PAD CODE ******************************************************************************/ /* PADCHARUP - Pass a character up towards the user */ #define PADCHARUP(c,tp) (*linesw[(tp)->t_line].l_rint) ((c), (tp)) PRIVATE void xxpadhandle(ds, tp, pi) struct dda_softc *ds; struct tty *tp; /* pointer to relevant tty structure */ padinfo *pi; /* pointer to relevant padinfo structure */ { register int i; register char c; register struct dda_cb *dc; int nch; char tbuf[CBSIZE]; /* CBSIZE is number of char in a * cblock */ nch = q_to_b(&tp->t_outq, tbuf, CBSIZE); /* handle characters in command state. Its OK if were slow here because * there is a person on the other end of the discussion */ dc = (struct dda_cb *) tp->t_addr; for (i = 0; i < nch; i++) { if (pi->p_idx >= P_LINELEN) { xxpadmsg("\r\ncommand too long\r\n@", tp); pi->p_idx = 0; return; } c = pi->p_line[pi->p_idx] = tbuf[i] & INPUT_PARITY_MASK; if (c == '\r' || c == '\n') { PADCHARUP('\r', tp); PADCHARUP('\n', tp); pi->p_line[pi->p_idx] = '\0'; if (dc && dc->dc_state != LC_IDLE) { xxpadmsg("cannot call, line is in transition\r\n", tp); if (dc && dc->dc_state == LC_CALL_PENDING) xxpadmsg("previous call still pending\r\n", tp); } else if (xxpadparse(ds, pi, tp) == 0) PADCHARUP('@', tp); pi->p_idx = 0; } else if (c == '\b' || c == '\177') { if (pi->p_idx) { pi->p_idx--; xxpadmsg("\b \b", tp); } } else { pi->p_idx++; PADCHARUP(c, tp); } } } PRIVATE int xxpadparse(ds, pi, tp) struct dda_softc *ds; padinfo *pi; struct tty *tp; { char *p = pi->p_line; if (*p == 'c' || *p == 'C') { /* connect command */ for (p++; *p == ' '; *p++); if (*p < '0' || *p > '9') xxpadmsg("???\r\n", tp); else /* place a call */ return xxpadcall(ds, p, tp); } else if (*p) xxpadmsg("invalid command\r\n", tp); return 0; } PRIVATE int xxpadcall(ds, addr, tp) struct dda_softc *ds; char *addr; struct tty *tp; { register int i = 0; struct in_addr in; while (addr[i]) { if (addr[i] < '0' || addr[i] > '9') { xxpadmsg("invalid address\r\n", tp); return 0; } i++; } ddacb_called_addr[0] = i; bcopy(addr, ddacb_called_addr + 1, i); ddacb_user_data[0] = (u_char) 0; /* no user data for now */ in.s_addr = 0; return make_x25_call(ds, (struct dda_cb *) tp->t_addr, in, X25_PROTO_X29); } PRIVATE void xxpadmsg(s, tp) char *s; struct tty *tp; { while (*s) { PADCHARUP(*s, tp); s++; } } /* * This routine is used to respond to * READ_PARAMS and SET_READ_PARAMS requests, and also * to send out a SET_PARAMS request for incoming calls. * The outgoing pad supports NO parameters. */ send_x29_param_msg(ds, dc, type, msg, len) register struct dda_cb *dc; register struct dda_softc *ds; x29_pad_pair *msg; { struct mbuf *m; u_char *p; short i; register struct ifqueue *oq; m = 0; /* Allocate an mbuf to stuff the chars into */ MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) { DMESG(ds->dda_if.if_unit, 106, (DDALOG(LOG_ERR) "dda%d:(x29) couldn't get mbuf for QBIT message\n", ds->dda_if.if_unit DDAELOG)); return; } m->m_dat[MLEN - 1] = QBIT; /* set Q-bit */ p = mtod(m, u_char *); len = len / 2; *p++ = type; if (type == PAR_INDICATION) { /* our pad supports NO parameters */ for (i = 0; i < len; i++) { *p++ = msg[i].ref | 0x80; /* set invalid bit */ *p++ = 1; /* not implemented */ } } else { /* BREAK_INDIC, SET_PAD to ack break */ for (i = 0; i < len; i++) { *p++ = msg[i].ref; *p++ = msg[i].val; } } m->m_len = 1 + 2 * len; oq = &(dc->dc_oq); /* point to output queue */ if (IF_QFULL(oq)) { /* if q full */ IF_DROP(oq); /* drop the data */ m_freem(m); ds->dda_if.if_collisions++; /* for netstat display */ } else { IF_ENQUEUE(oq, m); /* otherwise queue it */ dda_start(ds, dc); /* and try to output */ } } PRIVATE int x29_break_reply_is_required(mcp, len) char *mcp; int len; { mcp++; /* skip over break indication msg */ while (len > 1) { /* while there are parameters left, */ if ((*mcp == 8) && (mcp[1] == 1)) /* paramter 8 set to 1? */ return 1; /* yes */ mcp += 2; len -= 2; } return 0; } /* * Ultrix 3.0 removed the old ttbreakc() kernel routine when moving to * a posix compliant driver. Here it is again, (for our local use only!!!) * */ #if ACC_ULTRIX >= 30 static int ttbreakc(c, tp) register c; register struct tty *tp; { return (c == tp->t_cc[VEOL] || c == tp->t_cc[VEOF] || c == tp->t_cc[VEOL2] || c == '\r' && (tp->t_flags & CRMOD)); } #endif /* Revision History: 09-Jun-1988: Unknown (Brad?) Initial implementation. 15-Feb-1989: Paul Traina Fixed point bug in send_x29_prm_msg 08-Mar-1989: Steve Johnson Fixed bug in xx_flow logic 24-May-1989: Paul Traina Upgraded for Ultrix 3.0 28-May-1989: Paul Traina Added more driver intelligence to disable pad durring call pending 31-May-1989: Paul Traina Added flexible mapping for # of boards per unit 04-Jun-1989: Paul Traina Fixed driver to dequeue Q-bit X29 packets from the mbuf chain properly. 19-Jun-1989: Paul Traina Fixed previous fix-- will need to go over if-elseif logic more carefully to make sure we're doing the right thing. It should be recoded. Modernized entire debug code suite, changed xxshow functionality to use the uprintf() kernel call to display data on user's terminal for the xxshow hack. 12-Jul-1989: Paul Traina Changed format of some debug messages. Removed LOCAL_VOID in favor of PRIVATE routine to aid in debugging. Simplified some chunky logic. 18-Jul-1989: Paul Traina Flipped search order for finding a free X29W lcn at RING time. Moved the dc_key.ttyline field out of the union and made it dc_line. This fixed the Dartmouth singleuser bug. 19-Jul-1989: Paul Traina Changed the packet decode logic in x29_data to immediately process packets with more data pending (i.e. the M-bit) right away, instead of queuing them up. (Note: it still queues up Q-bit packets) This may fix the Dartmouth mbuf problem with blasting uploads. 27-Jul-1989: Paul Traina Removed 8-bit strip in x29_dhandle. 01-Aug-1989: Paul Traina Added additional two parameters to make_x25_call for userdata/length for merge with new pad software. 02-Aug-1989: Paul Traina Reinserted 8-bit strip on data received from the net. (uses PARITY_MASK define for easy change). Fixed forward declaration of ttbreakc(). Improved readability of xxshow output. Removed "super" pad code. Modified ps_state to be a real state variable. 03-Aug-1989: Paul Traina Reversed earlier change to xxselect which didn't pass major #. Modified xxshow output to not use %nd which isn't supported in BSD. 28-Aug-1989: Paul Traina Changed parameters of make_x25_call -- plug user data field directly. 14-Nov-1989: Paul Traina Added support for Ultrix 3.1 which uses HUPCL instead of HUPCLS because of that stupid termio interface (sigh). 16-Nov-1989: Paul Traina Changed parity mask to input_parity_mask, added output_parity_mask. */ da_if.if_unit, 106, (DDALOG(LOG_ERR) "dda%d:(x29) couldn't get mbuf for QBIT message\n", ds->dda_if.if_unit DDAELOG)); return; } m->m_dat[MLEN - 1] = QBIT; /* set Qdriver/if_pivar.h.tahoe 644 540 24 12662 4535573160 10505 /*************************************************************************/ /* if_pivar.h V1.1 26 Jun 1989 */ /*************************************************************************/ /* */ /* */ /* ________________________________________________________ */ /* / \ */ /* | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* \________________________________________________________/ */ /* */ /* Copyright (c) 1988 by Advanced Computer Communications */ /* 720 Santa Barbara Street, Santa Barbara, California 93101 */ /* (805) 963-9431 */ /* */ /* */ /* File: if_pivar.h */ /* */ /* Author: Brad Engstrom. */ /* */ /* Project: UNIX DDA-X.25 Network Programmers */ /* Interface for ACP 5250 and ACP 6250 */ /* */ /* Function: This file contains definitions of the PI */ /* data structures and ioctl indicators. */ /* */ /* Revision History: */ /* */ /* 06-Sep-1988: V1.0 - Brad Engstrom */ /* First generated */ /* 26-Jun-1989: V1.1 - Paul Traina */ /* Updated ioctl defines for 4.3-tahoe & newer CPP */ /* */ /*************************************************************************/ /* * Ioctl's have the command encoded in the lower word, * and the size of any in or out parameters in the upper * word. The high 2 bits of the upper word are used * to encode the in/out status of the parameter; for now * we restrict parameters to at most 128 bytes. */ /* this structure is used when writing or reading data data */ struct pi_dblock { caddr_t dataptr; /* pointer to user data to be written */ u_int length; /* length of data pointed to by dataptr */ u_short lcn; /* logical channel to send data out on */ u_char func; /* read status , write function value */ u_char subfunc; /* read substatus, write subfunction value */ u_short flags; /* special flags */ }; /* pi_dblock.flags: The follwing values can be used to set bits */ #define DB_NONBLOCK 0x01 /* non-blocking read or write */ typedef struct proto_range { u_char lower; /* lower bound on protocol range (inclusive) */ u_char upper; /* upper bound on protocol range (inclusive) */ } proto_range; /* data write */ #define XIOWRITE _IOWR('t', 1, struct pi_dblock) /* data read */ #define XIOREAD _IOWR('t', 2, struct pi_dblock) /* see if data is ready to be read on a particular channel. * The field will return the number of pending data bytes. */ #define XIORPEND _IOR('t', 3, int) /* allow rings on this minor device. The proto_range structure specifies * the lower and upper bounds on the protocol byte of incomming calls */ #define XIOACCRING _IOW('t', 4, proto_range) /* specify that a protocol not requested by anyone else will be accepted on * this channel. */ #define XIOANYPROTO _IO('t', 5) /* reserve an lcn for use by this channel (minor device). The number of the * lcn is returned the the character passed */ #define XIOGETLCN _IOR('t', 6, u_char) /* free an lcn specified by the u_char parameter. This should be called for all * lcn's obtained with XIOGETLCN before the channel is closed. */ #define XIOFREELCN _IOW('t', 11, u_char) /* clear any data associated with a channel. The lcn is passed. Note that * this does not clear the circut. It only flushes queued data stored in the * driver */ #define XIOCLRCHAN _IO('t', 7) /* disallow incomming calls on this channel */ #define XIONORING _IO('t', 9) /* specify a signal to be sent to the process when there is data ready to * be read on a particular channel. The u_int specifies the signal to be * sent. */ #define XIORSIG _IOW('t', 10, u_int) /* abort all output for the given lcn. */ #define XIOABORT _IOW('t', 12, u_char) cations */ /* 720 Santa Barbara Street, Santa Barbara, California driver/if_dda_bibus.c 444 540 24 136505 4535573573 10225 /*************************************************************************/ /* */ /* */ /* ________________________________________________________ */ /* / \ */ /* | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAA CCCC CCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | */ /* | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | */ /* \________________________________________________________/ */ /* */ /* Copyright (c) 1986 by Advanced Computer Communications */ /* 720 Santa Barbara Street, Santa Barbara, California 93101 */ /* (805) 963-9431 */ /* */ /* */ /* File: if_dda_bibus.c */ /* BI bus support routines for dda */ /* */ /* Project: DDN-X.25 Network Interface Driver for ACP 7250 */ /* */ /* revision history at the end of if_dda.c */ /*************************************************************************/ #include "../vaxif/if_uba.h" #include "../vaxuba/ubavar.h" #include "../vaxbi/bireg.h" #include "../vaxif/if_ddabique.h" #ifdef SIMULATION #define KM_CLUSTER 28 #define KM_NOWAIT 1 #define KM_ALLOC(space, cast, size, type, flags) { \ (space) = (cast)malloc(size); \ } #define KM_FREE(addr, type) { \ (void)free((caddr_t)(addr)); \ } #undef svtophy #define svtophy(x) ((int)(x)) #endif /* * private functions in this module */ PRIVATE void dda_shm_setup(); PRIVATE void dda_disable(); PRIVATE void dda_dump_biic_regs(); PRIVATE void dda_dump_shm(); PRIVATE void dda_unit_reset(); PRIVATE int dda_dload(); extern struct bidata bidata[]; /* previous_unload contains this value if the request queue is *not* blocked */ #define UL_NOT_BLOCKED (RQSIZE+1) typedef struct { byte fe_state; byte fe_soft_id; byte fe_soft_vers; byte fe_diag_status; } GP_REG3_USAGE; PRIVATE u_short ddastd[] = { 0 }; /* standard addresses */ /* ddainfo is setup by OS */ struct uba_device *ddainfo[NDDA]; /* ptrs to device info */ struct uba_driver ddadriver = /* device driver info */ { ddaprobe, /* device probe routine */ 0, /* slave probe routine */ ddaattach, /* device attach routine */ 0, /* "dmago" routine */ ddastd, /* device address */ "dda", /* device name */ ddainfo, /* ptr to device info ptrs */ "dda", /* device name */ 0 }; /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAPROBE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine is pretty much a dummy. The real probe is done */ /* the operating system. */ /* */ /* Call: ddaprobe(ui) */ /* Argument: ui: pointer to device data structure, used */ /* for BI environment */ /* Returns: length of register structure for ACP device */ /* Called by: network software, part of autoconfiguration on */ /* the VAX, the address of this routine is one of */ /* the fields of the uba_driver structure */ /* Calls to: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE int savefirmrev; /* save between probe & attach */ ddaprobe(nxv, nxp, cpup, binumber, binode, ui) struct bi_nodespace *nxv; char *nxp; struct cpusw *cpup; int binumber; int binode; struct uba_device *ui; { GP_REG3_USAGE *gpreg3; /* check to see if this board was in system configuration */ if (ui->ui_unit > NDDA) return 0; /* extra board? punt */ gpreg3 = (GP_REG3_USAGE *) &nxv->biic.biic_gpr3; /* check that the device is really a 7250 and save away */ /* the firmware revision level for version dependent processing */ /* If we just booted, the diagnostics may still be running as we */ /* probe the device - it's still OK to read the ID and VERSION */ /* numbers, which come valid within milliseconds after power is */ /* applied to the board - so say the firmware gurus */ dda_hasmaint = gpreg3->fe_soft_vers & 0x80; savefirmrev = gpreg3->fe_soft_vers & 0x7f; switch (gpreg3->fe_soft_id) { case 0x68: dda_product = "ACP7250"; break; default: #ifdef notdef if (dda_hasmaint == 0) /* assume we know what we're doing */ return 0; savefirmrev = 255; /* assume high version */ dda_product = "ACP7250"; break; #else return 0; #endif } return 1; /* return that we have found a board and are happy */ } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAATTACH() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine attaches the device to the network software. The */ /* network interface structure is filled in. The device will be */ /* initialized when the system is ready to accept packets. The */ /* dda_init initialization/service flag is zeroed, DDN standard */ /* X.25 service is implemented by default unless otherwise */ /* specified by the user via the acpconfig program. */ /*-- Various other types of one-time initialization: - allocate memory and setup queues --*/ /* */ /* Call: ddaattach(ui) */ /* Argument: ui: ptr to the uba_device data structure */ /* Returns: nothing */ /* Called by: network software, part of network system */ /* configuration, identification to the network */ /* software, the address of this routine is one */ /* of the fields of the uba_driver structure */ /* Calls to: if_attach() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ ddaattach(ui) struct uba_device *ui; { register struct dda_softc *ds = &dda_softc[ui->ui_unit]; SYSGEN_BLOCK *SysGenPtr; static long initmsgs[] = {0, 0, 0, 0, -1, -1, -1, -1, -2}; ds->dda_init = DDA_STANDARD; /* init/service flag <- default */ ds->dda_firmrev = savefirmrev; /* saved firmware rev level */ ds->dda_net_id = 0; /* default */ ds->dda_if.if_unit = ui->ui_unit; /* set unit number */ ds->dda_if.if_name = "dda"; /* set device name */ ds->dda_if.if_mtu = DDAMTU; /* set max msg size */ ds->dda_if.if_init = ddainit; /* set init routine addr */ ds->dda_if.if_ioctl = ddaioctl; /* set ioctl routine addr */ ds->dda_if.if_output = ddaoutput; /* set output routine addr */ ds->dda_if.if_reset = ddareset; /* set reset routine addr */ ds->dda_if.if_watchdog = ddatimer; /* set timer routine addr */ bcopy(initmsgs, ddamsgs[ui->ui_unit], sizeof(initmsgs)); /* allocate shared memory segment */ KM_ALLOC(SysGenPtr, SYSGEN_BLOCK *, 1024, KM_CLUSTER, KM_NOWAIT); if (SysGenPtr == NULL) { DMESG(ui->ui_unit, 0, (DDALOG(LOG_ERR) "dda%d: unable to get shared memory segment\n", ui->ui_unit DDAELOG)); return; } ds->dda_mapreg = (int) SysGenPtr; /* save it in ds structure */ /* hook the interrupt vector to scb */ #ifndef SIMULATION bidev_vec(ui->ui_adpt, ui->ui_nexus, LEVEL14, ui); #endif dda_shm_setup(ui->ui_unit, (struct biic_regs *) ui->ui_addr, SYSGEN_VALID); if_attach(&ds->dda_if); /* attach new network device */ } PRIVATE void dda_shm_setup(unit, nxv, drvr_state) int unit; struct biic_regs *nxv; int drvr_state; { register struct dda_softc *ds = &dda_softc[unit]; register struct uba_device *ui = ddainfo[unit]; SYSGEN_BLOCK *SysGenPtr; char *mem; SysGenPtr = (SYSGEN_BLOCK *) ds->dda_mapreg; mem = (char *) ds->dda_mapreg; SysGenPtr->request = (RQUEUE *) &mem[512 - 4]; /* get entries on page */ SysGenPtr->completion = (CQUEUE *) &mem[sizeof(SYSGEN_BLOCK) + 7 & 0xff7c]; SysGenPtr->prequest = svtophy(SysGenPtr->request); SysGenPtr->pcompletion = svtophy(SysGenPtr->completion); SysGenPtr->req_size = RQSIZE; SysGenPtr->comp_size = CQSIZE; SysGenPtr->intr_level = 0; /* level 0 is lowest level */ SysGenPtr->pwr_action = 0; /* boot default code */ SysGenPtr->previous_unload = UL_NOT_BLOCKED; /* request queue not blocked */ SysGenPtr->request->load = 0; SysGenPtr->request->unload = 0; SysGenPtr->completion->load = 0; SysGenPtr->completion->unload = 0; bzero(SysGenPtr->completion->entry, sizeof(CENTRY) * CQSIZE); bzero(SysGenPtr->request->entry, sizeof(RENTRY) * RQSIZE); #ifndef SIMULATION /* set interrupt destination */ nxv->biic_int_dst = bidata[ui->ui_adpt].biintr_dst; #endif nxv->biic_err = nxv->biic_err; /* actually clears error reg */ nxv->biic_gpr1 = svtophy(SysGenPtr); /* get BI physical address */ nxv->biic_gpr0 = drvr_state; /* tell FE our state */ } #ifdef DDADEBUG PRIVATE void dda_dump_biic_regs(nxv) struct biic_regs *nxv; { uprintf("biic register dump (nxv=0x%x 0x%x(p))\n", nxv, svtophy(nxv)); uprintf("typ=0x%x ctrl=0x%x err=0x%x err_int=0x%x\n", nxv->biic_typ, nxv->biic_ctrl, nxv->biic_err, nxv->biic_err_int); uprintf("int_dst=0x%x ip_msk=0x%x ip_dst=0x%x ip_src=0x%x\n", nxv->biic_int_dst, nxv->biic_ip_dst, nxv->biic_ip_dst, nxv->biic_ip_src); uprintf("strt=0x%x end=0x%x bci_ctrl=0x%x wrt_stat=0x%x\n", nxv->biic_strt, nxv->biic_end, nxv->biic_bci_ctrl, nxv->biic_wrt_stat); uprintf("int_ctrl=0x%x\n", nxv->biic_int_ctrl); uprintf("gpr0=0x%x gpr1=0x%x gpr2=0x%x gpr3=0x%x\n", nxv->biic_gpr0, nxv->biic_gpr1, nxv->biic_gpr2, nxv->biic_gpr3); } PRIVATE void dda_dump_shm(shm) SYSGEN_BLOCK *shm; { uprintf("request=0x%x 0x%x(p) completion=0x%x 0x%x(p)\n", shm->request, shm->prequest, shm->completion, shm->pcompletion); uprintf("req_size=%d comp_size=%d intr_level=%d pwr_act=%d\n", shm->req_size, shm->comp_size, shm->intr_level, shm->pwr_action); uprintf("req_load=%d req_un=%d comp_load=%d comp_ul=%d prev=%d\n", shm->request->load, shm->request->unload, shm->completion->load, shm->completion->unload, shm->previous_unload); uprintf("request queue is%s blocked\n", (shm->previous_unload == UL_NOT_BLOCKED ? " not" : "")); } #endif /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDARESET() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Reset of interface. Free mbufs if there is */ /* queued output data. */ /* */ /* Call: ddareset(unit, uban) */ /* Arguments: unit: ACP device unit number */ /* uban: Unibus adapter # (unused, but the */ /* kernel placed it on the stack) */ /* Returns: nothing */ /* Called by: network software, address of routine is */ /* defined in dda_if network interface struct */ /* Calls to: DDALOG() */ /* hist_link_state() */ /* hist_all_lcns() */ /* dda_shm_setup() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*ARGSUSED*/ ddareset(unit, uban) int unit; int uban; { DMESG(unit, 0, (DDALOG(LOG_ERR) "dda%d: reset\n", unit DDAELOG)); dda_unit_reset(unit, 1, SYSGEN_VALID); } PRIVATE void bufreset(unit) int unit; { DMESG(unit, 0, (DDALOG(LOG_ERR) "dda%d: buffer reset\n", unit DDAELOG)); dda_unit_reset(unit, 0, SYSGEN_VALID); } PRIVATE void dda_unit_reset(unit, doreset, drvr_state) int unit; int doreset; int drvr_state; { register struct uba_device *ui = ddainfo[unit]; register struct dda_softc *ds = &dda_softc[unit]; register struct dda_cb *dc; register int lcn; struct biic_regs *nxv; if (unit >= NDDA || (ui == 0 || ui->ui_alive == 0)) return; nxv = (struct biic_regs *) ui->ui_addr; ds->dda_if.if_flags &= ~IFF_UP; hist_link_state(unit, ds->dda_state, S_DISABLED); ds->dda_state = S_DISABLED; /* reset the board */ if (doreset) nxv->biic_ctrl = BICTRL_STS | BICTRL_SST; DELAY(50000); /* delay 50ms for BIIC to run tests */ ds->dda_flags = 0; /* clear ACP operational flag */ /* which will get us ready for B int */ ds->dda_init &= ~DDA_INTCLOCK; /* reset internal-clocking-set bit */ nddach[unit] = NDDACH_DEFAULT; /* reset SVC limit */ dc = ds->dda_cb; /* flush any queued output data */ /* LCNLINK */ for (lcn = 0; lcn <= NDDACH; lcn++) { /* for all LCN's ... */ dc->dc_inaddr.s_addr = 0; /* clear remote internet addr */ dc->dc_key.key_addr.s_addr = 0; dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; dc->dc_state = LC_IDLE; /* init LCN state */ dc->dc_timer = TMO_OFF; /* turn LCN timer off */ dc->dc_flags = 0; #ifdef DDADEBUG dc->dc_out_t = TMO_OFF; /* turn FE completion timer off */ #endif dc++; } hist_all_lcns(unit, LC_IDLE); #ifdef DDA_RAWOPT pi_init(unit, 1); #endif #ifdef DDA_PADOPT x29_init(unit, 1); #endif dda_shm_setup(unit, nxv, drvr_state); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAINIT() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine initializes the interface for operation. The */ /* device control blocks are initialized, UNIBUS resources are */ /* allocated and an initialization message is sent to the ACP. */ /* */ /* Note that interrupt "b" is enabled here to avoid a possible */ /* race condition at power up time - it was previously done in */ /* the probe and reset routines. */ /* */ /* Call: ddainit(unit) */ /* Argument: unit: ACP device unit number */ /* Returns: nothing */ /* Called by: network software, address of this routine is */ /* defined in dda_if network interface struct */ /* ddaioctl() */ /* ddaintb() */ /* Calls to: ddatimer() */ /* splimp() */ /* dda_rrq() */ /* splx() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ ddainit(unit) int unit; { register struct dda_softc *ds; register struct dda_cb *dc; register struct uba_device *ui; int lcn, s; ds = &dda_softc[unit]; ui = (struct uba_device *) ddainfo[unit]; #ifdef DDADEBUG if (DDADBCH(0, unit)) { DDALOG(LOG_DEBUG) "dda%d: ddainit()\n", unit DDAELOG; DDALOG(LOG_DEBUG) "dda%d: ds->dda_if.if_addrlist=0x%x ds->dda_flags=0x%x\n", unit, ds->dda_if.if_addrlist, ds->dda_flags DDAELOG; } #endif DDADEBUG /* if we have no internet addr if device not operational don't init yet */ if (ds->dda_if.if_addrlist == (struct ifaddr *) 0 || ((ds->dda_flags & DDAF_OK) == 0)) return; /* set default options */ ds->dda_init &= ~DDA_INTCLOCK; /* reset internal-clocking-set bit */ nddach[unit] = NDDACH_DEFAULT; /* reset SVC limit */ if ((ds->dda_if.if_flags & IFF_RUNNING) == 0) { dc = ds->dda_cb; /* setup ptr to first LCN cntl block */ /* LCNLINK */ for (lcn = 0; lcn <= NDDACH; lcn++) { /* for all LCN's ... */ dc->dc_lcn = lcn; /* record LCN */ dc->dc_inaddr.s_addr = 0; /* clear remote internet addr */ dc->dc_key.key_addr.s_addr = 0; dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; dc->dc_state = LC_DOWN; /* init LCN state */ dc->dc_timer = TMO_OFF; /* turn LCN timer off */ #ifdef DDADEBUG dc->dc_out_t = TMO_OFF; /* turn FE completion timer off */ #endif /* init LCN output queue */ s = splimp(); dc->dc_oq.ifq_head = (struct mbuf *) 0; dc->dc_oq.ifq_tail = (struct mbuf *) 0; splx(s); dc->dc_oq.ifq_len = 0; dc->dc_oq.ifq_maxlen = DDA_OQMAX; dc->dc_oq.ifq_drops = 0; /* init HDX channels */ dc->dc_rchan.hc_next = (struct hdx_chan *) 0; dc->dc_rchan.hc_chan = lcn * 2; dc->dc_wchan.hc_next = (struct hdx_chan *) 0; dc->dc_wchan.hc_chan = (lcn * 2) + 1; dc->dc_rchan.hc_mbuf = (struct mbuf *) 0; dc->dc_rchan.hc_curr = (struct mbuf *) 0; dc->dc_wchan.hc_mbuf = (struct mbuf *) 0; dc->dc_wchan.hc_curr = (struct mbuf *) 0; dc->dc_flags = 0; /* initialize flags */ dc++; /* point at next cntl blk */ } hist_all_lcns(unit, LC_DOWN); ds->dda_sioq.sq_head = (struct hdx_chan *) 0; ds->dda_sioq.sq_tail = (struct hdx_chan *) 0; ds->dda_if.if_flags |= IFF_RUNNING; } s = splimp(); dc = ds->dda_cb; /* setup ptr to first LCN cntl block */ for (lcn = 0; lcn <= nddach[unit]; lcn++) { /* issue reads on all LCNs */ dda_rrq(ds, &(dc->dc_rchan)); dc++; } splx(s); ddatimer(unit); /* start timers */ #ifdef DDA_RAWOPT pi_init(unit, 0); /* initialize progammer interface */ #endif #ifdef DDA_PADOPT x29_init(unit, 0); #endif } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAINTA() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This is the interrupt handler for I/O interrupts (interrupt */ /* "a") from the ACP device. The I/O mailboxes are scanned for */ /* handshake events to process. Two types of interrupts are */ /* processed: I/O request acknowledge, and I/O completion. */ /* The interrupting HDX channel and interrupt type are */ /* obtained. If interrupt is an I/O request acknowledge the */ /* next I/O request is passed to the device. If the interrupt */ /* is an I/O completion, check for errors, if ok process */ /* according to whether supervisory or data channel. */ /* */ /* Call: ddainta(unit) */ /* Arguments: unit: ACP device unit number */ /* Returns: nothing */ /* Called by: network software, address of this routine */ /* is defined in af_inet network interface */ /* data structure */ /* Calls to: DDALOG() */ /* start_chn() */ /* dda_data() */ /* dda_supr() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ ddainta(unit) int unit; { register struct dda_softc *ds; register struct hdx_chan *hc; register struct uba_device *ui; CQUEUE *complq; CENTRY *centry_ptr; byte flags; int chan, cc, subcc, cnt; SYSGEN_BLOCK *SysGenPtr; ds = &dda_softc[unit]; SysGenPtr = (SYSGEN_BLOCK *) ds->dda_mapreg; ui = (struct uba_device *) ddainfo[unit]; #ifdef DDADEBUG if (DDADBCH(5, unit)) { DDALOG(LOG_DEBUG) "dda%d: ddainta()\n", unit DDAELOG; } #endif DDADEBUG /***********************************************************************/ /* Check Request Queue if xmt was blocked */ /***********************************************************************/ /* was request queue blocked before? (previous unload = a blocked value) */ if (SysGenPtr->previous_unload != UL_NOT_BLOCKED) { /* yes, so check to see if unload pointer has changed */ if (SysGenPtr->previous_unload != SysGenPtr->request->unload) { /* if the unload pointer changed, we're no longer blocked */ SysGenPtr->previous_unload = UL_NOT_BLOCKED; start_chn(ds); /* re-start I/O */ } } /***********************************************************************/ /* Check I/O Completion Queue */ /***********************************************************************/ complq = SysGenPtr->completion; /* point to completion q */ centry_ptr = complq->entry + complq->unload; /* new entry valid? I/O Completion Mailbox */ while ((flags = centry_ptr->flags) & CENTRY_VALID) { #ifdef DDA_MSGQ dda_mqstr("(cx)"); #endif centry_ptr->flags = 0; /* clear valid bit */ /* Get logical channel information. */ chan = centry_ptr->dpn; if (chan > nddach[unit]) { DMESG(unit, 6, (DDALOG(LOG_ERR) "dda%d: unknown completion channel, lcn=%d\n", unit, chan DDAELOG)); goto bump_unload; } if (flags & FLAGS_DIR) hc = &(ds->dda_cb[chan].dc_wchan); /* write channel */ else hc = &(ds->dda_cb[chan].dc_rchan); /* read channel */ cc = centry_ptr->stat; /* Mailbox I/O completion status */ subcc = centry_ptr->sbstat; /* Mailbox I/O completion substatus */ cnt = centry_ptr->count; /* Mailbox I/O completion byte count */ #ifdef DDADEBUG if (DDADBCH(33, ds->dda_if.if_unit)) prt_bytes(ds->dda_if.if_unit, "incoming data", hc->hc_curr, cnt); #endif switch (cc) { /* check for unsuccessful I/O completion status */ case DDAIOCABT: /* probably VCN flush */ if (LOG_ABT) DDALOG(LOG_ERR) "dda%d: abort completed on chan %d\n", unit, hc->hc_chan DDAELOG; break; case DDAIOCERR: DMESG(unit, 7, (DDALOG(LOG_ERR) "dda%d: program error\n", unit DDAELOG)); goto daterr; case DDAIOCOVR: DMESG(unit, 8, (DDALOG(LOG_ERR) "dda%d: overrun error\n", unit DDAELOG)); goto daterr; case DDAIOCUBE: DMESG(unit, 9, (DDALOG(LOG_ERR) "dda%d: transfer count = 0\n", unit DDAELOG)); goto daterr; case DDAIODMAE: DMESG(unit, 10, (DDALOG(LOG_ERR) "dda%d: DMA completion error (%x)\n", unit, subcc DDAELOG)); goto daterr; case DDAIOLCOL: DMESG(unit, 11, (DDALOG(LOG_ERR) "dda%d: listen collision\n", unit DDAELOG)); goto daterr; case DDAIOFUNC: DMESG(unit, 12, (DDALOG(LOG_ERR) "dda%d: invalid function\n", unit DDAELOG)); goto daterr; case DDAIODPN: DMESG(unit, 13, (DDALOG(LOG_ERR) "dda%d: invalid dpn\n", unit DDAELOG)); goto daterr; daterr: DMESG(unit, 14, (DDALOG(LOG_ERR) "lcn=%d func=%x\n", chan, hc->hc_func DDAELOG)); if (hc->hc_func & DDARDB) ds->dda_if.if_ierrors++; else ds->dda_if.if_oerrors++; } /* was it supervisor or data traffic? */ if (chan > DDA_SUPR) { #ifdef DDA_PADOPT if (ds->dda_cb[chan].dc_flags & DC_X29) x29_data(ds, hc, cc, cnt, subcc); else #endif #ifdef DDA_RAWOPT if (ds->dda_cb[chan].dc_flags & DC_RAW) pi_data(ds, hc, cc, cnt, subcc); else #endif dda_data(ds, hc, cc, cnt); } else dda_supr(ds, hc, cc, cnt); bump_unload: /* Ack the interrupt. Fix the Mailbox Ready and Done bits: set DON bits, and clear RDY bits so mailbox may be reused. */ complq->unload = (complq->unload + 1) % CQSIZE; centry_ptr = complq->entry + complq->unload; } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDAINTB() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Service interrupt "b", system interrupt, from the ACP device. */ /* If the ACP device is operational, interrupt is unexpected, */ /* disable the device. If the interrupt indicates a powerup */ /* diagnostic failure, disable the device. Otherwise, set ACP */ /* flag for device operational, enable interrupt a, enable DMA, */ /* and perform initialization tasks. */ /* */ /* Call: ddaintb(unit) */ /* Argument: unit: DDA device unit number */ /* Returns: nothing */ /* Called by: network software, address of this routine is */ /* defined in af_inet network interface struct */ /* Calls to: DDALOG() */ /* ddainit() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ ddaintb(unit) int unit; { register struct dda_softc *ds = &dda_softc[unit]; register int lcn; register struct mbuf *m; register struct hdx_chan *hc; register struct dda_cb *dc; struct biic_regs *nxv = (struct biic_regs *) ddainfo[unit]->ui_addr; int stat_val; stat_val = ((GP_REG3_USAGE *) &nxv->biic_gpr3)->fe_diag_status; #ifdef DDADEBUG if (DDADBCH(6, unit)) { DDALOG(LOG_DEBUG) "dda%d: ddaintb()\n", unit DDAELOG; } #endif DDADEBUG if (ds->dda_flags & DDAF_OK) { DMESG(unit, 15, (DDALOG(LOG_ERR) "dda%d: asynchronous restart, status = %d\n", unit, stat_val DDAELOG)); ds->dda_flags = 0; ds->dda_if.if_flags &= ~(IFF_RUNNING | IFF_UP); hist_link_state(unit, ds->dda_state, S_DISABLED); ds->dda_state = S_DISABLED; ddareset(unit, 0); return; } if ((stat_val & DDASTAT_ERR) != 0) { DMESG(unit, 16, (DDALOG(LOG_ERR) "dda%d: Diagnostic failure = %d\n", unit, stat_val DDAELOG)); dda_disable(unit); } else if (stat_val == DDASTAT_NMC) { DMESG(unit, 17, (DDALOG(LOG_ERR) "dda%d: No Microcode Present!\n", unit DDAELOG)); dda_disable(unit); } else if (stat_val == DDASTAT_OK) { SYSGEN_BLOCK *SysGenPtr = (SYSGEN_BLOCK *) ds->dda_mapreg; ds->dda_flags |= DDAF_OK; /* no longer expecting a B interrupt */ for (lcn = 0; lcn <= NDDACH; lcn++) { /* for all LCNs */ dc = &dda_softc[unit].dda_cb[lcn]; while (dc->dc_oq.ifq_len) { /* clear output queue */ IF_DEQUEUE(&dc->dc_oq, m); if (m) m_freem(m); } hc = &dc->dc_rchan; if (hc->hc_mbuf) { m_freem(hc->hc_mbuf); /* free read mbufs */ hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } hc = &dc->dc_wchan; if (hc->hc_mbuf) { m_freem(hc->hc_mbuf); /* free write mbufs */ hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } } SysGenPtr->request->load = 0; SysGenPtr->request->unload = 0; SysGenPtr->completion->load = 0; SysGenPtr->completion->unload = 0; bzero(SysGenPtr->completion->entry, sizeof(CENTRY) * CQSIZE); bzero(SysGenPtr->request->entry, sizeof(RENTRY) * RQSIZE); SysGenPtr->pwr_action = 0xff; /* we are operational */ /* this 10ms delay is here to give the FEP enough time to re-program its interrupt vector to point to the A interrupt routine and reset the MFP interrupt counter before the driver posts the NDDACH reads in ddainit. It's a kludge, but keep it for safety */ DELAY(10000); /* delay 10 miliseconds */ ddainit(unit); } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDA_WRQ() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Process write requests. Put I/O request values in */ /* half-duplex control channel structure: set function code */ /* for write to ACP with Transfer Grant set. If there are no */ /* more mbufs in chain, mark DDAEOS for end of stream. Set */ /* count from data length (byte count) in mbuf, and subfunction */ /* is zero. If the COMREGs are busy, queue for start later. */ /* */ /* Call: dda_wrq(ds, hc, abt) */ /* Argument: ds: pointer to device control block struct */ /* hc: pointer to half-duplex channel cntl blk */ /* abt: indication of whether request is an */ /* abort request. */ /* Returns: nothing */ /* Called by: dda_start() */ /* dda_data() */ /* Calls to: mtod() */ /* start_chn() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void dda_wrq(ds, hc, abt) struct dda_softc *ds; register struct hdx_chan *hc; u_char abt; { register struct mbuf *m; register int s; if (abt) hc->hc_func = DDAABT; else hc->hc_func = DDAWRT; #ifdef DDADEBUG if (DDADBCH(15, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: dda_wrq: chan=%d func=%x\n", ds->dda_if.if_unit, hc->hc_chan, hc->hc_func DDAELOG; } #endif DDADEBUG s = splimp(); /* * If ACP comm regs busy, queue start i/o for later. */ if (ds->dda_sioq.sq_head) { (ds->dda_sioq.sq_tail)->hc_next = hc; ds->dda_sioq.sq_tail = hc; hc->hc_next = 0; splx(s); return; } /* start i/o on channel now */ ds->dda_sioq.sq_head = hc; ds->dda_sioq.sq_tail = hc; hc->hc_next = 0; splx(s); (void) start_chn(ds); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% DDA_RRQ() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Process read requests. Quit if attempt to get an mbuf is */ /* unsuccessful. Put I/O request values in half-duplex control */ /* channel structure: set function code for read from ACP with */ /* Transfer Grant set, set count from data length (byte count) */ /* in mbuf, and subfunction is zero. */ /* */ /* Call: dda_rrq(ds, hc) */ /* Argument: ds: pointer to device control block struct */ /* hc: pointer to half-duplex control chan */ /* Returns: nothing */ /* Called by: ddainit() */ /* dda_data() */ /* dda_supr() */ /* Calls to: MGET() */ /* MCLGET() */ /* DDALOG() */ /* start_chn() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void dda_rrq(ds, hc) struct dda_softc *ds; register struct hdx_chan *hc; { register struct mbuf *m; register int s; #if ACC_ULTRIX > 12 struct mbuf *p; #endif #ifdef DDADEBUG if (DDADBCH(16, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: dda_rrq()\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) { DMESG(ds->dda_if.if_unit, 25, (DDALOG(LOG_ERR) "dda%d: couldn't get buffer for read\n", ds->dda_if.if_unit DDAELOG)); return; } s = splimp(); /* hc_mbuf set to zero during initialization */ /* if hc->hc_mbuf is zero, then this is the first mbuf in the chain, so be conservative and only queue up a small mbuf */ if (hc->hc_mbuf == 0) { m->m_len = MLEN; /* set the size to a small mbuf */ hc->hc_mbuf = m; hc->hc_curr = m; /* if it's not the first mbuf in the chain, we may be running a FTP or something that deserves higher performance, so queue a cluster */ } else { #if ACC_ULTRIX > 12 MCLGET(m, p); /* associate a page cluster with this mbuf */ #else MCLGET(m); #endif hc->hc_curr->m_next = m; hc->hc_curr = m; m->m_next = 0; } splx(s); hc->hc_func = DDARDB + DDASTR; hc->hc_sbfc = 0; s = splimp(); /* * If ACP comm regs busy, queue start i/o for later. */ if (ds->dda_sioq.sq_head) { (ds->dda_sioq.sq_tail)->hc_next = hc; ds->dda_sioq.sq_tail = hc; hc->hc_next = 0; splx(s); return; } /* start i/o on channel now */ ds->dda_sioq.sq_head = hc; ds->dda_sioq.sq_tail = hc; hc->hc_next = 0; splx(s); (void) start_chn(ds); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% START_CHN() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine copies ACP I/O requests into the ACP */ /* Communications Registers (COMREGs) and notifies the ACP. */ /* If the channel number is odd, indicating write, then the */ /* direction flag is set to indicate a transfer from the host */ /* to the front end. */ /* */ /* Call: start_chn(ds) */ /* Argument: ds: pointer to device control block struct */ /* Returns: nothing */ /* Called by: ddainta() */ /* dda_rrq() */ /* dda_wrq() */ /* Calls to: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE int start_chn(ds) struct dda_softc *ds; { register struct hdx_chan *hc; register int s; struct uba_device *ui; struct biic_regs *nxv; RQUEUE *requestq; short next_load; SYSGEN_BLOCK *SysGenPtr; SysGenPtr = (SYSGEN_BLOCK *) (ds->dda_mapreg); ui = (struct uba_device *) (ddainfo[ds->dda_if.if_unit]); nxv = (struct biic_regs *) ui->ui_addr; #ifdef DDADEBUG if (DDADBCH(17, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: start_chn()\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG s = splimp(); while (hc = (struct hdx_chan *) (ds->dda_sioq.sq_head)) { requestq = SysGenPtr->request; next_load = (requestq->load + 1) % RQSIZE; if (next_load == requestq->unload) { /* any room left in q? */ /* no, say we're blocked */ SysGenPtr->previous_unload = requestq->unload; break; } /* * Check sioq for invalid writes */ /* if write channel, but not supervisor, and it's marked invalid, then either drop or abort it */ if ((hc->hc_chan & 0x01) && (hc->hc_chan != 1) && (hc->hc_inv & INVALID_MBUF)) { if (ds->dda_cb[hc->hc_chan >> 1].dc_flags & DC_OBUSY) { #ifdef DDADEBUG if (DDADBCH(28, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d: start_chn: aborting chan %d\n", ds->dda_if.if_unit, hc->hc_chan DDAELOG; } #endif DDADEBUG goto send; /* send an abort if output not complete */ } else { /* otherwise free it */ if (hc->hc_mbuf) { /* free pending request */ m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } /* * Restart Output */ hc->hc_inv &= ~INVALID_MBUF; ds->dda_sioq.sq_head = ds->dda_sioq.sq_head->hc_next; /* The following call may cause a re-entrant call to start_chn, but only if the sioq is currently empty. In that case, the re-entrant call is ok */ dda_start(ds, &ds->dda_cb[hc->hc_chan >> 1]); continue; } } send: { /* Read or Write request is valid */ RENTRY *rentry_ptr; int index; /* Set up queue element */ rentry_ptr = requestq->entry + requestq->load; /* Fill the slots starting at hc->hc_curr */ rentry_ptr->count = 0; index = 0; #ifdef DDADEBUG if (DDADBCH(33, ds->dda_if.if_unit)) { struct mbuf *m; int i; DDALOG(LOG_DEBUG) "hc->hc_mbuf=0x%x hc->hc_curr=0x%x\n", hc->hc_mbuf, hc->hc_curr DDAELOG; for (i = 0, m = hc->hc_mbuf; m && m != hc->hc_curr; i++, m = m->m_next) DDALOG(LOG_DEBUG) "previous(%d): 0x%x\n", i, m DDAELOG; for (i = 0, m = hc->hc_curr; m; i++, m = m->m_next) DDALOG(LOG_DEBUG) "chain(%d): 0x%x\n", i, m DDAELOG; } #endif DDADEBUG /* if it's a write, map as many mbufs on as we can */ if (hc->hc_chan & 1) { register struct mbuf *last_mapped; while (hc->hc_curr) { register int len, seglen, pages; register char *addr; addr = mtod(hc->hc_curr, char *); len = hc->hc_curr->m_len; pages = (len - 1 >> PGSHIFT) + 1; if (len == 0) printf("start_chn: len was zero?!?\n"); /* If we have enough slots left in this entry to map this mbuf, then do so */ if (NUM_BI_ADDR - index > pages) { while ((seglen = MIN(NBPG, len)) > 0) { rentry_ptr->BI_address[index] = svtophy(addr); rentry_ptr->BI_count[index] = seglen; rentry_ptr->count += seglen; len -= seglen; addr += seglen; index++; } /* we've mapped this mbuf onto the queue, deal with next */ last_mapped = hc->hc_curr; hc->hc_curr = hc->hc_curr->m_next; } else { /* if this buffer can't ever fit, there is a major problem (someone changed the size of a large mbuf so it can't fit in NUM_BI_ADDR memory pages). This should never happen. */ if (index == 0) panic("dda driver: mbuf too large to map\n"); break; } } /* if more mbufs to map, we're not at the end of stream */ if (hc->hc_curr) hc->hc_func |= DDASTR; else hc->hc_func |= DDAEOS; /* higher up routines expect hc->hc_curr to be the last mapped mbuf */ hc->hc_curr = last_mapped; } else { /* otherwise it's a read, so process the one mbuf */ register int len, seglen, pages; register char *addr; addr = mtod(hc->hc_curr, char *); len = hc->hc_curr->m_len; pages = (len - 1 >> PGSHIFT) + 1; /* if it's a read, zero out the mbuf's m_len field because the mbuf currently contains no valid data */ hc->hc_curr->m_len = 0; if (len == 0) printf("start_chn: len was zero?!?\n"); /* If we have enough slots left in this entry to map this mbuf, then do so */ if (NUM_BI_ADDR >= pages) while ((seglen = MIN(NBPG, len)) > 0) { rentry_ptr->BI_address[index] = svtophy(addr); rentry_ptr->BI_count[index] = seglen; rentry_ptr->count += seglen; len -= seglen; addr += seglen; index++; } /* if this buffer can't ever fit, there is a major problem (someone changed the size of a large mbuf so it can't fit in NUM_BI_ADDR memory pages). This should never happen. */ else panic("dda driver: mbuf too large to map\n"); } rentry_ptr->opcode = CMD_MPCPRQS; rentry_ptr->dpn = hc->hc_chan >> 1; rentry_ptr->func = hc->hc_func; rentry_ptr->sbfunc = hc->hc_sbfc; #ifdef DDADEBUG if (DDADBCH(34, ds->dda_if.if_unit)) { int i; DDALOG(LOG_DEBUG) "dpn=0x%x func=0x%x sbfunc=0x%x count=0x%x index=%d\n", rentry_ptr->dpn, rentry_ptr->func, rentry_ptr->sbfunc, rentry_ptr->count, index DDAELOG; for (i = 0; i < NUM_BI_ADDR; i++) DDALOG(LOG_DEBUG) "addr[%d]=0x%x count[%d]=0x%x\n", rentry_ptr->BI_address[i], rentry_ptr->BI_count[i] DDAELOG; } #endif DDADEBUG #ifdef DDADEBUG if (hc->hc_chan & 1) { /* write */ struct dda_cb *dc; dc = &ds->dda_cb[hc->hc_chan >> 1]; if (DDADBCH(17, ds->dda_if.if_unit)) DDALOG(LOG_DEBUG) "dda%d: start_chn: WRITE on lcn %d func %x\n", ds->dda_if.if_unit, hc->hc_chan >> 1, hc->hc_func DDAELOG; if (dc->dc_lcn) /* don't start timer on lcn 0 */ dc->dc_out_t = TMO_RESTART; /* Wait 90 sec for completion */ } if (DDADBCH(30, ds->dda_if.if_unit)) DDALOG(LOG_DEBUG) "dda%d: interrupting FE\n", ds->dda_if.if_unit DDAELOG; #endif DDADEBUG /* TELL 7000 about new entry in request queue */ requestq->load = next_load; /* boom! */ nxv->biic_gpr0 = SYSGEN_VALID; /* interrupt FE */ #ifdef SIMULATION MFP_COUNTER_SIM(); /* simulate hardware action */ #endif } next_sioq_element: ds->dda_sioq.sq_head = ds->dda_sioq.sq_head->hc_next; } splx(s); return; } /* * disable interrupts and forget about unit */ PRIVATE void dda_disable(unit) int unit; { struct uba_device *ui = ddainfo[unit]; struct biic_regs *nxv; nxv = (struct biic_regs *) ui->ui_addr; nxv->biic_int_dst = 0; } PRIVATE int dda_dload(unit, dl) int unit; struct dda_dnload *dl; { register struct uba_device *ui = ddainfo[unit]; register int i; static unsigned char *dlbuf[RQSIZE]; if (unit >= NDDA) return EFAULT; switch (dl->lcommand) { case DN_LCMD_SETUP: /* allocate buffers, reset board/setup shared memory */ for (i = 0; i < RQSIZE; i++) { KM_ALLOC(dlbuf[i], unsigned char *, 512, KM_CLUSTER, KM_NOWAIT); if (dlbuf[i] == NULL) return ENOMEM; } dda_unit_reset(unit, 1, SYSGEN_DLOAD); DMESG(unit, 0, (DDALOG(LOG_ERR) "dda%d: download mode\n", unit DDAELOG)); return 0; case DN_LCMD_CLEANUP: /* deallocate buffers and restore shm to normal */ for (i = 0; i < RQSIZE; i++) if (dlbuf[i]) KM_FREE(dlbuf[i], KM_CLUSTER); dda_shm_setup(unit, (struct biic_regs *) ui->ui_addr, SYSGEN_VALID); DMESG(unit, 0, (DDALOG(LOG_ERR) "dda%d: operational mode\n", unit DDAELOG)); /* now we will expect a B interrupt from the unit which will cause ddainit to be called and set up the driver */ return 0; case DN_LCMD_FEOP: { /* perform a front end operation */ struct dda_softc *ds = &dda_softc[unit]; struct biic_regs *nxv = (struct biic_regs *) ui->ui_addr; register RQUEUE *requestq; register RENTRY *rentry_ptr; register unsigned checksum; register unsigned char *buffer; short next_load; requestq = ((SYSGEN_BLOCK *) ds->dda_mapreg)->request; next_load = (requestq->load + 1) % RQSIZE; if (next_load == requestq->unload) { /* any room left in q? */ /* no, say we're blocked */ return ENOMEM; } rentry_ptr = requestq->entry + requestq->load; switch (dl->type) { case DN_TYPE_DATA: case DN_TYPE_ID: case DN_TYPE_VER: case DN_TYPE_COPY: #ifdef DDADEBUG if (DDADBCH(29, unit)) DDALOG(LOG_DEBUG) "dda%d: xfr type=%d buf=%d len=%d dest=0x%x\n", unit, dl->type, requestq->load, dl->len, dl->dest DDAELOG; #endif DDADEBUG buffer = dlbuf[requestq->load]; if (copyin(dl->data, buffer, dl->len)) return EFAULT; /* Set up queue element */ rentry_ptr->opcode = dl->type; rentry_ptr->count = dl->len; rentry_ptr->BI_address[0] = svtophy(buffer); rentry_ptr->BI_address[1] = dl->dest; for (checksum = 0, i = dl->len; i--; ) checksum += *buffer++; rentry_ptr->BI_address[2] = checksum; rentry_ptr->BI_address[2] = 0; break; case DN_TYPE_XFR: #ifdef DDADEBUG if (DDADBCH(29, unit)) DDALOG(LOG_DEBUG) "dda%d: execute buf=%d addr=0x%x\n", unit, requestq->load, dl->dest DDAELOG; #endif DDADEBUG rentry_ptr->opcode = dl->type; rentry_ptr->BI_address[1] = dl->dest; break; } /* TELL 7000 about new entry in request queue */ requestq->load = next_load; /* boom! */ nxv->biic_gpr0 = SYSGEN_DLOAD; /* interrupt FE */ #ifdef SIMULATION MFP_COUNTER_SIM(); /* simulate hardware action */ #endif return 0; } default: return EFAULT; } } /* Revision history: 30-Nov-1989 Paul Traina First cut for beta release. */ c->hc_func DDAELOG; if (dc->dc_lcn) /* don't start timer on lcn 0 */ dc->dc_out_t = TMO_RESTART; /* Wait 90 sec for completion */ } if (DDADBCH(30, ds->dda_if.if_unitdriver/README 644 540 24 760 4535574376 6264 Some of the files in this directory: if_dda.c -- the main driver module if_dda_uqbus.c -- included module for ACP5250/ACP6250 boards if_dda_bibus.c -- included module for ACP7250 only if_ddareg.h -- dda register descriptions if_ddavar.h -- shared data between acpconfig and the driver if_pi.c -- programmer's interface option if_pivar.h -- include file for PI for pre-4.3+tahoe systems if_pivar.h.tahoe -- include file for PI for 4.3+tahoe and 4.4bsd if_x29.c -- X29 interface option bout new entry i */ /* */ /* File: if_dda_bibus.c */ /* BI bus support routines for dda */ /* */ /* Project: DDN-X.25 Network Interface Driver for ACP 7250 */ /* */ /* revision history at the end of if_dda.c */ /*************************************************************************/ #include "../vaxif/if_uba.h" #include "../vaxuba/ubavar.h" #include "../vaxbi/bireg.h" #include "../vaxif/if_ddabique.h" #ifdef SIMULATION #define KM_CLUSTER 28 #define KM_NOWAIT 1 #define KM_ALLOC(space, cast, size, type, flags) { \ (space) = (cast)malloc(size); \ } #define KM_FREE(addr, type) { \ (void)free((caddr_t)(addr)); \ } #undef svtophy #define svtophy(x) ((int)(x)) #endif /* * private functions in this module */ PRIVATE void