This document is a single-page version of a a multi-page document, suitable for easy printing.

Modem Connections

This chapter discusses how to set up your Nokia 9000i Communicator to emulate a Hayes-compatible modem to establish a direct serial connection.

On the Nokia 9000i Communicator, you connect through a "virtual" serial port and use a stream to read and write data to this port just as you would with an actual serial port. (For more information on streams, see the Streams chapter of the Concepts Book.) The modem driver handles data notifications from the port.

The ModemC Library provides an interface to the modem driver while the VirtualSerial Library provides a stream interface to the "virtual" serial port. In addition, the modem.goc module provides a set of basic routines for establishing a modem connection. You can drop this module into any application that uses a modem.


Modem Connections: 1 Introduction

Sending and receiving data over a serial line is similar to sending and receiving data through a socket connection. You configure your hardware, make the connection, open streams for sending and receiving data, and close the connection ( i.e. , hang up).

This chapter uses the MTalk sample application to illustrate how to set up and use a modem connection. MTalk establishes a simple "talk" exchange between two devices over a modem connection. It provides user interface for taking text input (which it sends to the other end of the connection) and for displaying data received from the other end of the connection.

(Note: Both mtalk.goc, the sample application discussed in this chapter, and modem.goc, the module that provides its basic modem connectivity, live in \APPL\SDK_9000\MTALK\.)


Modem Connections: 2 Connecting

To establish a connection, MTalk does the following:

Code Display 9-1 Making A Serial Line Connection

    /*
     * phoneNum - Phone number to dial.
     * retVal - Return value of ModemConnect().
     */
    char        phoneNum[MAX_PHONE_NUM_LENGTH + 1];
    Boolean     retVal;
    /*
     * Allocate a global memory block to be used as a buffer to hold received data.
     * A local buffer variable in the read function would use up
     * precious stack space, and a global buffer variable
     * would increase the size of the application's fixed data segment,
     * which degrades overall memory manager performance.
     *
     * Allocating it to be DISCARDABLE has further advantages.
     * Because we don't care what happens to the contents after they
     * have been processed, we'll allow the memory manager to throw
     * the block out if it decides it needs the heap space. This
     * means a little extra work for us whenever we want to use the
     * block, since we have to check for this condition, and
     * reallocate if necessary.
     */
    recvBuffer = MemAlloc( INPUT_BUFFER_SIZE, HF_DISCARDABLE, 0 );
    /*
     * If the buffer was not successfully allocated, memory must be getting low
     * so shut down.
     */
    if ( recvBuffer == NullHandle )
    {
	@send application::MSG_META_QUIT();
	return;
    }
    /*
     * Get the phone number to dial.
     */
    @call MTalkPhoneNumberText::MSG_VIS_TEXT_GET_ALL_PTR( phoneNum );
    /*
     * Create the modem connection and register the object and messages
     * it will receive when data is received or when the call has ended.
     * Note: in this example, oself refers to the process object.
     * Remember that ModemConnect() returns FALSE if it successfully makes
     * the connection.
     */
    retVal = ModemConnect( phoneNum,
			 oself,
			 MSG_MTALK_PROCESS_READ_DATA,
			 MSG_MTALK_PROCESS_CALL_ENDED );
    /*
     * If the connection failed, free the buffer and return. (Note: after freeing
     * any allocated memory, it is good practice to re-set handles and pointers to
     * Null so that you can test for a Null value later.)
     */
    if ( retVal )
    {
	MemFree( recvBuffer );
	recvBuffer = NullHandle;
	return;
    }

ModemConnect() takes care of the "housekeeping" details involved in making a connection. Specifically, this routine takes care of the following:

ModemConnect() takes four parameters:

phoneNumber
Char pointer to the phone number to dial.
dataReceiver
Optr to the object that will receive data or call end notification.
dataReceiptMessage
Message sent when data arrives on the port.
endCallMessage
Message sent when the call has ended.

ModemConnect() returns FALSE if the attempt to connect is successful. If the attempt is unsuccessful, it displays a dialog box notifying the user of the error. Possible error messages include:

"Connectivity Problem:
Data calls cannot be made while another call is active."
"Connectivity Problem: Line is busy. Try again later."
"Connectivity Problem:
No answer. Check the phone number and try again later."
"Connectivity Problem: General Error."
"Connectivity Problem: Error initializing modem."

Modem Connections: 3 Sending Data

To send data, MTalk does the following:

Code Display 9-2 Sending Data Over A Serial Connection

    /*
     * textBlock - Handle of block containing text to send.
     * textPtr - Pointer to text to send.
     * textSize - Size of text to send.
     */
    MemHandle		textBlock;
    char *		textPtr;
    int		textSize;
    /*
     * Get text that user has typed and put it into a newly created memory block.
     * Lock the block down on the heap and obtain a pointer to it.
     */
    textBlock = @call MTalkSendText::MSG_VIS_TEXT_GET_ALL_BLOCK( NullHandle );
    textPtr = MemLock( textBlock );
    /*
     * Get the size of the text then send it.
     */
    textSize = LocalStringSize( textPtr );
    if ( ModemSend( textSize, (byte*)textPtr ) != STREAM_NO_ERROR )
    {
	/* handle the error */
    }
    /*
     * Once the text has been sent, free its block.
     */
    MemFree( textBlock );

