Cover V02, I05
Article

sep93.tar


Setting up a High-Speed Modem

Larry Reznick

The prices on 14400 modems are coming down. Those modems are being supported more and more widely by various services. I've been using one for months, but not on a UNIX system. The time came to install one on a client's central site and on the client's beta site, both running SCO UNIX. Once that setup was proven, it would become the standard setup for my client's turnkey product.

My first concern was the delay in making the connection while the modems at each end negotiate with each other. That negotiation establishes the speed each modem is capable of, whether error correction can be used, and whether data compression can be used. A few years ago, when MNP-5 modems were popular and inexpensive, I experimented with attaching one to a Sun UNIX system. I ran into some problems with that negotiation delay. UUCP timed out before the negotiation could finish. Unfortunately, the priority for getting this MNP-5 modem to work was very low. I was moved to another project before I could find a way around the problem and still get the MNP-5 advantages: data compression delivering up to 4800 BPS on a 2400 modem. This time, with making it work a high priority, I knew there had to be a way.

The 14400 modems can adjust their speed to that of the modem at the other end, which means that modem doesn't need to be another 14400. After adjusting, they can notify the computer of the speed. On the one hand, 14400 is not a standard BPS rate, since BPS rates, as a general rule, double from each increment starting with 300. In fact, I know of no communication software that actually supports that 14400 rate. On the other hand, the modems are capable of communicating with the computer at speeds up to 57600 BPS. The modem does this by taking advantage of the difference between the data communications equipment (DCE) connection from modem to modem and the data terminal equipment (DTE) connection from modem to computer.

By not requiring each of the connections to run at the same rate, one modem can negotiate with the other modem over the phone line, establish the appropriate DCE speed according to what the modems can handle, and translate to a different DTE speed for transmission to and from the computer. To achieve a DTE speed higher than the DCE speed, each local modem buffers the data. If the DTE speed is higher than the DCE speed, the effect is an average faster throughput than would be expected were the two speeds identical.

Because of the modem's local buffering, the computer can be told to connect to the modem (DTE) at a single BPS rate, ideally the fastest the modem is capable of: 57600. However, the ideal and the real are often different. For instance, during Zmodem transfers on some computers, I've found that the time it takes for the system to write a disk block is long enough to miss the next modem data. The data is just coming in too fast for the disk handling. Slow the DTE speed down and the system can keep up. The DCE speed is still 14400, though, so the transfers are measurably fast.

SCO UNIX doesn't strictly support any modem speed faster than 9600. However, they do indicate that some Hayes compatible modems have been tested and work at the faster rates. I hooked up a Practical Peripherals 14400 FXSA in early tests, and in the final installation used two Practical Peripherals 14400 FXMTs. Those modems worked just fine. I have no reason to believe other brands wouldn't work as well; I can't vouch for them because I haven't tried them.

To set up the 14400 modem on UNIX, some files need to be changed for the hardware interface and some for the UUCP interface. Before starting, be sure the getty on the modem port is disabled. On SCO UNIX, the port I used for the modem is tty1A, so the command to disable it is:

disable tty1A

This command changes the /etc/inittab file and the /etc/conf/cf.d/init.base file. The SPBinittab(F)SPE file contains the device names and characteristics used when SPBinit(M)SPE starts. The init.base file supposedly feeds default settings to inittab when the kernel is rebuilt, but I have no evidence this actually happens on SCO UNIX. Whenever the kernel is rebuilt, inittab settings come from a file named /etc/conf/init.d/sio. (On SVR4, the equivalent to sio is named asy.) Unfortunately, when the kernel is rebuilt the sio file's settings are appended to the end of the inittab without checking to see if that appending creates duplicate settings. Since it does create duplicates, "tty spawning too rapidly" errors appear after subsequent reboots. init is trying to spawn two or more gettys on the same tty. To solve this problem, I commented out any lines in the sio file that are also in the inittab file. I keep the inittab and init.base files identical, just in case SCO does draw from the init.base file.

