Designing an OSI Test Bed

Ken and Michael discuss the synchronous communications device drivers that play a key part in the Open Systems Interconnection (OSI) test bed they helped design and implement.


December 01, 1990
URL:http://www.drdobbs.com/architecture-and-design/designing-an-osi-test-bed/184408457

Figure 1

Figure 2

Figure 3

DEC90: DESIGNING AN OSI TEST BED

DESIGNING AN OSI TEST BED

Synchronous communications device drivers are critical to success

Kenneth L. Crocker and Michael T. Thompson

Ken is a member of the technical staff at The MITRE Corp. and Michael is a senior software engineer at Planning Systems Inc. They can be reached at 7525 Colshire Dr., Mailstop W389, McLean, VA 22102. Ken and Michael can also be reached via e-mail at [email protected] and [email protected], respectively.


As of August 1990, new government procurements must comply with the Government Open Systems Interconnection Profile (GOSIP, see Federal Information Processing Standard 146). One outgrowth of this requirement will certainly be accelerated development efforts for portable OSI applications, like the PC-based OSI protocol test bed we describe in this article.

This test bed utilizes commercially available, portable source code to provide a Class Four Transport over X.25 Wide Area Network (WAN) implementation. The Class Four transport service provides connection-oriented, end-to-end service regardless of the underlying network services. The test bed software consists of approximately 42,000 lines of portable C source code purchased from Retix, 8000 lines of C developed to emulate an FAA weather data transfer application, and 200 lines of C to interface the Intel 82530 communications hardware to the Retix software. In particular, we'll focus on developing the synchronous communications device drivers that enable the Retix software to communicate with the 82530 hardware.

When designing the hardware-level device driver, you must address: 1. The amount of time allocated to input/output (I/O) routines based upon the highest expected data rate; 2. Timing requirements unique to the communications hardware; and 3. The interface to the portable code. The information presented in this article can be used as an example of how to design an interface to the Intel 82530 controller, as well as an example of overall system design when using portable communications software.

Software and Hardware Platform

The test bed itself is a DOS-based system using Microsoft C 5.1 and Microsoft Assembler (MASM) 5.0 as the development environment. A non-Unix operating system was chosen to better study portability issues. Figure 1 shows the communications and software architecture currently implemented on the test bed. As shown in the figure, Class Four Transport, X.25 packet layer protocol (PLP), and Link Access Procedure Balanced (LAPB) were purchased as portable source code from Retix. Operating as a transport user, emulations of the FAA Data Link Processor (DLP) and Weather Message Switching Center Replacement (WMSCR) provide realistic FAA weather data to demonstrate the OSI protocols. The device driver forms the interface between the Intel 82530 Serial Communications Controller (SCC) and the lower interface of the Retix LAPB module. The SCC is implemented on an IBM PC/XT-compatible communications peripheral purchased from Sealevel Systems. In our implementation, the 82530 provides limited link-layer functions (for example, framing and error checking) with RS-232 drivers provided by the Sealevel Systems physical interface. The current end-state hardware platform of the test bed is the IBM PC/XT 286. The XT 286 operates at a clock speed of 6 MHz with zero wait states. As a development platform, the Compaq 386/33 is used in conjunction with a software in-circuit emulation package. The timing analysis explained in this article is based upon the XT 286 platform.

Device Driver Timing Requirements

Unix, a multiuser, multitasking operating system (OS), was the OS Retix chose to develop its portable OSI software products in. MS-DOS, a single-tasking OS, allows one program or process to run at any given time. This means that the device driver can either move data via software polling or hardware interrupts. In polling, the device driver routines are placed inside what is essentially an infinite loop. The polling routine is then executed during each pass through the loop; however, every instruction that is added to the polling code, such as program diagnostics, increases the total loop execution time, which means that the communications hardware is interrogated less often. Conversely, interrupt- or event-driven communications alert the processor whenever data has been received or the transmit buffer is ready to accept more data. The interrupt-driven solution gives program control to the device driver whenever it is required, unlike the polling method, where program control is gained on a per-loop basis. The system described in this article uses a combination of polled and interrupt-driven I/O. The upper layer portable code is polled to check a series of event queues, while the receive and transmit data are handled via interrupts. The design of the interrupt handler for this interface must account for the following timing considerations: 1. The overall time to execute the interrupt routine must allow for non-interrupt processing to occur during data transfer; 2. The cycle and reset recovery times of the SCC must be met; and 3. The timing window of the SCC for reading and writing data must also be met.