ModemSend() calls the VirtualSerial Library routine, VirtualSerialWrite() , to send the passed data. VirtualSerialWrite() writes the passed data to stream and, if necessary, blocks until enough space in the stream becomes available. (If you don't want to block, pass STREAM_NO_BLOCK in VirtualSerialWrite() .)

ModemSend() takes two arguments:

dataLength
Length of the string (not counting the Null character).
data
Pointer to the data to be sent.

ModemSend() returns a StreamError value:

STREAM_NO_ERROR
if it successfully sends the data.
STREAM_CLOSED
if the stream is not open.
STREAM_SHORT_READ_WRITE
if the required amount of space was not available and STREAM_NO_BLOCK was specified.

Modem Connections: 4 Receiving Data

When the modem driver receives notification that data has arrived on the port, it sends a pre-registered message to a specified object. (You specify both the object and the message when you call ModemConnect() .)

In the MTalk example, the driver sends MSG_MTALK_PROCESS_READ_DATA to the process object when data has arrived on the port. The handler for this message is shown in Receiving Data Over A Serial Connection .

To receive data, MTalk does the following:

Code Display 9-3 Receiving Data Over A Serial Connection

@method MTalkProcessClass, MSG_MTALK_PROCESS_READ_DATA
{
	/*
	 * recvDataSize - Amout of data actually read in.
	 * recvDataPtr - Pointer to data to read in.
	 * retVal - Return value of ModemReceive().
	 */
    word		revDataSize;
    char *		recvDataPtr;
    StreamError		retVal;
	/*
	 * Check to see if connection is still open. Because of message queueing
	 * delays, we may have received this data notification after the connection
	 * has been closed.
	 *
	 * ConnectionOpen - global flag that keeps track of whether
	 * the connection is open or closed.
	 */
    if ( ConnectionOpen == FALSE )
    {
	return;
    }
	/*
	 * Lock the input buffer down on the heap, and obtain a pointer to it.
	 * (Note: This buffer is a memory block that was allocated in the
	 * "Connecting" example.)
	 */
    recvDataPtr = MemLock( recvBuffer );
	/*
	 * A NULL pointer means that the memory manager has gone ahead
	 * and discarded the buffer block since the last time it was
	 * unlocked.  So we need to reallocate it here.  Note that the
	 * MemHandle is still in use; it just isn't bound to any block of memory.
	 */
    if ( recvDataPtr == NULL )
    {
	    /*
	     * Reallocate the block, locking it immediately,
	     * so it doesn't get discarded again. If the block cannot
	     * be re-allocated, memory must be low so close the connection.
	     */
	if (( MemReAlloc( recvBuffer, RECV_BUFFER_SIZE, HAF_LOCK ) == NullHandle)
	{
		@send self::MSG_MTALK_PROCESS_CLOSE();
		return;
	}
	    /*
	     * After re-allocating the locked block, dereference its handle
	     * to obtain a pointer to it.
	     */
	recvDataPtr = MemDeref( recvBuffer );
    }
	/*
	 * Read available data up to the size of our buffer then display it.
	 */
	if ( ModemReceive( RECV_BUFFER_SIZE, (byte*)recvDataPtr, &recvDataSize )
							== STREAM_NO_ERROR )
	{
	    @call MTalkOutText::MSG_VIS_TEXT_APPEND_PTR( recvDataPtr, 
						     recvDataSize );
	}
	/*
	 * If the read data filled the entire buffer, there's a good
	 * chance more data is available so send the message through the
	 * process object's queue again.  (We do not want to keep reading
	 * in a loop in case the other side dumped a very large stream
	 * of data; this would keep the process object from handling
	 * other messages in its queue.)
	 */
    if ( RECV_BUFFER_SIZE == recvDataSize ) {
	@send ,forceQueue self::MSG_MTALK_PROCESS_READ_DATA();
    }
	/*
	 * Once all the data has been read, unlock the buffer block.
	 */
    MemUnlock( recvBuffer );
} /* end of MSG_MTALK_PROCESS_READ_DATA */

Like ModemSend() , ModemReceive() calls a VirtualSerial routine, VirtualSerialRead() , to read incoming data. Because no blocking is specified, VirtualSerialRead() reads only data that is currently available in the stream. (If you want the routine to block until there is enough data in the stream to fill the entire buffer, pass STREAM_BLOCK in VirtualSerialRead() .)

ModemReceive() takes three arguments:

recvBufLength
Size of allocated buffer (in bytes).
recvBuf
Byte pointer to the buffer.
bytesReceived
Word pointer. ModemReceive() writes the number of bytes actually read in to this parameter.

ModemReceive() returns a StreamError value:

STREAM_NO_ERROR
if it successfully reads the data.
STREAM_CLOSED
if the stream is not open.
STREAM_SHORT_READ_WRITE
if the required amount of data was not available and STREAM_NO_BLOCK was specified.

Modem Connections: 5 Disconnecting

When a call has ended, the modem driver sends a pre-registered message to a specified object. (You specify both the message and the receiving object in ModemConnect() .)

In the MTalk example, the modem driver sends MSG_MTALK_PROCESS_CALL_ENDED to the process object when the call has ended for any reason. This message checks if the connection is still open and if it is, calls yet another message, MSG_MTALK_PROCESS_CLOSE, to close it. The handler for MSG_MTALK_PROCESS_CLOSE is shown in Ending A Serial Line Connection .

Code Display 9-4 Ending A Serial Line Connection

@method MTalkProcessClass, MSG_MTALK_PROCESS_CLOSE
{
	/* close the connection */
    ModemDisconnect();
	/*
	 * Free the buffer and re-set the (global) "connection" flag
	 * to indicate the connection is closed.
	 */
    MemFree( recvBuffer );
    recvBuffer = NullHandle;
    ConnectionOpen = FALSE;
} /* end of MSG_MTALK_PROCESS_CLOSE */

ModemDisconnect() , defined in modem.goc, takes care of the following:

ModemDisconnect() takes no parameters and returns void.

 


This document is a single-page version of a a multi-page document, suitable for easy printing.