How TINI does PPP

11 May

How TINI does PPP

The TINI API handles PPP connections via the PPP,  PPPEventListener, and PPPEvent classes in the com.dalsemi.tininet.ppp package. The PPP acts as a layer between the IP network layer and the physical interface. To communicate via PPP, an application must create a PPP object and pass a serial port object to it, telling it which port to use for communication. Table 13-1 illustrates some of the PPP methods (the ones we’ll be making use of). The TINI API has more complete information.

Table 13-1: The PPP Related methods used in our upcoming examples

The application must also listen for PPPEvents, via a PPPEventListener. The PPPEventListener class is an interface that we need to provide an implementation for. The TINI API for PPPEventListener describes its functionality in terms of a finite

state machine (FSM). That is, during PPP operation, there are a fixed number of internal states that our PPPEventListener can be in. As changes occur in the PPP connection, PPPEvents are generated, and we move from state to state based on what

state we’re in and what event we see. There are six states in our FSM. These states, for the most part, map directly into the five events that our event listener is responsible for handling.

Table 13-2: The five possible PPPEvents

The six states of the PPPEventListener FSM

INIT

We move to this state upon creation of a PPP object, i.e. pppObject  =  new ppp();

START

We move to the START state when the owner of our PPP object calls the open() method. During this state, the event listener must call the up() method and provide, as an argument, the serial port we wish to use for our PPP connection.

AUTH

We move from the START state to the AUTH state after the owner of the PPP object calls the up() method. During this state, the event listener must perform authentication on a user name and password. This state is only part of the PPP connection process if user authentication is required. Authentication is established as part of the connection process by calling the setAuthenticate() method with an argument of true. We will see examples both with and without authenticate in this fashion. Authentication is accomplished by calling the getPeerID() and getPeerPassword() method, then comparing the returned values to those values stored in the password file. If the values match, the authenticate() method is called with an argument of false. It’s the authenticate() method that will determine which state we go to next.

STOPPED

To get to the STOPPED state, something such as a link negotiation error, remote termination, or an authentication failure has occurred. During this state, the event listener must call the close() method and print out any diagnostic messages that are desired.

UP

We get to the UP state when authentication has succeeded (authenticate(true) was called). During this state, the event listener is responsible for calling the addInterface() method, with the interface name to be added as an argument. If authentication wasn’t required, we get here from the START state.

CLOSED

We get to the CLOSED state when a close() method is called. During this state, the event listener is responsible for calling the down() and removeInterface() methods.

This state machine is best understood by looking at it graphically.

Figure 13-7: The PPPEventListener Finite State Machine

So, putting it all together, the various methods in the PPP class cause events to be generated. The PPPEventListener listens for these events, and uses them as a basis for defining what state our PPP connection is in. The response to an event will involve invoking additional PPP methods, which in turn, may cause an event, causing us to change state. The state we’re currently in is a function of the most recent event that occurred, and that event was a function of the previous state we were in. The finite state machine allows us to envision this fairly complicated process in a simple way. The best way to learn about this is with some example code, but before we can move on to that, we have to address the somewhat troublesome subject of cables and modems.

The physical interface (cabling and modems)

Sometimes the devil is in the details, and in the case of TINI PPP connectivity, the devil has a lot of places to hide. One such place is the physical interface: the modem and modem cable.

Cables

One way or another, to connect a modem to a TINI stick you’re going to have to use some kind of RS232-style cable. Some key things to keep in mind:

•   Both the TINI serial0 and the modem have RS232 ports that behave as data communications equipment (DCE), so you have to use a cross-over cable (otherwise known as a null modem cable). This is in contrast to the type of modem cable you use between a PC and a modem, and between a PC and TINI when using JavaKit. Since the PC acts as data terminal equipment (DTE) in these instances, the cable needs to be straight-through.

•   Modems may require signals that the TINI doesn’t support. Specifically, the TINI stick only uses TX and RX on serial0. Many modems require a logic “1” on DTR and RTS. Without this, you may get outright failure or very erratic behavior from

the modem.

•   The DTR signal can reset the TINI stick if you connect it. So if you tie the DTR to logic “1” to make the modem work, you need to keep that logic “1” from the TINI stick’s DTR line by removing the DTR jumper, or by not connecting it all the way through to the TINI DB9 connector.