Because the 9600 bit-per-second (bps) inbound data will generate a receive interrupt every 104 microseconds (mus) in the test bed environment, the SCC is programmed, as will be described later, to generate an interrupt for every 8 bits of received data and whenever the transmit buffer is empty. The receive interrupt period is derived as:

period of receive interrupt = (no. bits per character from SCC) / (transmission rate in bps)

The 104-s time span translates into approximately 624 timing, or clock, states for the 6-MHz XT 286. The 80286 can process approximately 100 assembler instructions in that time frame, assuming an average of six timing states per instruction for the types of instructions used in the device driver (for example, push, pop, mov, test, in, out) and ignoring other pending interrupts, system calls, and so on. For efficiency, the interrupt routine that loads the incoming data into the communication stack buffer manager should not occupy more than 25 percent of the total interrupt period. In a true full-duplex environment, one-half of the total processing time would be spent handling I/O while the other half would be used by application and communications stack-processing needs. Development in the 6-MHz XT-286 environment has shown that a faster processor is required for this type of interface. At 6 MHz, our interrupt routines can take up to a full character time (or 104 ,s) to execute. This is due to the buffer management requirements imposed by the portable software design and the required handshaking with the communications hardware. In our prototype environment, we control the overall throughput of the system by inserting delay to limit the transmit frame rate at the transport layer. In this manner we are able to adjust the transmit throughput at the link layer to accommodate our specific needs. Because LAPB is a windowing, asynchronous, acknowledgment-based protocol, when system A is transmitting a frame to system B, system B will wait until a set number of frames have been correctly received before acknowledging the window to system A. Once a link-layer connection has been established, normal communications continue in this manner.

Assuming a frame size of 512 bytes, this will yield 53 milliseconds (ms) of interrupt processing time and 947 ms to process the frame at the upper layers. For proper throughput efficiency, either a faster processor or a more stream-lined interface to the portable code will be required; however, we have been able to successfully interoperate with hardware and software manufactured by different vendors using this processing method.

The second set of timing considerations of the device driver are the cycle and reset recovery times of the SCC. The cycle recovery time applies to the time period between any read or write cycles to the SCC. This time period is defined to be six Processor Clock (PCLK) cycles. The reset recovery time applies to the time required to perform a software or hardware reset. The local PCLK on the Sealevel Systems board operates at 4.9152 MHz. This implies a cycle recovery time of 1.22 s and a reset recovery time of 2.24 s. To ensure that this critical timing requirement is met, the resulting assembler output from the C compiler must be analyzed. In sections where the cycle and reset recovery times are not met, dummy code (for example, nop,jmp$+2) is inserted to achieve the proper delay. The modified assembler code is then compiled using Microsoft MASM 5.0.

The third device driver timing consideration is the maximum time allowed to transfer data to and from the chip. Synchronous communication transfers data in a steady stream. Unlike asynchronous protocols, which use start and stop bits to frame each character, synchronous data is transferred in streams of 128, 256, and greater bytes of data. These data streams, or frames, are separated by flags. Because each byte of data must be transmitted in succession with the previous byte, the transmit routine must place the outbound data into the SCC before the previous byte has been completely transmitted. This timing requirement is imposed by the internal architecture of the 82530. The architecture of the controller and the required interface software are discussed in following sections.

Hardware Architecture and Initialization

The SCC has two independent full-duplex channels with 14 write registers and 7 read registers per channel. All modes of communications are established by the bit values sent to the write registers when the system is initialized. As data is received or transmitted, the read register values will change according to the mode programmed in the original setup. These changes to the read registers can cause hardware or software actions that lead to changes to other registers or other software or hardware reactions.

The SCC also utilizes a 3-byte first-in-first-out (FIFO) receive queue and a 20-bit transmit shift register which is loaded by a 1-byte transmit data buffer. The user of the SCC communicates with both mechanisms via a 1-byte I/O buffer. For synchronous communications, the transmit shift register must always contain data during a frame transmission. If a transmit underrun occurs, the SCC appends the 2-byte checksum field and flag information to the data stream. The receiving side of this data transfer will then see an incomplete link-layer data frame, resulting in a link-layer reject and possibly network-layer error recovery. If the 3-byte receive queue overflows, then receive data is lost, again causing link-layer rejects to occur. As stated in the previous section, the hardware driver must assure that the transmit shift register always contains data when sending a frame and that the receive queue is maintained at a nearempty level.

