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.
1 Introduction
2 Connecting
3 Sending Data
4 Receiving Data
5 Disconnecting
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\.)
To establish a connection, MTalk does the following:
ModemConnect()
(defined in modem.goc) to make the connectionCode 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:
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:
To send data, MTalk does the following:
ModemSend()
(defined in modem.goc) to send the dataCode 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:
ModemSend()
returns a
StreamError
value:
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:
ModemReceive()
(defined in modem.goc) to read incoming dataCode 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:
ModemReceive()
writes the number of bytes actually read in to this parameter.
ModemReceive()
returns a
StreamError
value:
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.