Figure 13-8a shows a cable we made by cutting a straight-through RS232 cable in half, splicing in a 9V battery, and reconnecting it to be cross-over. This is not an elegant solution, but it works well for experimentation. No matter how you choose to construct it, it will need to implement the functionality shown. Chapter 9 has additional information on RS232.

Figure 13-8b: Another special crossover modem cable for use with a TINI

Figure 13-8b shows a different version of a special cross-over cable. This one connects the modem signal on the CTS line to the RTS pin and the modem signal on the DSR to the DTR pin. This type of connector works if your modem sends traditional status signals on the CTS and DSR lines. This may not work if you are using a wireless modem or a wireless device with a built-in modem. You will  need to do a little experimentation. This cable worked well with the external modems we used, but it did not work with the cell phone that we were using as a modem.

Modem AT commands/HyperTerminal

Modem control is handled by sending  the modem a series of commands through the serial cable. For many years Hayes was the standard in modems. As the number of modem manufacturers grew, most manufacturers adhered to a somewhat loosely defined standard set of commands that was originally determined by Hayes. These are now called the AT commands. Below is a table of commonly used AT commands and their meaning.

Table 3-3: Some Basic AT commands

The syntax associated with AT commands can be summarized as follows:

•  Commands from the table are preceded by “AT” and followed by a carriage return “\r”.

•  Multiple commands can be strung together on one line, up to forty characters.

The downside of this is that if something goes wrong and the command line is

rejected, you won’t know which command caused the problem. That’s why it’s common to see one or two commands per line.

There are a great many more AT commands than those shown above. If you’re not familiar with AT commands, the best way  to learn about them is to experiment. Windows environments have a terminal-emulating program called HyperTerminal

that allows you to issue commands directly to your modem. The following discussion assumes that you have a modem attached to your PC via a COM port. Click on the HyperTerminal icon to run it (HyperTerminal can usually be found in the Windows menus in Programs…Accessories… Communications…HyperTerminal). A popup window will appear asking you for a connection name. Put in anything you want, and click OK.

In the next window that pops up, configure the port as shown.

In the next window that pops up, configure the port as shown.

Figure 13-11: Configuring the COM port for HyperTerminal

You can now type AT commands directly to your modem and watch the result. This can be useful in debugging modem problems. You can take the modem that you plan to connect to the TINI, connect it to your PC via a straight-through cable, and then use HyperTerminal to test the series of AT commands that you plan to use when it’s connected to the TINI stick. This way you eliminate as many problems with the modem as possible before you connect it to the TINI stick. If you are using Linux, minicom can be used to try your modem commands as well.

Once you have a set of AT commands that works well with your modem (such as the set of commands with which you will dial into an ISP), you can see if the modem works the same way when connected to the TINI stick. To help with this step, we wrote up a simple Modem class and test program, ModemDialTest.

Figure 13-12: AT commands in HyperTerminal

Getting TINI to talk to a modem: The Modem class

In the previous section we discussed ways of testing modem dialing strings using a terminal emulator on a PC. Now we’re going to connect the modem to a TINI stick through our special  cross-over cable (Figure 13-8)  and send it AT commands. We will use three  classes: Modem, ModemATTest, and ModemDialTest. The Modem class contains utilities for opening a serial port, sending AT commands to a modem and printing out the response, calling an ISP and waiting for the CONNECT response, and answering an incoming call. We’ll present the program in its entirety first, then go through it in detail  ModemATTest, ModemDialTest are  programs that test our Modem class.

Listing 13-1: Modem.java

import java.io.*;

import javax.comm.*;