The initialization of the SCC for interrupt-driven synchronous communications is divided into three parts. Each part is unique to the initialization process and must follow the proper initialization sequence for correct operation of the SCC. The proper initialization sequence is the most critical part of the initialization process. As we learned, the SCC can appear to operate correctly, but not be capable of interoperating with other communications hardware. In our case, the two back-to-back SCCs on our test bed were moving data without errors, yet when we tried to communicate across a packet switch, every frame was rejected because of a bad checksum. The transmitter and receiver were inverting the checksum due to an out-of-sequence, but otherwise correct, initialization process.

During early debugging and experimentation with the device driver, we inserted an initialization sequence that corrected our current problem and caused another problem that did not surface until we introduced a different system, such as the packet switch. The initialization of the SCC must strictly follow the sequence explained in the technical manual, and any "debugging" changes should be checked against this sequence to avoid side effects similar to what we encountered.

The first part of the initialization sequence consists of programming the operation modes of the SCC such as bits-per-character and time constants. As you can see in the source code listings, we begin by forcing a hardware reset. The Synchronous Data Link Control (SDLC) mode of operation is then selected. The SCC actually implements a small subset of the SDLC protocol. This subset, in conjunction with the LAPB functions provided by the Retix link-layer software, meets the link-layer requirements of the test bed. The receive and transmit character lengths are then set to 8 bits each, and 7Eh is selected as the flag pattern to be sent between frames. The initial checksum value is set to FFh (required for proper SDLC operation), the RTxC pin of the 82530 is selected as the source for DTE clocking, and the baud-rate time constant is loaded during the final initialization steps of this phase of SCC initialization.

The second phase of SCC initialization consists of enabling the baud-rate generator to provide transmit clock and enabling the transmitter and receiver. During each subsequent phase of initialization, the previously set values (such as transmit and receive character length) must again be set during each use of a particular write register. As an example, write register 3 is used in this section to enable the receiver, yet all previously programmed information for write register 3 must be also be included. This phase of initialization is completed by enabling the transmit checksum generator, transmit interrupts, and receive interrupts.

Part three consists of enabling the different interrupts used by the system. In our implementation, the transmit underrun interrupt is enabled and all previously selected interrupts are enabled. This completes the initialization of the SCC. For further initialization information, the reader should consult the 82530 technical reference manual. The remainder of this article describes the software required to interface the Retix LAPB portable software to the SCC, as well as general information concerning the implementation of portable communications software.

Data Buffers Initialization

At the start of the initialization process, a large block of memory is assigned to the system buffer pool so that data buffers can be allocated for both the transmit and receive routines. Some of those buffers are preallocated for use by the receive routine only and put into the Baddr array for later use by the receiver. The remaining buffer pool can then be allocated and deallocated to buffers as needed by the transmit routine. After the buffer pool is created, the init_rcvbuf() routine in x251t.c (see Listing One, page 92) is used to fill the receive buffer array with the pointers to the allocated buffers.

Preallocation of the receive buffers is necessary in the synchronous, interrupt-driven environment. Buffers must be allocated when the interrupts are enabled so that if no buffer is currently available, the allocation routine can loop until one becomes available. If interrupts were disabled during this period, no buffers would ever become available -- none could ever be freed without the transfer of data.

Receiver

After the system has preallocated the first buffer in the Baddr array, the first buffer is then available to be written to by the interrupt receiver routine as soon as the interrupts are enabled. As each new character is received, the pointer to the next memory location is incremented and the character is stored in the buffer. When the last character of the frame is received, the frame is checked for a valid checksum. If the checksum is invalid, the software performs an error reset and discards the bad frame by resetting the character counter to zero. If the frame is valid, the MDATind (listed as upmidi in the code; see Listing Two, page 93) routine is called and the new buffer pointer is put on the Retix queue for further processing by the portable software. After the buffer pointer is added to the incoming data queue, we get the address of the next available buffer from the Baddr array and set the proper pointers so that it is now available for new incoming data. The data receiver routine has a higher priority than the transmitter routine; therefore, at the end of the receiver routine we check to see if a transmit interrupt is pending. If so, that character is processed before leaving the interrupt routine.

Transmitter