Once the getty is disabled, two files need to be changed: /etc/inittab and /etc/gettydefs. The inittab file contains the init reference for the tty. That reference points to the gettydefs entry. The gettydefs file contains the port settings used by the getty before and after making a tty connection. These settings are similar to stty(C) except that they use all capital letters. (See gettydefs(F) and termio(M) for more information about those settings.)

I created the following single-line entry in /etc/gettydefs:

ppi14 # B38400 CS8 SANE HUPCL TAB3 ECHOE CRTSFL -IXANY

-IXON -IXOFF -CLOCAL -CTSFLOW -RTSFLOW # B38400 CS8

SANE HUPCL TAB3 ECHOE CRTSFL -IXANY -IXON -IXOFF

-CLOCAL -CTSFLOW -RTSFLOW #\r\n@!login: # ppi14

A gettydefs entry has five fields, each separated by a pound sign (#). The first field contains the name of the gettydefs entry. Inittab refers to this name. The second field contains the initial termio settings used when init runs getty. The third field contains the final termio settings used before getty runs login(M). The fourth field contains the login prompt string. The "@" symbol in the login prompt represents the system name, which is taken from /etc/systemid. The fifth field refers to the next gettydefs entry to be used if a BREAK code is sent down the line. This BREAK is commonly used to cycle through multiple BPS rates controlled by software. For instance, 2400 BPS modem setups often allow users to dial in at 1200 and 300, while 9600 BPS setups typically allow 4800 and 2400. In the first case, the 2400 entry refers to the 1200 entry, and the 1200 entry refers to the 300 entry, and the 300 entry refers back to the 2400 entry, closing the cycle. The ppi14 entry refers to itself. There is no need for software cycling through different BPS rates because the modem hardware negotiates the rate. Thus, the DTE rate is fixed at 38400. Other operations set a lower speed if needed.

(I didn't honestly expect 57600 to work well under multitasking conditions. I had hopes for 38400 and expected no trouble with 19200.)

This entry contains identical initial and final termio settings. The 14400 modem is best run with RTS/CTS turned on and XON/XOFF turned off.

XON/XOFF is a software protocol. When an internal software buffer gets to a certain percentage of full, say 75 percent, the software sends an XOFF data byte to the other system. The other system stops sending. The remaining 25 percent of the buffer catches the data sent down the line before the XOFF was received by the remote system. With the data flow stopped, the local system can empty the buffer without worrying about a buffer overflow. When the buffer is a certain smaller percentage full, say 25 percent, the modem sends an XON data byte. The other system will start sending again. The remaining 25 percent of the buffer still containing data keeps the local system busy while the remote system is receiving the XON.

RTS/CTS (Ready-To-Send/Clear-To-Send) is a hardware protocol. Instead of the local modem telling the remote computer's software to stop sending, using the RTS/CTS protocol the local modem tells the local computer to stop sending. When data is available to send, the RTS line is raised. When the data can be received, the CTS line is raised. By toggling those lines, the local computer and modem avoid buffer overflows and the data from modem to modem goes as fast as it can. Yet, no extraneous data transmits between the two modems. With the higher speed modems, RTS/CTS is required due to the modem's internal buffering, used to make the different DTE/DCE speeds work.

I was using SCO UNIX System V Release 3.2 Version 4.0. I found out that v4.0 didn't properly handle the RTS/CTS protocol. This bug was resolved in the interim release v4.1. Without RTS/CTS, the best I could do was 9600 BPS using XON/XOFF protocol. (The termio setting for XON/XOFF in gettydefs would be the same as above except you would substitute IXANY for all the settings between the ECHOE and the #. Other AT-style initialization codes are needed for the modem itself. More about those later.) Once I installed the v4.1 upgrade I got the RTS/CTS protocol working. The CRTSFL flag was created by SCO for v4.0. In a single setting, it is supposed to replace the CTSFLOW and RTSFLOW flags. When CRTSFL is used, the other flags must be turned off: -CTSFLOW -RTSFLOW. The CLOCAL flag must be turned off whenever RTS/CTS protocol is used. (SCO v4.2 is available now. I haven't installed it as of the time of this writing. I have seen a note from SCO that the gettydefs settings are slightly different, requiring the elimination of the CRTSFL and use of CTSFLOW and RTSFLOW.)

With gettydefs set, I edited /etc/inittab to make tty1A (labeled in the inittab file as "t1A") point to the new gettydefs entry. If I change the last field in the inittab file to ppi14, the new gettydefs entry will be used when I enable tty1A. It's also necessary to change the init.base file, too.

The next steps configure cu(C) and uucp(C) to work. After cding to /usr/lib/uucp, I edit /usr/lib/uucp/Devices. The Devices(F) file identifies the speed and dialing characteristics of the modem's tty port. If there is more than one entry for the same device in the Devices file, the first one becomes the default entry. Otherwise, the speed identifies the entry to use. When cu or uucp names a system, the Systems(F) file contains that system's speed and device information. If you use cu to dial any other phone number or to make a direct connection to the modem, you must name the tty line with the "-l" option. Then, you must either identify the speed to use on the tty line with the "-s" option or cu will use the highest speed configured in the first entry in the Devices file.

I ended up using three Devices lines:

ACU tty1A - 19200-38400 /usr/lib/uucp/atdialPPI14
ACU tty1A - 300-9600 /usr/lib/uucp/atdialPPI9
Direct tty1a - 300-38400 direct

The first two lines specify two different dialers to use for the ACU: the first one for the higher rates using the 14400 DCE, and the second one for the lower rates using a DCE of anything else. The third line, labeled "Direct," lets me make a direct connection to the modem at any speed without modem control, since tty1a (notice the lowercase "a") has no modem control in the device driver.

The last field of each ACU line refers to the SCO "atdialer" program as the dialer. I first tried to use entries in the Dialers(F) file, which is also found in /usr/lib/uucp. Here are the single-line entries that I tried in the Dialers file:

ppi14400 =,-, "" ATZ\r\c OK\r
AT&FE1L2M1Q0X4&C1&D2&K3&Q5S0=0S2=43S95=46&W\r\c OK\r \
ATDT\T\r\c Speed
&ppi14400 =,-, "" +++\dATH OK\r ATS0=1S2=128\r

ppi9600 =,-, ATZ\r\c OK\r
AT&FE1L2M1Q0X4&C1&D2&K3&Q6S0=0S2=43S95=46&W\r\c OK\r \
ATDT\T\r\c Speed

&ppi9600 =,-, "" +++\dATH OK\r ATS0=1S2=128\r

There are really only three fields: the dialer name, the pause translation table, and the chat script.

The lines named ppi14400 and ppi9600 are the dialer names referred to by the Devices file entries. These lines' chat scripts are sent as initialization codes just prior to dialing with any cu or uucp command. The lines having the same names but preceded by an ampersand (&) are used to hang up and reset after the session is finished.

The pause translation table contains translation characters for certain kinds of delays. The first one is a wait-for-dialtone pause, the second, a simple pause. Some modems use different characters for those different pauses. In a phone number used on cu's command line or in uucp's Systems file, the equals sign represents the wait-for-dialtone code, and the minus sign represents the pause code. For example, if you needed to dial 9, wait for a dialtone, then dial the number, you might use the phone number 9=5551212. The dialer script will replace the equals sign with the modem's character for that kind of delay. For Hayes-compatible modems, there is no distinction between the two pauses: the equals sign and minus sign is replaced by a comma for both of them.

The chat script is just like the one used in the Systems file: a set of expect/send strings. The first string defines the expected response from the modem and the second is a string to send. The rest of the chat script is more expect/send sequences. When an empty string ("") is used for the expect string, that means expect nothing.

The 14400 and 9600 chat scripts are identical except for the &Q entry. Briefly, the initialization string breaks down like this:

AT -- Get the modem's attention.

&F -- Load the default factory profile for the switches and S-registers and completely reset the modem. This is a more complete reset than that used by the old ATZ.

E1 -- Turn on command echo. Use this to see what you're typing when you're not connected to another modem.

L2 -- Set speaker volume to medium. This is a default setting. I have it here so I can explicitly turn it low (L0) for a customer site's modem.

M1 -- Keep speaker on until carrier detect. This is also a default setting. Again, I have it here so I can explicitly turn it off (M0) so customers don't have to listen to the tones.

Q0 -- Enable result codes.

X4 -- Set extended result codes to show connect speed. Also waits for a dialtone before dialing.

&C1 -- Set Data Carrier Detect (DCD) according to the remote modem.

&D2 -- Use Data Terminal Ready (DTR). Dropped DTR hangs up and goes to command mode.

&K3 -- Set local flow control to RTS/CTS. Use &K4 if you must use 9600 with XON/XOFF instead.

&Q5 -- Set v.42 error control. The v.42bis (data compression with theoretical 4:1 compression for text) will be automatically negotiated if the remote modem supports it. If you must use 9600 or less, use &Q6 instead. Data compression will be disabled but you should get a good connection. However, if you must run XON/XOFF (using &K4) this must be set to &Q0. That turns off negotiation and error correction, giving you a straightforward connection at exactly the BPS rate specified. The BPS rate must be 9600 or less to use XON/XOFF.

S0=0 -- Turn off the autoanswer.

S2=43 -- Enable the plus symbol as the escape code. Normally, this is best turned off when connected to another modem. But, while debugging the communication, it is very handy to be able to go into command mode and run ATI6 and AT&V. ATI6 shows the active BPS rates, &K and &Q settings, and whether error correction and data compression protocols were activated. AT&V shows the current DTE and DCE settings, all of the options, and all of the S-register settings. After looking at them, I can go back online by typing ATO. If you're configuring both sides of a modem connection, though, be sure that the remote modem has the escape code turned off, using S2=128. Otherwise, when you escape your local modem, you might escape the remote modem too. If a timeout isn't active to kill the connection after carrier is dropped, that modem will hang on the line even after you hangup.

S95=46 -- Set the modem to tell the maximum information about the connection. This is useful primarily when debugging the communication. There are five bits available:

1 Show Connect DCE speed instead of DTE.
2 Show /ARQ if error correction active.
4 Show Carrier DCE speed.
8 Show Protocol NONE, LAP-M, or MNP.
32 Show Compression NONE, 4.2BIS, or MNP5.

Just add the bits together to get the value to set in the register. I don't use the 1 value because the regular Connect message shows the DTE speed while the Carrier message shows the DCE speed. This S-register can remain set this way because the uucp and other chat scripts will either ignore or absorb the other messages this setting generates.

&W -- Write the current switch and register settings to nonvolatile profile 0. Strictly speaking, this setting isn't needed with every dialer initialization. I could have sent everything other than the &Q (and maybe the &K) to the modem manually, stored it once with the &W, and then reduced the chat script's initialization string to AT&F to reset to factory conditions, followed by ATZ to set the profile 0 configurations. I didn't do it this way because if I or anyone else in the future needed to experiment with other settings, including changes to the modem's profile, I wanted cu and uucp to be able to reset the modem completely.

When the communication is finished, the appropriate &ppi setting is activated. The three pluses get the modem's attention, the ATH hangs up the line, the autoanswer will pick up the phone on the first ring (S0=1), and the modem escape is disabled (S2=128), so that the next remote call that comes in can't escape the local modem to command mode.

(The "\T" in the dialer entries marks the location where the phone number will be placed when the dialer is used. The keyword "Speed" in the expect field tells the dialing program to match the connect speed for a variable speed modem.)

These were good-looking Dialers entries. I got the 9600 entry to work in the XON/XOFF mode (using &K4&Q0) while I waited for SCO's v4.1 upgrade to arrive. When v4.1 arrived, I expected the dialer entries would continue to work with &K3&Q5 at 19200 and above. No such luck.

The problem was that despite my gettydefs entries, the RTS/CTS flow control simply wouldn't set properly. Using stty while the test connections were active, I could debug the line protocols from another virtual terminal (SCO calls them multiscreens). The stty program always works with the standard input (stdin), even though it looks weird to do so. To find out what the line is doing, I executed the following command:

stty -a </dev/tty1A

That shows all the settings currently active on tty1A. (You can't look at tty1A while the Terminal Ready (TR) light is off. The stty program simply hangs until an interrupt is sent to the stty process. When stty is interrupted, you'll see a message such as "Can't open device." If you must look at the serial line when TR is off, use tty1a (notice the lowercase) instead. The settings will be the same when there is no connection. When the port has been disabled, the TR light turns off. When it is enabled, TR is on. The cu and uucp programs turn TR on as needed.) I needed the following settings: -clocal, crtsfl, -ctsflow, -rtsflow, -ixon, -ixany, and -ixoff. I wasn't getting them with v4.1. Despite the gettydefs settings, RTS/CTS was off and XON/XOFF was on! Because of that, the modem connection would lose lots of characters and every so often, there would be ^S and ^Q characters in my keyboard commands.

To force the settings the way I wanted, I used the following command:

stty crtsfl -ctsflow -rtsflow -ixon -ixany -ixoff </dev/tty1A

That looks strange because the command seems to need input from tty1A, yet the intent is to output to tty1A. But, that's the way stty works. It reads input from whatever is on stdin, and it writes output to whatever is on stdin. When I had a 19200 BPS connection with lost data, it worked correctly once I forced those settings. I needed something to force those stty settings. I have no idea why gettydefs isn't sufficient on SCO UNIX.

Fortunately, SCO saved me some programming by providing a program named atdialer. The C source code is included, but I was able to use the supplied program without modification instead of the Dialers file entries. atdialer looks for a textfile in the /etc/defaults directory containing a description of various modem commands and responses. You can use a shell script named make.dialer, supplied with the atdialer program, to create that file if you like, or use a text editor. The textfile must be given a unique name.

Usually, the textfile name is "atdial" followed by an all-capitals abbreviation for the modem being configured. atdialer knows which textfile to use by looking at its program name. The name of the atdialer program will be the same as the textfile name. This is similar to the trick used for ls(1). On some UNIX systems, ls has alias names, such as ll and lc. They are really hard links to the primary program. The program looks at which name was used to invoke it and modifies its operation according to the name.

When make.dialer is run, it asks what name I want to use. I selected "atdialPPI14" for the 14400 mode of operation. The make.dialer script asked me a bunch of questions about the modem's commands and responses. The answers are put into a textfile named /etc/default/atdialPPI14, and a hard link to /usr/lib/uucp/atdialer, named /usr/lib/uucp/atdialPPI14, is generated. In other words, the program's basename is identical to the configuration textfile's basename. Internally, the program only needs to change the path to get to the textfile.

Unfortunately, the make.dialer script doesn't request stty settings, even though the atdialer.c source shows that the program can use them. To solve the problem, edit the file and manually enter the stty settings. Following is the resulting file for the 14400 operations:

* Setup for Practical Peripherals 14400 FXMT
STTY=-IXON -IXOFF -RTSFLOW -CTSFLOW CRTSFL
MDM_SETUP=AT&FE1Q0X4&C1&D2&K3&Q5S0=0S2=43S95=46&W
MDM_OPTION=
MDM_DIALCMD=ATDT
MDM_ESCAPE=+++
MDM_HANGUP=ATQ0H0
MDM_RESET=ATQ0Z
MDM_DIALIN=ATS0=1
MDM_ATTN=AT
MDM_DSBLESC=ATS2=128
RTC_OK=OK
RTC_NOCARR=NO CARRIER
RTC_ERROR=ERROR
RTC_NOTONE=NO DIALTONE
RTC_BUSY=BUSY
RTC_NOANS=NO ANSWER
RTC_300=CONNECT
RTC_1200=CONNECT 1200
RTC_2400=CONNECT 2400
RTC_4800=CONNECT 4800
RTC_9600=CONNECT 9600
RTC_19200=CONNECT 19200
RTC_38400=CONNECT 38400

(The atdialer program searches through the file for an exact match of each variable name, so anything in the file not using one of those names is ignored. Since SCO used the asterisk (*) as if it were a comment introducer, I did the same.)

The only change for 9600 or less operations is to change the &Q5 to &Q6, make one file for the 14400, (which I named atdialPPI14), and another file for the 9600 (named atdialPPI9). Those files must be worldwide readable and are placed in /etc/default. The next step is to make hard links to /usr/lib/uucp/atdialer using those same names. After creating the files, the entire command sequence should be:

cd /etc/default
chmod a+r atdial*

cd /usr/lib/uucp
ln atdialer atdialPPI14
ln atdialer atdialPPI9

After setting up the atdialer, I changed the Devices file to refer to the atdialPPI14 and atdialPPI9 dialer programs, as shown earlier.

Finally, so cu and uucp can use the system names, I changed the /usr/lib/uucp/Systems file to make each system entry refer to the correct speed: 19200. I tried 38400 and got connections, but cu lost characters when UNIX did other things. This was annoying with interactive connections; it would be devastating for uucp transfers. So, for now, set the top rate as 19200. The problem is probably that the sio driver can't keep up with that speed in its current condition and SCO hasn't chosen to bring the driver up to date with modern higher-speed modems yet. There might be a way to fix that without rewriting the device driver. Writing a new device driver is the last resort.

The /usr/spool/uucp/.Admin/xferstats file keeps track of the uucp transfer speeds. Sample entries, each a single line, look like this:

rezbook!mmdf S (6/24-11:57:01) (C,1997,1) [tty1A] -> 528 /
1.230 secs, 429 bytes/sec

rezbook!mmdf S (6/24-11:57:05) (C,1997,2) [tty1A] -> 146 /
0.090 secs, 1622 bytes/sec

chevrn1!mgr M (6/24-16:18:43) (C,3106,64) [tty1A] <- 308655 /
360.420 secs, 856 bytes/sec

chevrn1!mgr M (6/24-16:25:16) (C,3106,78) [tty1A] <- 309225 /
370.850 secs, 833 bytes/sec

(The rezbook system had a 2400 BPS connection. The chevrn1 system had the 14400 FXMT.) These samples show the system!user who sent the file, the date and time of the transfer, a sequence number within the connection, the port, whether it is coming from the other system (an arrow formed from a less-than and a hyphen <-) or going to the other system (an arrow formed from a hyphen and a greater-than ->), how many bytes are in the transfer, how many seconds it took to transfer, and the bytes-per-second throughput.

The number of seconds is total time, not just the time of the actual transferring. It reflects not only the time it took to transfer the bytes, but the time UNIX used to do other things, so the bytes-per-second is a real throughput time. In the traditional estimation of transfer time, you divide the BPS rate by 10 (eight data bits, one start bit, and one stop bit) to get the theoretical maximum bytes-per-second rate. That means a 9600 transfer can reach a top rate of 960 bytes per second, and a 14400 transfer goes to 1440. Since the DTE speed is higher than 14400, the actual maximum speed could be higher than 1440 (although not quite as high as 1920 or 3840). But, UNIX has other things to do. Time taken away from the transfer reduces the throughput. Still, the sample numbers show that the transfers are in the 800 bytes-per-second range. This time represents a four to five times speedup from 2400. Since 14400:2400 is a 6:1 ratio, we seem to be getting most of the speed out of the modem. Because the modem is able to deliver 57600 BPS, we might be able to get more out of the modem with a better sio driver.

About the Author

Larry Reznick has been programming professionally since 1978. He is currently working on systems programming in UNIX and DOS. He teaches C language courses at American River College in Sacramento. He can be reached via email at: rezbook!reznick@csusac.ecs.csus.edu.