public class Modem  {

SerialPort serialPort = null;

public boolean openSerialPort(String port) {

boolean flag = true;

try {

CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(port); serialPort = (SerialPort)portId.open(“ppp0”, 0); serialPort.setSerialPortParams(19200,
SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
serialPort.setFlowControlMode(serialPort.FLOWCONTROL_NONE);
} catch(Exception e) { System.out.println(e);
System.out.println(“We failed to open the port”);
flag = false;
}
return flag;
}
public String sendCommand(String command) { String modemResponse = “”;
try {
InputStream inputStream = serialPort.getInputStream(); OutputStream outputStream = serialPort.getOutputStream(); System.out.println(“Sending: “ + command); System.out.println(“Receiving: “); outputStream.write((command+’\r’).getBytes()); Thread.sleep(500);
int bytesToGet = inputStream.available();
if (bytesToGet > 0) {
byte[] readBuffer = new byte[bytesToGet];
int bytesToPrint = inputStream.read(readBuffer, 0, bytesToGet); modemResponse = new String(readBuffer, 0, bytesToPrint); System.out.println(modemResponse);
}
} catch(Exception e) {}
return modemResponse;
}

public boolean dial(String[] atCommands) { String atCommand = “”;
String modemMessage;
int bytesToGet;
int n=0;
boolean flag = true;
try {
InputStream inputStream = serialPort.getInputStream();
for (int i=0; i<atCommands.length; i++) {
sendCommand(atCommands[i]);
}
boolean notConnected = true;
modemMessage = “”;
while ((n<180) && (notConnected)) {
try {Thread.sleep(500);} catch(Exception e) {}

bytesToGet = inputStream.available();

if (bytesToGet > 0) {

byte[] readBuffer = new byte[bytesToGet];

int bytesToPrint = inputStream.read(readBuffer, 0, bytesToGet);

modemMessage = modemMessage + (new String(readBuffer, 0, bytesToPrint));

if (modemMessage.indexOf(“CONNECT”) != -1) { notConnected = false; System.out.println(modemMessage);

}

}

n++;

}

} catch (IOException e) { System.out.println(e); flag = false;

}

return flag;

}

public boolean answer() { String atCommand = null; String modemMessage; boolean flag = true;

int n=0;

int m=0;

int bytesToGet=0;

try {

InputStream inputStream = serialPort.getInputStream();

atCommand = “AT”; sendCommand(atCommand); modemMessage = “”;

System.out.println(“Wait for 3 rings”);

while (n<3) {

bytesToGet = inputStream.available();

if (bytesToGet > 0) {

byte[] readBuffer = new byte[bytesToGet];

int bytesToPrint = inputStream.read(readBuffer, 0, bytesToGet);

modemMessage = modemMessage + (new String(readBuffer, 0, bytesToPrint));

if (modemMessage.indexOf(“RING”) != -1) {

n++; System.out.println(modemMessage); modemMessage = “”;

}

}

}

atCommand = “ATA”; sendCommand(atCommand); boolean notConnected = true;

modemMessage = “”; System.out.println(“Wait for CONNECT”); while ((m<180) & (notConnected)) {

try {Thread.sleep(500);} catch(Exception e) {}

bytesToGet = inputStream.available();

if (bytesToGet > 0) {

byte[] readBuffer = new byte[bytesToGet];

int bytesToPrint = inputStream.read(readBuffer, 0, bytesToGet);

modemMessage = modemMessage + (new String(readBuffer, 0, bytesToPrint));

if (modemMessage.indexOf(“CONNECT”) != -1) { notConnected = false; System.out.println(modemMessage);

}

}

m++;

}

} catch (IOException e) { System.out.println(e); flag = false;

}

return flag;

}

}

We start out by importing the necessary Java class libraries. No TINI specific libraries are used in the Modem class. We also make our class declaration, and declare our member variable serialPort.

import java.io.*;

import javax.comm.*;

public class Modem  {

SerialPort serialPort = null;

The openSerialPort() method takes the name of the port we wish to open as an argument and returns a Boolean representing whether this operation succeeded. Note that we set the port for 8 databits, 1 stop bit, no parity bit, and no flow control. Since we will be using this later for establishing PPP connections, we supplied “ppp0” as the application name assigned to the port in the open() method.

public boolean openSerialPort(String port) {

boolean flag = true;

try {

CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(port); serialPort = (SerialPort)portId.open(“ppp0”, 0); serialPort.setSerialPortParams(19200,

SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

serialPort.setFlowControlMode(serialPort.FLOWCONTROL_NONE);

} catch(Exception e) { System.out.println(e);

System.out.println(“We failed to open the port”);

flag = false;

}

return flag;

}

The sendCommand() method takes a string representing an AT command and sends it to the port, then prints and returns the response. One thing to note here is that before we send the AT command to the port, we append a carriage return “\r” to the string. This is required syntax and by placing it here it keeps us from having to deal with it later. The half-second delay we introduce next may or may not be necessary, depending on your modem. Without it, we found that we sometimes didn’t read the entire response back from the modem, as if we tried to read it before the modem was ready. The sendCommand()  method is fairly generic.

public String sendCommand(String command) { String modemResponse = “”;

try {

InputStream inputStream = serialPort.getInputStream(); OutputStream outputStream = serialPort.getOutputStream(); System.out.println(“Sending: “ + command); System.out.println(“Receiving: “); outputStream.write((command+’\r’).getBytes()); Thread.sleep(500);

int bytesToGet = inputStream.available();

if (bytesToGet > 0) {

byte[] readBuffer = new byte[bytesToGet];

int bytesToPrint = inputStream.read(readBuffer, 0, bytesToGet); modemResponse = new String(readBuffer, 0, bytesToPrint); System.out.println(modemResponse);

}

} catch(Exception e) {}

return modemResponse;

}

The dial() method takes as its argument an array of strings representing the series of AT commands required to connect to an ISP. It uses the sendCommand() method to send the AT commands, then waits for the word CONNECT to be in the response after it has sent the last AT command in the sequence. It returns true if we succeed in connecting, false if something goes wrong. The method will wait 90 seconds for the CONNECT, then it will give up. The timeout is implemented in half-second increments, by looping

180 times. The method prints out the CONNECT if it receives it.

public boolean dial(String[] atCommands) { String atCommand = “”;

String modemMessage;

int bytesToGet;

int n=0;

boolean flag = true;

try {

InputStream inputStream = serialPort.getInputStream();

for (int i=0; i<atCommands.length; i++) {

sendCommand(atCommands[i]);

}

boolean notConnected = true;

modemMessage = “”;

while ((n<180) && (notConnected)) {

try {Thread.sleep(500);} catch(Exception e) {}

bytesToGet = inputStream.available();

if (bytesToGet > 0) {

byte[] readBuffer = new byte[bytesToGet];

int bytesToPrint = inputStream.read(readBuffer, 0, bytesToGet);

modemMessage = modemMessage + (new String(readBuffer, 0, bytesToPrint));

if (modemMessage.indexOf(“CONNECT”) != -1) { notConnected = false; System.out.println(modemMessage);

}

}

n++;

}

} catch (IOException e) { System.out.println(e); flag = false;

}

return flag;

}

The answer() method waits for an incoming call by looking for the RING response from the modem three times. When it sees it, it uses the sendCommand() method to instruct the modem to answer via the ATA command. Then it waits 90 seconds for the CONNECT to appear before giving up. The issue of answering an incoming call and then forming a connection has proven to be the most problematic of this entire

chapter. It can take a lot of modem experimentation to get this to work; from modem to modem, the required trick seems to vary.

public boolean answer() { String atCommand = null; String modemMessage; boolean flag = true;

int n=0;

int m=0;

int bytesToGet=0;

try {

InputStream inputStream = serialPort.getInputStream();

atCommand = “AT”;

sendCommand(atCommand); modemMessage = “”; System.out.println(“Wait for 3 rings”); while (n<3) {

bytesToGet = inputStream.available();

if (bytesToGet > 0) {

byte[] readBuffer = new byte[bytesToGet];

int bytesToPrint = inputStream.read(readBuffer, 0, bytesToGet);

modemMessage = modemMessage + (new String(readBuffer, 0, bytesToPrint));

if (modemMessage.indexOf(“RING”) != -1) {

n++; System.out.println(modemMessage); modemMessage = “”;

}

}

}

atCommand = “ATA”; sendCommand(atCommand); boolean notConnected = true; modemMessage = “”;

System.out.println(“Wait for CONNECT”);

while ((m<180) && (notConnected)) {

try {Thread.sleep(500);} catch(Exception e) {}

bytesToGet = inputStream.available();

if (bytesToGet > 0) {

byte[] readBuffer = new byte[bytesToGet];

int bytesToPrint = inputStream.read(readBuffer, 0, bytesToGet);

modemMessage = modemMessage + (new String(readBuffer, 0, bytesToPrint));

if (modemMessage.indexOf(“CONNECT”) != -1) { notConnected = false; System.out.println(modemMessage);

}

}

m++;

}

} catch (IOException e) { System.out.println(e); flag = false;

}

return flag;

}

}

Our first goal with the Modem class is to see that we can get a TINI stick to send AT commands to a modem. We’ll test this out by using the Modem class to send AT commands to an external modem from a TINI via our special cross-over cable

(Figure 13-8). The programs we will use are ModemATTest and ModemDialTest. They use the Modem class. Let’s take a look at ModemATTest.

Random Posts

Comments are closed.