On the transmit side, when a buffer is first sent to the MDATreq routine (see Listing Two), we check to see if another frame is currently being transmitted. If one is, we return to the calling routine and try again later. If no frame is being transmitted, a test is made to see if the SCC transmit buffer is empty. If it is, the pointers and counters are set and the first character is sent. At that point, we confirm the buffer and start the end of frame processor. The transmit buffer is again checked to see if it is empty and when it is we send the second character. The interrupt driver will continue to send the remaining data until the last byte of the frame is delivered along with the frame's checksum data. The reason two characters are sent instead of one is because the amount of processing that must be done after the first character is sent does not allow us to return from this routine before the transmit buffer of the 82530 chip empties. The transmit underrun condition will result in a false end-of-frame and checksum generation. We essentially "prime" the SCC transmit shift register with enough data to allow the driver to stay ahead of the SCC transmitter during the first few bytes of frame transmission.

System Polling Routine

As we discussed earlier, the system employs a combination polled and interrupt-driven environment. The portable software is executed out of a polling loop and is driven by the data from the SCC. After the hardware and buffer initialization have been completed, the polldat() routine (see Listing Three, page 92) manages the preallocated, receiver buffer array. As a buffer is used by the receiver routine, the pointer in the array is set to NULL and the buffer array counter, rbufcnt, is decremented by one. If the buffer counter ever goes below four, then the test in poldat() will be true. Each NULL pointer in the Baddr array is then given a new buffer address. This allows the receiver routine to always have a fresh supply of buffer pointers. Buffer management is depicted in Figure 2.

More Details.

Interoperability Results

Our test bed has been used to prototype several query/response applications that run over the transport layer and, in another environment, the network layer. The applications include weather database transfer and maintenance messaging. Furthermore, we have been able to successfully interoperate with a VAX utilizing DEC OSI software as well as a Tandem computer implementing Tandem X.25. Successful tests have also been conducted over a small packet switch with two PCs functioning as the end-systems. The PC-based system has to provide the flexibility to interoperate in a heterogeneous hardware and software environment.

Time, Skill, and Resource Requirements

Development of the four-layer system and application, including learning curve, required approximately 15 staff months. The staff working on a project of this nature should possess layered protocol, real-time software development, and testing knowledge. This type of project requires the same hardware and software tools as any real-time project. At a minimum, a logic analyzer, oscilloscope, in-circuit emulator, and protocol analyzer with simulation capability should be available. Code-level debuggers (such as Microsoft Code-View) are useful in debugging the application code; however, they do interfere with the individual protocol timers of each layer and the timing requirements of moving data up and down the communications stack. Software in-circuit emulators that utilize the protected mode of the 80386 and allow you to run the test application as a virtual machine (for example, Nu-Mega SoftIce) are very useful. If an 80386-based machine is available, this is a cost-effective substitute for a hardware in-circuit emulator.

Recommended Development Process

Development of the test bed has shown that the device driver is the most critical part of the system design. The device driver should be designed, tested, and tuned before the upper layers are added. Most likely, vendor-unique management interfaces will need to be implemented with the device driver. The device driver can be written in the native language of the portable code to ease the interface to the link layer, and the assembler output of your compiler should be analyzed to determine if further optimization is necessary. In our case, Microsoft C 5.1 (compiling for execution speed optimization) produced tight assembler code that required little hand-tuning outside of that necessary to meet the 82530 cycle and reset times. Once the device driver and management routines have been developed, work can then progress on porting the upper layers. Finally, the application can be written, but the timing, polling, and interrupt activity must be considered at every phase of the system debugging.

Conclusions

The field of OSI software is still young, and technical risks will continue to decrease as the field matures. The test bed development effort has shown that at least the lower four layers can be implemented today using commercially available portable source code. Our work has confirmed that it is much easier to implement portable source code than to redevelop it for a particular system. However, the use of portable source code is not just a "compile-and-go" situation, since it is essentially a large real-time system driven by the data of another OSI end-system or relay-system.

Acknowledgments

The authors wish to thank Michael McGurrin, group leader, and Dr. Paul T. R. Wang, lead engineer, both of The MITRE Corporation, for their efforts in developing the test bed.

The OSI Reference Model

A system can be "closed," whereby components are not interchangable with another supplier's equipment (or alternate technology) without affecting other parts of the system; or, using well-defined, publicly available interface standards that treat components as black-boxes, the system can be "open." The advantage of an open system is its access to multivendor systems and growth without requiring totally new systems.

The International Organization for Standardization's (ISO) Open Systems Interconnection (OSI) Reference Model is an open system approach for communications interfaces. As illustrated in Figure 3, standardization reduces the number of unique interfaces across a network. In the example, four closed systems require 12 unique interfaces to communicate with each other. With a standard set of rules and procedures, like OSI, only four unique OSI-to-native environment interfaces are required. This approach reduces the cost of system development, procurement, support, and maintenance by eliminating the need to develop, debug, and maintain custom interfaces.

The OSI reference model is a framework divided into seven data communications function layers: physical, data link, network, transport, session, presentation, and application. The OSI architecture presents few new ideas to data communications: It is just a way of dividing the functions required for data communications into manageable and understandable subsets. Communications systems designed without using the OSI Reference Model contain many of the same functions as OSI-based systems; these functions, however, are generally lumped together into several undefined layers. OSI provides primitives, a minimum, well-defined flow of information between the layers. The implementation of these primitives is unique to a system, but the information passed with these primitives is standard across all systems. This modular approach to communications eases software maintenance since it lends itself to the development of modular, maintainable code.

Within each function layer, several international standard protocols exist for implementation within the realm of the OSI Reference Model. GOSIP (the U.S. Government OSI Profile, see Federal Information Processing Standard 146) further defines the reference model into sets of protocols (or profiles) that will be used for government data communications systems. The OSI suite can be viewed as providing transport and application services. The transport services (physical, data link, network, transport) provide communications-related functions (such as network routing and guaranteed delivery of data). The application services (session, presentation, and application) provide a toolkit of features for the applications programmer, so you do not need to know the details of data communications to write an application.

For example, if you need to write a word processor that includes file transfer capabilities, you would design the word processor to use the the File, Transfer, Access, and Management application-layer protocol (see ISO 8571). Using the standard application-layer protocol, the basics of file transfer, such as the use of synchronization points for roll-back and management of the virtual filestore, would be handled in the application-layer protocol, not the word processor. This layering of functions is the basis for the OSI Reference Model. If you need to design a special application not covered by the standard application-layer protocols, you could use the other application services features (like data translation and remote operation support) without having to redevelop these functions. --K.C., M.T.

_DESIGNING AN OSI TEST BED_ by Kenneth L. Crocker and Michael T. Thompson

[LISTING ONE]



/*########## INITIALIZE INTERRUPTS, X.25 & TRANSPORT LAYER DATA #########*/
/* AUTHOR: Michael T. Thompson, Planning Systems Inc. */
/*          2-16-89 for Mitre Corp. (W85)  */

/* initialize interrupt routines */

#include <stdio.h>   /* MicroSoft "C" 5.1 " */
#include <dos.h>   /* MS-DOS 3.30 */
#include <ctype.h>

extern interrupt far clock();   /* Retix clock routine */
extern interrupt far rcvdata();   /* Interrupt interface routine */
extern buf_type bfh_head;
extern unsigned char *rcvdat;  /* Common Receiver buffer pointer varible */
extern buf_type rcvbuf;          /* Receiver User Data buffer pointer */
extern int icnt, rbufcnt;      /* Number of pointers not used in Baddr Array */
extern buf_type Baddr[];       /* Array of buffer pointers */

int xbufsiz=256;        /* Standard Buffer size */
int number_of_buffers=30;    /* Total number of transmit and receive buffers */

/* ----- Primary system Initialization routine ----- */
x25_init()
{
int i, tbufsiz, result;      /* Local varibles */
tbufsiz = xbufsiz + 30;      /* Buffer size plus header data */

   _disable();      /* disable interrupts while initializing */
   init_memory();       /* get available memory from DOS */
   init_bufpool(&bpool,tbufsiz,number_of_buffers); /* setup buffer pool */
   init_timers();      /* initialize system timers */
   init_rcvbuf();      /* initialize receive buffers */
   cominz();              /* initialize clock and I/O Interrupt routines */
   _enable();      /* start interrupts backup */
}

/*------------------- Initialize Receive Buffer Array --------------------*/
init_rcvbuf()
{
int i;

   for(i=0;i<10;i++)      /* store pointers in buffer array */
           Baddr[i] = getbuf(&bpool,xbufsiz+30); /* +30 for X.25 header info */
   rbufcnt = 9;                   /* 0 to 9 = 10 buffers */
   rcvbuf = Baddr[rbufcnt];    /* preallocate first buffer pointer */
   Baddr[rbufcnt] = (buf_type) NULL;     /* clear the array pointer */
   rcvdat = (char *)(BuffData(rcvbuf));  /* point to the user space */
   icnt = 0;                  /* zero frame character count */
}

/* ######################## COMINZ.C ########################## */
/*   Initialize the Clock and Synchronous Interrupt routines    */
cominz()
{
unsigned intnum;
unsigned int val;   /* local varibles */

   /* install MAC I/O driver */
   intnum = 0x1c;         /* Clock interrupt vector */
   _dos_setvect(intnum,clock);   /* setup new clock interrupt routine */
   istart();          /* enable I/O interrupt processing */
   intnum = 0x0c;         /* I/O interrupt vector */
   _dos_setvect(intnum,rcvdata);   /* setup tx & rx interrupt routine */
   val = inp(0x21);
   outp(0x21,(val & 0xc7));   /* start irq 3, 4, & 5 */
}

/* ++++++++++++++++ INITIALIZE SEALEVEL BOARD +++++++++++++++++ */
istart()
{
unsigned char InitArray[50];   /* Allocate the size of the Init Array */
int iCount;
   /*------------------- Section # 1 ---------------------*/
   InitArray[0]  = 9;
   InitArray[1]  = 0xC0;   /* force hardware reset */
   InitArray[2]  = 0;
   InitArray[3]  = 0x00;
   InitArray[4]  = 4;
   InitArray[5]  = 0x20;   /* (SDLC) mode selected */
   InitArray[6]  = 3;
   InitArray[7]  = 0xC0;   /* rx 8 bits, sync char inhabit */
   InitArray[8]  = 5;
   InitArray[9]  = 0x61;   /* tx 8 bits */
   InitArray[10] = 6;
   InitArray[11] = 0;      /* For Mono-sync */
   InitArray[12] = 7;
   InitArray[13] = 0x7E;   /* sdlc sync character */
   InitArray[14] = 10;
   InitArray[15] = 0x80;   /* CRC set to inverted bit pattern */
   InitArray[16] = 11;
   InitArray[17] = 0;   /* for dte clock from RTxC pin */
   InitArray[18] = 12;
   InitArray[19] = 0xFE;   /* Low order baud rate value */
   InitArray[20] = 13;
   InitArray[21] = 00;      /* High order baud rate value */
            /*                        H  L  */
            /*               38400 = 00 3E  */
            /*               19200 = 00 7E  */
            /* time constant: 9600 = 00 FE  */
            /*                4800 = 01 FE  */
   InitArray[22] = 14;
   InitArray[23] = 2;      /* gen enabled, gen source */
   /*------------------- Section # 2 ---------------------*/
   InitArray[24] = 14;
   InitArray[25] = 3;      /* gen enabled, gen source */
   InitArray[26] = 3;
   InitArray[27] = 0xD9;   /* rx 8 bits, hunt mode, rx CRC, rx enabled */
   InitArray[28] = 5;
   InitArray[29] = 0x69;   /* tx 8 bits, tx enabled, tx CRC */
   InitArray[30] = 0;
   InitArray[31] = 0x80;   /* tx CRC gen */
   InitArray[32] = 1;   /* int on all rx chars or special cond. */
   InitArray[33] = 0x12;   /* enable tx interrupts */
   /*------------------- Section # 3 ---------------------*/
   InitArray[34] = 15;
   InitArray[35] = 0x41;   /* tx underrun int enabled */
   InitArray[36] = 0;
   InitArray[37] = 0x30;   /* error reset */
   InitArray[38] = 0;
   InitArray[39] = 0x90;   /* tx CRC gen, reset ext/status int */
   InitArray[40] = 0;
   InitArray[41] = 0x90;   /* twice */
   InitArray[42] = 1;   /* int on all rx chars or special cond. */
   InitArray[43] = 0x12;   /* enable tx interrupts */
   InitArray[44] = 9;
   InitArray[45] = 8;   /* Master int enable */
   InitArray[46] = 0;
   InitArray[47] = 0xF0;   /* reset tx underrun, error reset */
   InitArray[48] = 0;
   InitArray[49] = 0x28;   /* reset tx int pending */

   for (iCount = 0; iCount < 50; iCount++)
      outp(0x239,InitArray[iCount]);   /* Output Data to SDLC Chip */
}




[LISTING TWO]


/********************************************************************
 *  RCV2.C -- Interrupt Handler routine for use with the Sealevel   *
 *  Systems synchronous communications board that uses preallocated *
 *  buffers to Transmitt and Receive X.25 data frames.               *
 ********************************************************************/

/* AUTHORS - Michael T. Thompson, Planning Systems Inc. and
 *           Ken Crocker, The MITRE Corporation   5/23/89
 */

#include <stdio.h>   /* MicroSoft "C" 5.1 " */
#include <dos.h>      /* MS-DOS 3.30 */
#include <ctype.h>

/* RETIX OSI SOFTWARE COMMON HEADER FILES */
#include "c:\retix\include\bufflib.h"
#include "c:\retix\include\common.h"
#include "c:\retix\include\system.h"
#include "c:\retix\include\lapb.h"
#include "c:\retix\include\address.h"
#include "c:\retix\include\network.h"
#include "c:\retix\include\x25.h"

extern buf_type MDATcon();    /* Data Confirm routine */
extern struct sp_ent *mac;   /* Service provider table */
extern xbufsiz;         /* Current TPDU buffer size */
extern vpmidi();      /* MDATind Data Indiction routine */

unsigned char *rcvdat;         /* Pointer to received user data input buffer */
buf_type rcvbuf;         /* Pointer to received buffer header */
buf_type Baddr[10];         /* Array of allocated receive buffer pointers */
int icnt, rbufcnt, fcnt;      /* Receiver and Transmitter counters */
int fsize;            /* Size of Transmit Frame */
char *frame;            /* Temporary pointer to Transmit frame */

/************************** RCVDATA.C ****************************
 *          Interrupt Driven Receiver and Transmitter            *
 *  RECEIVER: On the receive side an array of buffer pointers is *
 *  allocated in the x251t.c initialization Routine. The first   *
 *  buffer is preassigned to the receive routine and then that   *
 *  buffer can be written to by the interrupt routine. When the  *
 *  last character of the frame is received the MDATind (vpmidi) *
 *  routine is called and the new buffer is put on the queue for *
 *  processing later. After that we get the address of another   *
 *  preallocated buffer from the array and setup the proper      *
 *  pointers.                                                    *
 *****************************************************************/

void interrupt cdecl rcvdata()
{
unsigned int c, c1, delay;    /* local varibles */
_enable();         /* enable interrupts */

/* check if this is an error, a receive or a transmitt interrupt */
outp(0x239,3);
delay = 0;       /* allow time for the register to setup */
   if(((c = inp(0x239)) & 0x30) != 0)  /* if not an error continue */
   {
        if((c & 0x20) != 0)     /* if not receive interrupt continue */
      {

   /******** RECEIVE DRIVER *********/
   /*   we must have receive data   */
   /*********************************/
   rcvdat[icnt++] = inp(0x238);    /* get character and store it */
   outp(0x239,1);
   delay = 0;
   if(((c1 = inp(0x239)) & 0x80) != 0)   /* check for end of frame */
   {
      if((c1 & 0x70) == 0)  /* check for a valid CRC */
      {   /* must be OK */
        if(icnt > 3)
        {
          rcvdat[icnt-2] = 0;
          BuffAdjust(rcvbuf,((xbufsiz+30)-(icnt-2)));
             /* reajust buffer size for lapb */
          vpmidi(rcvbuf,mac);
            /* send MDATind to service user */
          icnt = 0;
          while(Baddr[--rbufcnt] == (buf_type) NULL);
            /* Find an unused buffer pointer */
          rcvbuf = Baddr[rbufcnt];
            /* got a new receive buffer pointer */
          Baddr[rbufcnt] = (buf_type) NULL;
            /* clear buffer pointer from array */
          rcvdat = (char *)(BuffData(rcvbuf));
            /* setup receive data pointer */
            /* check for a transmit interrupt pending */
          if((c & 0x10) == 0)
            goto ENDINT;  /* no interrupt so end */
        /* found transmit interrupt pending so send it */
         goto TXINT;
        }
        /* frame is to short so go to error reset */
        goto ERRRES;
   }
   /* We got a BAD CRC */
   goto ERRRES;   /* goto error reset */
     }
    /* not end of frame so check for a transmit interrupt pending */
    if((c & 0x10) == 0)
   /* no transmit interrupt so end interrupt routine */
   goto ENDINT;
       }

TXINT:/************************ TRANSMIT DRIVER *****************************/
      /* After we have determined that we have received a transmit interrupt*/
      /* we check to see if we are at end of frame by checking its size. If */
      /* not, then we send out character pointed  at by frame pointer and   */
      /* then end the interrupt. If we are at end of frame, we clear frame  */
      /* counter and reset the transmit interrupt flag.                     */
      /********************************************************************/

      if(fsize > 0)     /* are at the end of the frame ? */
       {
      fsize--;                   /* decrement frame size */
      outp(0x238,frame[fcnt++]);    /* NO - send character */
      goto ENDINT;                   /* end the interrupt routine */
       }
       outp(0x239,0);      /* must be at the end of the frame */
       fcnt = 0;      /* clear the frame count */
       outp(0x239,0x28); /* reset transmit interrupts */
       goto ENDINT;     /* end the interrupt routine */
   }
ERRRES:
   outp(0x239,0);     /* error reset */
   icnt = 0;        /* reuse the same buffer */
   outp(0x239,0x30);
ENDINT:
   outp(0x20,0x20);        /* End of interrupt report */
   return;
}

/******************************** MDATreq() **********************************
 *  MDATreq: Transmit LAPB output requests. When a buffer is ready to be     *
 *  transmitted, that buffer is sent to MDATreq routine where we check to see*
 *  if data is currently being transmitted. If it is, we return to calling   *
 *  routine and try again later. If no data is being transmitted, we check   *
 *  transmit buffer register of synchronous controller chip to see if it is  *
 *  empty so that when it is, we can send out first character of frame. When *
 *  we can send out a character the pointers and counters are setup and      *
 *  first character is sent. After first character is sent, buffer is removed*
 *  from outbound queue and a second character is sent to help with system   *
 *  timing. At this point, interrupt driver will take over and continue to   *
 *  send remaining data until last byte of frame is delivered along with     *
 *  frames CRC.                                                              *
 **************************************************************************/

void MDATreq (msdu)
buf_type msdu;          /* Transmitt buffer pointer */
{
int rval, delay;      /* local varibles */
   if(fsize != 0)      /* if we are still transmitting a frame return */
      return;
   outp(0x239,0);
   fcnt=0;
   while (((rval = inp(0x239)) & 4) == 0);  /* test for buffer empty */

   frame = (char *)(BuffData(msdu));   /* get pointer to user buffer */
   fsize = (BuffSize(msdu)-1);       /* get size minus first char */
   QRemove(msdu);             /* remove buffer from queue */
   delay=0;
   outp(0x238,frame[fcnt++]);         /* send first byte of frame */

   msdu=(buf_type)MDATcon(msdu,mac);   /* and confirm the buffer */
   while (((rval = inp(0x239)) & 4) == 0);  /* test for buffer empty */

   fsize--;             /* decrement frame size */
   outp(0x238,frame[fcnt++]);       /* send second byte of frame */

   outp(0x239,0);
   delay = 0;
   outp(0x239,0xC0);           /* process end of frame CRC */

   return;
}




[LISTING THREE]


/***************************************************************************
 * Poll timer queue for any expired timers. Next check to see if there is  *
 * any X.25 traffic to be moved on Inbound or Outbound packet queue. The   *
 * last process we check is if Baddr Array is low on preallocated receive  *
 * buffers that are used by RCV2.C Interrupt Handler.                      *
 ***************************************************************************/
/* AUTHOR - Michael T. Thompson, Planning Systems Inc.  5/23/89  */

poldat()
{
   int i;   /* index varible */
   do_timer_queue();  /* Test for expaired timers */
   do_lapb_queue();   /* Check for X.25 Traffic */
   if(rbufcnt < 4)      /* replenish array if less than four pointers */
   {
      for(i=0;i<9;i++)   /* check all receive buffers */
      {
         if(Baddr[i] == NULL)   /* If Null, pointer has been used */
         {   /* Get new buffer pointer & assign it to the array */
          Baddr[i] = getbuf(&bpool,xbufsiz+30);
          rbufcnt++;           /* Increment buffer count */
         }
      }
   }
}   /* END OF POLDAT */










Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.