Copyright (c) Hyperion Entertainment and contributors.

Difference between revisions of "Serial Device"

From AmigaOS Documentation Wiki
Jump to navigation Jump to search
 
(31 intermediate revisions by 4 users not shown)
Line 1: Line 1:
  +
[[Category:Devices|Serial]]
{{NeedUpdate}}
 
 
== Serial Device ==
 
== Serial Device ==
   
 
The serial device provides a hardware-independent interface to the Amiga’s built-in RS-232C compatible serial port. Serial ports have a wide range of uses, including communication with modems, printers, MIDI devices, and other computers. The same device interface can be used for additional “byte stream oriented devices”—usually more serial ports. The serial device is based on the conventions of Exec device I/O, with extensions for parameter setting and control.
 
The serial device provides a hardware-independent interface to the Amiga’s built-in RS-232C compatible serial port. Serial ports have a wide range of uses, including communication with modems, printers, MIDI devices, and other computers. The same device interface can be used for additional “byte stream oriented devices”—usually more serial ports. The serial device is based on the conventions of Exec device I/O, with extensions for parameter setting and control.
   
  +
{| class="wikitable"
2c'''Serial Device Characteristics'''<br />
 
  +
|+ Serial Device Characteristics
'''Modes''' &amp; Exclusive<br />
 
  +
| Modes
&amp; Shared Access<br />
 
  +
| Exclusive, Shared Access
'''Baud Rates''' &amp; 110–292,000<br />
 
  +
|-
'''Handshaking''' &amp; Three-Wire<br />
 
  +
| Baud Rates
&amp; Seven-Wire<br />
 
  +
| 110–292,000
 
  +
|-
  +
| Handshaking
  +
| Three-Wire, Seven-Wire
  +
|}
   
 
== Serial Device Commands and Functions ==
 
== Serial Device Commands and Functions ==
Line 41: Line 45:
 
== Device Interface ==
 
== Device Interface ==
   
The serial device operates like the other Amiga devices. To use it, you must first open the serial device, then send I/O requests to it, and then close it when finished. See the “Introduction to Amiga System Devices” chapter for general information on device usage.
+
The serial device operates like the other Amiga devices. To use it, you must first open the serial device, then send I/O requests to it, and then close it when finished. See [[Exec_Device_I/O|Exec Device I/O]] for general information on device usage.
   
 
The I/O request used by the serial device is called IOExtSer.
 
The I/O request used by the serial device is called IOExtSer.
   
  +
<syntaxhighlight>
 
  +
struct IOExtSer
 
<pre>struct IOExtSer
 
 
{
 
{
 
struct IOStdReq IOSer;
 
struct IOStdReq IOSer;
Line 61: Line 64:
 
UBYTE io_SerFlags; /* serial device flags */
 
UBYTE io_SerFlags; /* serial device flags */
 
UWORD io_Status; /* status of serial port and lines */
 
UWORD io_Status; /* status of serial port and lines */
};</pre>
+
};
  +
</syntaxhighlight>
  +
 
See the include file devices/serial.h for the complete structure definition.
 
See the include file devices/serial.h for the complete structure definition.
   
Line 68: Line 73:
 
Three primary steps are required to open the serial device:
 
Three primary steps are required to open the serial device:
   
* Create a message port using CreatePort(). Reply messages from the device must be directed to a message port.
+
* Create a message port using AllocSysObject() and the ASOT_PORT type. Reply messages from the device must be directed to a message port.
* Create an extended I/O request structure of type IOExtSer using CreateExtIO(). CreateExtIO() will initialize the I/O request to point to your reply port.
+
* Create an extended I/O request structure of type IOExtSer using AllocSysObject() and the ASOT_IOREQUEST type. AllocSysObject() will initialize the I/O request to point to your reply port.
 
* Open the serial device. Call OpenDevice(), passing the I/O request.
 
* Open the serial device. Call OpenDevice(), passing the I/O request.
   
  +
<syntaxhighlight>
<pre>struct MsgPort *SerialMP; /* Define storage for one pointer */
 
  +
struct MsgPort *SerialMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
struct IOExtSer *SerialIO; /* Define storage for one pointer */
 
   
if (SerialMP=CreatePort(0,0) )
+
if (SerialMP != NULL)
  +
{
if (SerialIO=(struct IOExtSer *)
 
  +
struct IOExtSer *SerialIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
CreateExtIO(SerialMP,sizeof(struct IOExtSer)) )
 
  +
ASOIOR_ReplyPort, SerialMP,
SerialIO-&gt;io_SerFlags=SERF_SHARED; /* Turn on SHARED mode */
 
if (OpenDevice(SERIALNAME,0L,(struct IORequest *)SerialIO,0) )
+
ASOIOR_Size, sizeof(struct IOExtSer),
  +
TAG_END);
printf(&quot;%s did not open\n&quot;,SERIALNAME);</pre>
 
During the open, the serial device pays attention to a subset of the flags in the io_SerFlags field. The flag bits, SERF_SHARED and SERF_7WIRE, must be set before open. For consistency, the other flag bits should also be properly set. Full descriptions of all flags will be given later.
 
   
  +
if (SerialIO != NULL)
  +
{
  +
SerialIO->io_SerFlags = SERF_SHARED; /* Turn on SHARED mode */
  +
if (IExec->OpenDevice(SERIALNAME, 0, (struct IORequest *)SerialIO, 0) )
  +
IDOS->Printf("%s did not open\n", SERIALNAME);
  +
</syntaxhighlight>
   
  +
During the open, the serial device pays attention to a subset of the flags in the io_SerFlags field. The flag bits, SERF_SHARED and SERF_7WIRE, must be set before open. For consistency, the other flag bits should also be properly set. Full descriptions of all flags will be given later.
   
 
The serial device automatically fills in default settings for all parameters—stop bits, parity, baud rate, etc. For the default unit, the settings will come from Preferences. You may need to change certain parameters, such as the baud rate, to match your requirements. Once the serial device is opened, all characters received will be buffered, ''even if there is no current request for them''.
 
The serial device automatically fills in default settings for all parameters—stop bits, parity, baud rate, etc. For the default unit, the settings will come from Preferences. You may need to change certain parameters, such as the baud rate, to match your requirements. Once the serial device is opened, all characters received will be buffered, ''even if there is no current request for them''.
Line 91: Line 102:
 
You read from the serial device by passing an IOExtSer to the device with CMD_READ set in io_Command, the number of bytes to be read set in io_Length and the address of the read buffer set in io_Data.
 
You read from the serial device by passing an IOExtSer to the device with CMD_READ set in io_Command, the number of bytes to be read set in io_Length and the address of the read buffer set in io_Data.
   
  +
<syntaxhighlight>
<pre>#define READ_BUFFER_SIZE 256
 
  +
#define READ_BUFFER_SIZE 256
 
char SerialReadBuffer[READ_BUFFER_SIZE]; /* Reserve SIZE bytes of storage */
 
char SerialReadBuffer[READ_BUFFER_SIZE]; /* Reserve SIZE bytes of storage */
   
SerialIO-&gt;IOSer.io_Length = READ_BUFFER_SIZE;
+
SerialIO->IOSer.io_Length = READ_BUFFER_SIZE;
SerialIO-&gt;IOSer.io_Data = (APTR)&amp;SerialReadBuffer[0];
+
SerialIO->IOSer.io_Data = (APTR)&SerialReadBuffer[0];
SerialIO-&gt;IOSer.io_Command = CMD_READ;
+
SerialIO->IOSer.io_Command = CMD_READ;
DoIO((struct IORequest *)SerialIO);</pre>
+
IExec->DoIO((struct IORequest *)SerialIO);
  +
</syntaxhighlight>
  +
 
If you use this example, your task will be put to sleep waiting until the serial device reads 256 bytes (or terminates early). Early termination can be caused by error conditions such as a break. The number of characters ''actually received'' will be recorded in the io_Actual field of the IOExtSer structure you passed to the serial device.
 
If you use this example, your task will be put to sleep waiting until the serial device reads 256 bytes (or terminates early). Early termination can be caused by error conditions such as a break. The number of characters ''actually received'' will be recorded in the io_Actual field of the IOExtSer structure you passed to the serial device.
   
Line 106: Line 120:
 
To write a NULL-terminated string, set the length to -1; the device will output from your buffer until it encounters and transmits a value of zero (0x00).
 
To write a NULL-terminated string, set the length to -1; the device will output from your buffer until it encounters and transmits a value of zero (0x00).
   
  +
<syntaxhighlight>
<pre>SerialIO-&gt;IOSer.io_Length = -1;
 
SerialIO-&gt;IOSer.io_Data = (APTR)&quot;Life is but a dream. &quot;;
+
SerialIO->IOSer.io_Length = -1;
SerialIO-&gt;IOSer.io_Command = CMD_WRITE;
+
SerialIO->IOSer.io_Data = (APTR)"Life is but a dream.";
  +
SerialIO->IOSer.io_Command = CMD_WRITE;
DoIO((struct IORequest *)SerialIO); /* execute write */</pre>
 
  +
IExec->DoIO((struct IORequest *)SerialIO); /* execute write */
  +
</syntaxhighlight>
  +
 
The length of the request is -1, meaning we are writing a NULL-terminated string. The number of characters sent can be found in io_Actual.
 
The length of the request is -1, meaning we are writing a NULL-terminated string. The number of characters sent can be found in io_Actual.
   
Line 118: Line 135:
 
All IORequests must be complete before CloseDevice(). Abort any pending requests with AbortIO().
 
All IORequests must be complete before CloseDevice(). Abort any pending requests with AbortIO().
   
  +
<syntaxhighlight>
<pre>if (!(CheckIO(SerialIO)))
 
  +
if (!(IExec->CheckIO(SerialIO)))
 
{
 
{
AbortIO((struct IORequest *)SerialIO); /* Ask device to abort request, if pending */
+
IExec->AbortIO((struct IORequest *)SerialIO); /* Ask device to abort request, if pending */
 
}
 
}
WaitIO((struct IORequest *)SerialIO); /* Wait for abort, then clean up */
+
IExec->WaitIO((struct IORequest *)SerialIO); /* Wait for abort, then clean up */
CloseDevice((struct IORequest *)SerialIO);</pre>
+
IExec->CloseDevice((struct IORequest *)SerialIO);
  +
</syntaxhighlight>
  +
 
== A Simple Serial Port Example ==
 
== A Simple Serial Port Example ==
   
  +
<syntaxhighlight>
<pre>/*
 
  +
/*
 
* Simple_Serial.c
 
* Simple_Serial.c
 
*
 
*
* This is an example of using the serial device. First, we will attempt
+
* This is an example of using the serial device.
* to create a message port with CreateMsgPort(). Next, we will attempt
 
* to create the IORequest with CreateExtIO(). Then, we will attempt to
 
* open the serial device with OpenDevice(). If successful, we will write
 
* a NULL-terminated string to it and reverse our steps. If we encounter
 
* an error at any time, we will gracefully exit.
 
*
 
* Compile with SAS C 5.10 lc -b1 -cfistq -v -y -L
 
 
*
 
*
 
* Run from CLI only
 
* Run from CLI only
 
*/
 
*/
   
#include &lt;exec/types.h&gt;
+
#include <exec/types.h>
#include &lt;exec/memory.h&gt;
+
#include <exec/memory.h>
#include &lt;exec/io.h&gt;
+
#include <exec/io.h>
#include &lt;devices/serial.h&gt;
+
#include <devices/serial.h>
 
#include &lt;clib/exec_protos.h&gt;
 
#include &lt;clib/alib_protos.h&gt;
 
 
#include &lt;stdio.h&gt;
 
   
  +
#include <proto/exec.h>
#ifdef LATTICE
 
  +
#include <proto/dos.h>
int CXBRK(void) { return(0); } /* Disable SAS CTRL/C handling */
 
int chkabort(void) { return(0); } /* really */
 
#endif
 
   
void main(void)
+
int main()
 
{
 
{
struct MsgPort *SerialMP; /* pointer to our message port */
+
struct MsgPort *SerialMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
struct IOExtSer *SerialIO; /* pointer to our IORequest */
 
   
 
/* Create the message port */
 
/* Create the message port */
if (SerialMP=CreateMsgPort())
+
if (SerialMP != NULL)
 
{
 
{
 
/* Create the IORequest */
 
/* Create the IORequest */
if (SerialIO = (struct IOExtSer *)
+
struct IOExtSer *SerialIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
CreateExtIO(SerialMP,sizeof(struct IOExtSer)))
+
ASOIOR_ReplyPort, SerialMP,
  +
ASOIOR_Size, sizeof(struct IOExtSer),
  +
TAG_END);
  +
  +
if (SerialIO != NULL)
 
{
 
{
 
/* Open the serial device */
 
/* Open the serial device */
if (OpenDevice(SERIALNAME,0,(struct IORequest *)SerialIO,0L))
+
if (IExec->OpenDevice(SERIALNAME, 0, (struct IORequest *)SerialIO, 0))
   
 
/* Inform user that it could not be opened */
 
/* Inform user that it could not be opened */
printf(&quot;Error: %s did not open\n&quot;,SERIALNAME);
+
IDOS->Printf("Error: %s did not open\n",SERIALNAME);
 
else
 
else
 
{
 
{
 
/* device opened, write NULL-terminated string */
 
/* device opened, write NULL-terminated string */
SerialIO-&gt;IOSer.io_Length = -1;
+
SerialIO->IOSer.io_Length = -1;
SerialIO-&gt;IOSer.io_Data = (APTR)&quot;Amiga &quot;;
+
SerialIO->IOSer.io_Data = (APTR)"Amiga ";
SerialIO-&gt;IOSer.io_Command = CMD_WRITE;
+
SerialIO->IOSer.io_Command = CMD_WRITE;
if (DoIO((struct IORequest *)SerialIO)) /* execute write */
+
if (IExec->DoIO((struct IORequest *)SerialIO)) /* execute write */
printf(&quot;Write failed. Error - %ld\n&quot;,SerialIO-&gt;IOSer.io_Error);
+
IDOS->Printf("Write failed. Error - %ld\n", SerialIO->IOSer.io_Error);
   
 
/* Close the serial device */
 
/* Close the serial device */
CloseDevice((struct IORequest *)SerialIO);
+
IExec->CloseDevice((struct IORequest *)SerialIO);
 
}
 
}
 
/* Delete the IORequest */
 
/* Delete the IORequest */
DeleteExtIO(SerialIO);
+
IExec->FreeSysObject(ASOT_IOREQUEST, SerialIO);
 
}
 
}
 
else
 
else
 
/* Inform user that the IORequest could be created */
 
/* Inform user that the IORequest could be created */
printf(&quot;Error: Could create IORequest\n&quot;);
+
IDOS->Printf("Error: Could create IORequest\n");
   
 
/* Delete the message port */
 
/* Delete the message port */
DeleteMsgPort(SerialMP);
+
IExec->FreeSysOject(ASOT_PORT, SerialMP);
 
}
 
}
 
else
 
else
 
/* Inform user that the message port could not be created */
 
/* Inform user that the message port could not be created */
printf(&quot;Error: Could not create message port\n&quot;);
+
IDOS->Printf("Error: Could not create message port\n");
  +
}</pre>
 
  +
return 0;
<sub>b</sub>oxDoIO() vs. SendIO().The above example code contains some simplifications. The DoIO() function in the example is not always appropriate for executing the CMD_READ or CMD_WRITE commands. DoIO() will not return until the I/O request has finished. With serial handshaking enabled, a write request may ''never'' finish. Read requests will not finish until characters arrive at the serial port. The following sections will demonstrate a solution using the SendIO() and AbortIO() functions.
 
  +
}
  +
</syntaxhighlight>
  +
  +
{{Note|title=DoIO() vs. SendIO()|text=The above example code contains some simplifications. The DoIO() function in the example is not always appropriate for executing the CMD_READ or CMD_WRITE commands. DoIO() will not return until the I/O request has finished. With serial handshaking enabled, a write request may ''never'' finish. Read requests will not finish until characters arrive at the serial port. The following sections will demonstrate a solution using the SendIO() and AbortIO() functions.}}
   
 
== Alternative Modes for Serial Input or Output ==
 
== Alternative Modes for Serial Input or Output ==
Line 205: Line 219:
 
As an alternative to DoIO() you can use an asynchronous I/O request to transmit the command. Asynchronous requests are initiated with SendIO(). Your task can continue to execute while the device processes the command. You can occasionally do a CheckIO() to see if the I/O has completed. The write request in this example will be processed while the example continues to run:
 
As an alternative to DoIO() you can use an asynchronous I/O request to transmit the command. Asynchronous requests are initiated with SendIO(). Your task can continue to execute while the device processes the command. You can occasionally do a CheckIO() to see if the I/O has completed. The write request in this example will be processed while the example continues to run:
   
  +
<syntaxhighlight>
<pre>
 
SerialIO-&gt;IOSer.io_Length = -1;
+
SerialIO->IOSer.io_Length = -1;
SerialIO-&gt;IOSer.io_Data = (APTR)&quot;Save the whales! &quot;;
+
SerialIO->IOSer.io_Data = (APTR)"Save the whales! ";
SerialIO-&gt;IOSer.io_Command = CMD_WRITE;
+
SerialIO->IOSer.io_Command = CMD_WRITE;
SendIO((struct IORequest *)SerialIO);
+
IExec->SendIO((struct IORequest *)SerialIO);
   
printf(&quot;CheckIO %lx\n&quot;,CheckIO((struct IORequest *)SerialIO));
+
IDOS->Printf("CheckIO %lx\n", IExec->CheckIO((struct IORequest *)SerialIO));
printf(&quot;The device will process the request in the background\n&quot;);
+
IDOS->Printf("The device will process the request in the background\n");
printf(&quot;CheckIO %lx\n&quot;,CheckIO((struct IORequest *)SerialIO));
+
IDOS->Printf("CheckIO %lx\n", IExec->CheckIO((struct IORequest *)SerialIO));
WaitIO((struct IORequest *)SerialIO); /* Remove message and cleanup */
+
IExec->WaitIO((struct IORequest *)SerialIO); /* Remove message and cleanup */
  +
</syntaxhighlight>
</pre>
 
   
  +
Most applications will want to wait on multiple signals. A typical application will wait for menu messages from Intuition at the same time as replies from the serial device. The following fragment demonstrates waiting for one of three signals. The Wait() will wake up if the read request ever finishes, or if the user presses <kbd class="keyboard-key nowrap" style="border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;">Ctrl</kbd> + <kbd class="keyboard-key nowrap" style="border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;">C</kbd> or <kbd class="keyboard-key nowrap" style="border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;">Ctrl</kbd> + <kbd class="keyboard-key nowrap" style="border: 1px solid #aaa; border-radius: 0.2em; box-shadow: 0.1em 0.2em 0.2em #ddd; background-color: #f9f9f9; background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); padding: 0.1em 0.3em; font-family: inherit; font-size: 0.85em;">F</kbd> from the Shell. This fragment may be inserted into the above complete example.
Most applications will want to wait on multiple signals. A typical application will wait for menu messages from Intuition at the same time as replies from the serial device. The following fragment demonstrates waiting for one of three signals. The Wait() will wake up if the read request ever finishes, or if the user presses Ctrl-C or Ctrl-F from the Shell. This fragment may be inserted into the above complete example.
 
   
  +
<syntaxhighlight>
<pre>
 
 
/* Precalculate a wait mask for the CTRL-C, CTRL-F and message
 
/* Precalculate a wait mask for the CTRL-C, CTRL-F and message
* port signals. When one or more signals are received,
+
* port signals.  When one or more signals are received,
* Wait() will return. Press CTRL-C to exit the example.
+
* Wait() will return.  Press CTRL-C to exit the example.
 
* Press CTRL-F to wake up the example without doing anything.
 
* Press CTRL-F to wake up the example without doing anything.
 
* NOTE: A signal may show up without an associated message!
 
* NOTE: A signal may show up without an associated message!
*/
+
*/
  +
uint32 WaitMask;
  +
uint32 Temp;
  +
WaitMask = SIGBREAKF_CTRL_C|
  +
SIGBREAKF_CTRL_F|
  +
1L << SerialMP->mp_SigBit;
   
  +
SerialIO->IOSer.io_Command = CMD_READ;
WaitMask = SIGBREAKF_CTRL_C|
 
  +
SerialIO->IOSer.io_Length = READ_BUFFER_SIZE;
SIGBREAKF_CTRL_F|
 
  +
SerialIO->IOSer.io_Data = (APTR)&SerialReadBuffer[0];
1L &lt;&lt; SerialMP-&gt;mp_SigBit;
 
  +
IExec->SendIO((struct IORequest *)SerialIO);
   
  +
IDOS->Printf("Sleeping until CTRL-C, CTRL-F, or serial input\n");
SerialIO-&gt;IOSer.io_Command = CMD_READ;
 
SerialIO-&gt;IOSer.io_Length = READ_BUFFER_SIZE;
 
SerialIO-&gt;IOSer.io_Data = (APTR)&amp;SerialReadBuffer[0];
 
SendIO(SerialIO);
 
 
printf(&quot;Sleeping until CTRL-C, CTRL-F, or serial input\n&quot;);
 
   
 
while (1)
 
while (1)
  +
{
{
 
Temp = Wait(WaitMask);
+
Temp = IExec->Wait(WaitMask);
printf(&quot;Just woke up (YAWN!)\n&quot;);
+
IDOS->Printf("Just woke up (YAWN!)\n");
  +
  +
if (SIGBREAKF_CTRL_C & Temp)
  +
break;
  +
  +
if (IExec->CheckIO((struct IORequest *)SerialIO) ) /* If request is complete... */
  +
{
  +
IExec->WaitIO((struct IORequest *)SerialIO); /* clean up and remove reply */
  +
IDOS->Printf("%ld bytes received\n", SerialIO->IOSer.io_Actual);
  +
break;
  +
}
  +
}
  +
IExec->AbortIO((struct IORequest *)SerialIO); /* Ask device to abort request, if pending */
  +
IExec->WaitIO((struct IORequest *)SerialIO); /* Wait for abort, then clean up */
  +
</syntaxhighlight>
   
  +
{{Note|title=WaitIO() vs. Remove()|text=The WaitIO() function is used above, even if the request is already known to be complete. WaitIO() on a completed request simply removes the reply and cleans up. The Remove() function is ''not acceptable'' for clearing the reply port; other messages may arrive while the function is executing.}}
if (SIGBREAKF_CTRL_C &amp; Temp)
 
break;
 
 
if (CheckIO(SerialIO) ) /* If request is complete... */
 
{
 
WaitIO(SerialIO); /* clean up and remove reply */
 
printf(&quot;%ld bytes received\n&quot;,SerialIO-&gt;IOSer.io_Actual);
 
break;
 
}
 
}
 
AbortIO(SerialIO); /* Ask device to abort request, if pending */
 
WaitIO(SerialIO); /* Wait for abort, then clean up */
 
</pre>
 
 
{| class="wikitable"
 
| ''WaitIO() vs. Remove().'' The WaitIO() function is used above, even if the request is already known to be complete. WaitIO() on a completed request simply removes the reply and cleans up. The Remove() function is ''not acceptable'' for clearing the reply port; other messages may arrive while the function is executing.
 
|}
 
   
 
=== High Speed Operation ===
 
=== High Speed Operation ===
Line 281: Line 294:
 
The device will determine if a quick I/O request will be handled quickly. Most non-I/O commands will execute quickly; read and write commands may or may not finish quickly.
 
The device will determine if a quick I/O request will be handled quickly. Most non-I/O commands will execute quickly; read and write commands may or may not finish quickly.
   
  +
<syntaxhighlight>
<pre>
 
 
SerialIO.IOSer.io_Flags |= IOF_QUICK; /* Set QuickIO Flag */
 
SerialIO.IOSer.io_Flags |= IOF_QUICK; /* Set QuickIO Flag */
   
BeginIO((struct IORequest *)SerialIO);
+
IExec->BeginIO((struct IORequest *)SerialIO);
if (SerialIO-&gt;IOSer.io_Flags &amp; IOF_QUICK )
+
if (SerialIO->IOSer.io_Flags & IOF_QUICK )
 
/* If flag is still set, I/O was synchronous and is now finished.
 
/* If flag is still set, I/O was synchronous and is now finished.
 
* The IORequest was NOT appended a reply port. There is no
 
* The IORequest was NOT appended a reply port. There is no
 
* need to remove or WaitIO() for the message.
 
* need to remove or WaitIO() for the message.
 
*/
 
*/
printf(&quot;QuickIO\n&quot;);
+
IDOS->Printf("QuickIO\n");
 
else
 
else
 
/* The device cleared the QuickIO bit. QuickIO could not happen
 
/* The device cleared the QuickIO bit. QuickIO could not happen
Line 296: Line 309:
 
* In this case BeginIO() acted exactly like SendIO().
 
* In this case BeginIO() acted exactly like SendIO().
 
*/
 
*/
printf(&quot;Regular I/O\n&quot;);
+
IDOS->Printf("Regular I/O\n");
WaitIO(SerialIO);
+
IExec->WaitIO(SerialIO);
  +
</syntaxhighlight>
</pre>
 
   
 
The way you read from the device depends on your need for processing speed. Generally the BeginIO() route provides the lowest system overhead when quick I/O is possible. However, if quick I/O does not work, the same reply message overhead still exists.
 
The way you read from the device depends on your need for processing speed. Generally the BeginIO() route provides the lowest system overhead when quick I/O is possible. However, if quick I/O does not work, the same reply message overhead still exists.
Line 306: Line 319:
 
Reads and writes from the serial device may terminate early if an error occurs or if an end-of-file (EOF) is sensed. For example, if a break is detected on the line, any current read request will be returned with the error SerErr_DetectedBreak. The count of characters read to that point will be in the io_Actual field of the request.
 
Reads and writes from the serial device may terminate early if an error occurs or if an end-of-file (EOF) is sensed. For example, if a break is detected on the line, any current read request will be returned with the error SerErr_DetectedBreak. The count of characters read to that point will be in the io_Actual field of the request.
   
You can specify a set of possible end-of-file characters that the serial device is to look for in the input stream or output using the SDCDMD_SETPARAMS command. These are contained in an io_TermArray that you provide. io_TermArray is used only when the SERF_EOFMODE flag is selected (see the “Serial Flags” sectionbelow).
+
You can specify a set of possible end-of-file characters that the serial device is to look for in the input stream or output using the SDCDMD_SETPARAMS command. These are contained in an io_TermArray that you provide. io_TermArray is used only when the SERF_EOFMODE flag is selected (see the “Serial Flags” section below).
   
 
If EOF mode is selected, each input data character read into or written from the user’s data block is compared against those in io_TermArray. If a match is found, the IOExtSer is terminated as complete, and the count of characters transferred (including the termination character) is stored in io_Actual.
 
If EOF mode is selected, each input data character read into or written from the user’s data block is compared against those in io_TermArray. If a match is found, the IOExtSer is terminated as complete, and the count of characters transferred (including the termination character) is stored in io_Actual.
Line 312: Line 325:
 
To keep this search overhead as efficient as possible, the serial device requires that the array of characters be in descending order. The array has eight bytes and all must be valid (that is, do not pad with zeros unless zero is a valid EOF character). Fill to the end of the array with the lowest value termination character. When making an arbitrary choice of EOF character(s), you will get the quickest response from the lowest value(s) available.
 
To keep this search overhead as efficient as possible, the serial device requires that the array of characters be in descending order. The array has eight bytes and all must be valid (that is, do not pad with zeros unless zero is a valid EOF character). Fill to the end of the array with the lowest value termination character. When making an arbitrary choice of EOF character(s), you will get the quickest response from the lowest value(s) available.
   
  +
<syntaxhighlight>
<pre>
 
 
/*
 
/*
 
* Terminate_Serial.c
 
* Terminate_Serial.c
Line 325: Line 338:
 
* The read will terminate whenever one of the four characters in the termination
 
* The read will terminate whenever one of the four characters in the termination
 
* array is received or when 25 characters have been received.
 
* array is received or when 25 characters have been received.
*
 
* Compile with SAS C 5.10 lc -b1 -cfistq -v -y -L
 
 
*
 
*
 
* Run from CLI only
 
* Run from CLI only
 
*/
 
*/
   
#include &lt;exec/types.h&gt;
+
#include <exec/types.h>
#include &lt;exec/memory.h&gt;
+
#include <exec/memory.h>
#include &lt;exec/io.h&gt;
+
#include <exec/io.h>
#include &lt;devices/serial.h&gt;
+
#include <devices/serial.h>
 
#include &lt;clib/exec_protos.h&gt;
 
#include &lt;clib/alib_protos.h&gt;
 
 
#include &lt;stdio.h&gt;
 
 
#ifdef LATTICE
 
int CXBRK(void) { return(0); } /* Disable SAS CTRL/C handling */
 
int chkabort(void) { return(0); } /* really */
 
#endif
 
 
   
  +
#include <proto/dos.h>
  +
#include <proto/exec.h>
   
void main(void)
+
int main()
 
{
 
{
struct MsgPort *SerialMP; /* Define storage for one pointer */
 
struct IOExtSer *SerialIO; /* Define storage for one pointer */
 
 
 
struct IOTArray Terminators =
 
struct IOTArray Terminators =
 
{
 
{
Line 360: Line 359:
   
 
#define READ_BUFFER_SIZE 25
 
#define READ_BUFFER_SIZE 25
UBYTE ReadBuff[READ_BUFFER_SIZE];
+
uint8 ReadBuff[READ_BUFFER_SIZE];
UWORD ctr;
+
uint16 ctr;
   
  +
struct MsgPort *SerialMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
if (SerialMP=CreatePort(0,0) )
 
  +
if (SerialMP != NULL)
 
{
 
{
if (SerialIO=(struct IOExtSer *) CreateExtIO(SerialMP,sizeof(struct IOExtSer)))
+
struct IOExtSer *SerialIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
  +
ASOIOR_ReplyPort, SerialMP,
  +
ASOIOR_Size, sizeof(struct IOExtSer),
  +
TAG_END);
  +
  +
if (SerialIO != NULL)
 
{
 
{
if (OpenDevice(SERIALNAME,0L,(struct IORequest *)SerialIO,0) )
+
if (IExec->OpenDevice(SERIALNAME, 0, (struct IORequest *)SerialIO, 0) )
printf(&quot;%s did not open\n&quot;,SERIALNAME);
+
IDOS->Printf("%s did not open\n",SERIALNAME);
 
else
 
else
 
{
 
{
 
/* Tell user what we are doing */
 
/* Tell user what we are doing */
printf(&quot;\fLooking for Q, E, EOT or ETX\n&quot;);
+
IDOS->Printf("\fLooking for Q, E, EOT or ETX\n");
   
 
/* Set EOF mode flag
 
/* Set EOF mode flag
Line 378: Line 383:
 
* Send SDCMD_SETPARAMS to the serial device
 
* Send SDCMD_SETPARAMS to the serial device
 
*/
 
*/
SerialIO-&gt;io_SerFlags |= SERF_EOFMODE;
+
SerialIO->io_SerFlags |= SERF_EOFMODE;
SerialIO-&gt;io_TermArray = Terminators;
+
SerialIO->io_TermArray = Terminators;
SerialIO-&gt;IOSer.io_Command = SDCMD_SETPARAMS;
+
SerialIO->IOSer.io_Command = SDCMD_SETPARAMS;
if (DoIO((struct IORequest *)SerialIO))
+
if (IExec->DoIO((struct IORequest *)SerialIO))
printf(&quot;Set Params failed &quot;); /* Inform user of error */
+
IDOS->Printf("Set Params failed "); /* Inform user of error */
 
else
 
else
 
{
 
{
SerialIO-&gt;IOSer.io_Length = READ_BUFFER_SIZE;
+
SerialIO->IOSer.io_Length = READ_BUFFER_SIZE;
SerialIO-&gt;IOSer.io_Data = (APTR)&amp;ReadBuff[0];
+
SerialIO->IOSer.io_Data = (APTR)&ReadBuff[0];
SerialIO-&gt;IOSer.io_Command = CMD_READ;
+
SerialIO->IOSer.io_Command = CMD_READ;
if (DoIO((struct IORequest *)SerialIO)) /* Execute Read */
+
if (IExec->DoIO((struct IORequest *)SerialIO)) /* Execute Read */
printf(&quot;Error: Read failed\n&quot;);
+
IDOS->Printf("Error: Read failed\n");
 
else
 
else
 
{
 
{
 
/* Display all characters received */
 
/* Display all characters received */
printf(&quot;\nThese characters were read:\n\t\t\tASCII\tHEX\n&quot;);
+
IDOS->Printf("\nThese characters were read:\n\t\t\tASCII\tHEX\n");
for (ctr=0;ctr&lt;SerialIO-&gt;IOSer.io_Actual;ctr++)
+
for (ctr = 0; ctr < SerialIO->IOSer.io_Actual; ctr++)
printf(&quot;\t\t\t %c\t%x\n&quot;,ReadBuff[ctr],ReadBuff[ctr]);
+
IDOS->Printf("\t\t\t %c\t%x\n", ReadBuff[ctr], ReadBuff[ctr]);
printf(&quot;\nThe actual number of characters read: %d\n&quot;,
+
IDOS->Printf("\nThe actual number of characters read: %d\n",
SerialIO-&gt;IOSer.io_Actual);
+
SerialIO->IOSer.io_Actual);
 
}
 
}
 
}
 
}
CloseDevice((struct IORequest *)SerialIO);
+
IExec->CloseDevice((struct IORequest *)SerialIO);
 
}
 
}
   
DeleteExtIO((struct IORequest *)SerialIO);
+
IExec->FreeSysObject(ASOT_IOREQUEST, SerialIO);
 
}
 
}
 
else
 
else
printf(&quot;Error: Could not create IORequest\n&quot;);
+
IDOS->Printf("Error: Could not create IORequest\n");
   
DeletePort(SerialMP);
+
IExec->FreeSysObject(ASOT_PORT, SerialMP);
 
}
 
}
 
else
 
else
printf(&quot;Error: Could not create message port\n&quot;);
+
IDOS->Printf("Error: Could not create message port\n");
  +
  +
return 0;
 
}
 
}
  +
</syntaxhighlight>
</pre>
 
   
 
The read will terminate before the io_Length number of characters is read if a ‘Q’, ‘E’, ETX, or EOT is detected in the serial input stream.
 
The read will terminate before the io_Length number of characters is read if a ‘Q’, ‘E’, ETX, or EOT is detected in the serial input stream.
Line 421: Line 428:
 
In some cases there are advantages to creating a separate IOExtSer for reading and writing. This allows simultaneous operation of both reading and writing. Some users of the device have separate tasks for read and write operations. The sample code below creates a separate reply port and request for writing to the serial device.
 
In some cases there are advantages to creating a separate IOExtSer for reading and writing. This allows simultaneous operation of both reading and writing. Some users of the device have separate tasks for read and write operations. The sample code below creates a separate reply port and request for writing to the serial device.
   
  +
<syntaxhighlight>
<pre>
 
struct IOExtSer *SerialWriteIO;
 
struct MsgPort *SerialWriteMP;
 
 
 
/*
 
/*
 
* If two tasks will use the same device at the same time, it is preferred
 
* If two tasks will use the same device at the same time, it is preferred
* use two OpenDevice() calls and SHARED mode. If exclusive access mode
+
* to use two OpenDevice() calls and SHARED mode. If exclusive access mode
 
* is required, then you will need to copy an existing IORequest.
 
* is required, then you will need to copy an existing IORequest.
 
*
 
*
Line 433: Line 437:
 
*/
 
*/
   
SerialWriteMP = CreatePort(0,0);
+
struct MsgPort *SerialWriteMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
SerialWriteIO = (struct IOExtSer *)
 
CreateExtIO( SerialWriteMP,sizeof(struct IOExtSer) );
 
   
  +
struct IOExtSer *SerialWriteIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
if (SerialWriteMP &amp;&amp; SerialWriteIO )
 
  +
ASOIOR_Duplicate, SerialIO, /* Copy over the entire old IO request */
  +
ASOIOR_ReplyPort, SerialWriteMP, /* Override the message port */
  +
TAG_END);
  +
  +
if (SerialWriteMP && SerialWriteIO )
 
{
 
{
  +
SerialWriteIO->IOSer.io_Command = CMD_WRITE;
  +
SerialWriteIO->IOSer.io_Length = -1;
  +
SerialWriteIO->IOSer.io_Data = (APTR)"A poet's food is love and fame";
  +
IExec->DoIO(SerialWriteIO);
  +
}
  +
</syntaxhighlight>
   
  +
{{Note|title=Where’s OpenDevice()?|text=This code assumes that the OpenDevice() function has already been called. The initialized read request block is copied onto the new write request block.}}
/* Copy over the entire old IO request, then stuff the
 
* new Message port pointer.
 
*/
 
   
  +
== Setting Serial Parameters ==
CopyMem( SerialIO, SerialWriteIO, sizeof(struct IOExtSer) );
 
SerialWriteIO-&gt;IOSer.io_Message.mn_ReplyPort = SerialWriteMP;
 
   
  +
When the serial device is opened, default values for baud rate and other parameters are automatically filled in from the serial settings in Preferences. The parameters may be changed by using the SDCMD_SETPARAMS command. The flags are defined in the include file devices/serial.h.
SerialWriteIO-&gt;IOSer.io_Command = CMD_WRITE;
 
SerialWriteIO-&gt;IOSer.io_Length = -1;
 
SerialWriteIO-&gt;IOSer.io_Data = (APTR)&quot;A poet's food is love and fame&quot;;
 
DoIO(SerialWriteIO);
 
}
 
</pre>
 
   
  +
'''Serial Device Parameters (IOExtSer)'''
{| class="wikitable"
 
| ''Where’s OpenDevice()?'' This code assumes that the OpenDevice() function has already been called. The initialized read request block is copied onto the new write request block.
 
|}
 
   
  +
; io_CtlChar
== Setting Serial Parameters ==
 
  +
: Control characters to use for xON, xOFF, INQ, ACK respectively. Positioned within an unsigned longword in the sequence from low address to high as listed. INQ and ACK handshaking is not currently supported.
   
  +
; io_RBufLen
  +
: Recommended size of the buffer that the serial device should allocate for incoming data. For some hardware the buffer size will not be adjustable. Changing the value may cause the device to allocate a new buffer, which might fail due to lack of memory. In this case the old buffer will continue to be used.
   
  +
; io_ExtFlags
  +
: An unsigned long that contains the flags SEXTF_MSPON and SEXTF_MARK. SEXTF_MSPON enables either mark or space parity. SEXTF_MARK selects mark parity (instead of space parity). Unused bits are reserved.
   
  +
; io_Baud
When the serial device is opened, default values for baud rate and other parameters are automatically filled in from the serial settings in Preferences. The parameters may be changed by using the SDCMD_SETPARAMS command. The flags are defined in the include file devices/serial.h.
 
  +
: The real baud rate you request. This is an unsigned long value in the range of 1 to 4,294,967,295. The device will reject your baud request if the hardware is unable to support it.
   
  +
; io_BrkTime
ll
 
  +
: If you issue a break command, this variable specifies how long, in microseconds, the break condition lasts. This value controls the break time for all future break commands until modified by another SDCMD_SETPARAMS.
   
  +
; io_TermArray
2c'''Serial Device Parameters (IOExtSer)'''<br />
 
  +
: A byte-array of eight termination characters, must be in descending order. If the EOFMODE bit is set in the serial flags, this array specifies eight possible choices of character to use as an end of file mark. See the section above titled “Ending a Read Using Termination Characters” and the SDCMD_SETPARAMS summary page in the Autodocs
'''IOExtSer'''<br />
 
'''Field Name''' &amp; '''Serial Device Parameter It Controls'''<br />
 
io_CtlChar &amp; 9.2cmControl characters to use for xON, xOFF, INQ, ACK respectively. Positioned within an unsigned longword in the sequence from low address to high as listed. INQ and ACK handshaking is not currently supported.<br />
 
io_RBufLen &amp; 9.2cmRecommended size of the buffer that the serial device should allocate for incoming data. For some hardware the buffer size will not be adjustable. Changing the value may cause the device to allocate a new buffer, which might fail due to lack of memory. In this case the old buffer will continue to be used.<br />
 
io_ExtFlags &amp; 9.2cmAn unsigned long that contains the flags SEXTF_MSPON and SEXTF_MARK. SEXTF_MSPON enables either mark or space parity. SEXTF_MARK selects mark parity (instead of space parity). Unused bits are reserved.<br />
 
io_Baud &amp; 9.2cmThe real baud rate you request. This is an unsigned long value in the range of 1 to 4,294,967,295. The device will reject your baud request if the hardware is unable to support it.<br />
 
io_BrkTime &amp; 9.2cmIf you issue a break command, this variable specifies how long, in microseconds, the break condition lasts. This value controls the break time for all future break commands until modified by another SDCMD_SETPARAMS.<br />
 
io_TermArray &amp; 9.2cmA byte-array of eight termination characters, must be in descending order. If the EOFMODE bit is set in the serial flags, this array specifies eight possible choices of character to use as an end of file mark. See the section above titled “Ending a Read Using Termination Characters” and the SDCMD_SETPARAMS summary page in the Autodocs<br />
 
io_ReadLen &amp; 9.2cmHow many bits per read character; typically a value of 7 or 8. Generally must be the same as io_WriteLen.<br />
 
   
  +
; io_ReadLen
  +
: How many bits per read character; typically a value of 7 or 8. Generally must be the same as io_WriteLen.
   
  +
; io_WriteLen
<table>
 
  +
: How many bits per write character; typically a value of 7 or 8. Generally must be the same as io_ReadLen.
<tbody>
 
  +
<tr class="odd">
 
  +
; io_StopBits
<td align="left">io_WriteLen</td>
 
  +
: How many stop bits are to be expected when reading a character and to be produced when writing a character; typically 1 or 2. The built-in driver does not allow values above 1 if io_WriteLen is larger than 7.
<td align="left">9.2cmHow many bits per write character; typically a value of 7 or 8. Generally must be the same as io_ReadLen.</td>
 
  +
</tr>
 
  +
; io_SerFlags
<tr class="even">
 
  +
: See the “Serial Flags” section below.
<td align="left">io_StopBits</td>
 
  +
<td align="left">9.2cmHow many stop bits are to be expected when reading a character and to be produced when writing a character; typically 1 or 2. The built-in driver does not allow values above 1 if io_WriteLen is larger than 7.</td>
 
  +
; io_Status
</tr>
 
  +
: Contains status information filled in by the SDCMD_QUERY command. Break status is cleared by the execution of SDCMD_QUERY.
<tr class="odd">
 
<td align="left">io_SerFlags</td>
 
<td align="left">9.2cmSee the “Serial Flags” section below.</td>
 
</tr>
 
<tr class="even">
 
<td align="left">io_Status</td>
 
<td align="left">9.2cmContains status information filled in by the SDCMD_QUERY command. Break status is cleared by the execution of SDCMD_QUERY.</td>
 
</tr>
 
</tbody>
 
</table>
 
   
 
You set the serial parameters by passing an IOExtSer to the device with SDCMD_SETPARAMS set in io_Command and with the flags and parameters set to the values you want.
 
You set the serial parameters by passing an IOExtSer to the device with SDCMD_SETPARAMS set in io_Command and with the flags and parameters set to the values you want.
   
  +
<syntaxhighlight>
<pre>SerialIO-&gt;io_SerFlags &amp;= ~SERF_PARTY_ON; /* set parity off */
 
SerialIO-&gt;io_SerFlags |= SERF_XDISABLED; /* set xON/xOFF disabled */
+
SerialIO->io_SerFlags &= ~SERF_PARTY_ON; /* set parity off */
SerialIO-&gt;io_Baud = 9600; /* set 9600 baud */
+
SerialIO->io_SerFlags |= SERF_XDISABLED; /* set xON/xOFF disabled */
SerialIO-&gt;IOSer.io_Command = SDCMD_SETPARAMS; /* Set params command */
+
SerialIO->io_Baud = 9600; /* set 9600 baud */
  +
SerialIO->IOSer.io_Command = SDCMD_SETPARAMS; /* Set params command */
if (DoIO((struct IORequest *)SerialIO))
 
  +
if (IExec->DoIO((struct IORequest *)SerialIO))
printf(&quot;Error setting parameters!\n&quot;);</pre>
 
  +
IDOS->Printf("Error setting parameters!\n");
  +
</syntaxhighlight>
  +
 
The above fragment modifies two bits in io_SerFlags and changes the baud rate. If the parameters you request are unacceptable or out of range, the SDCMD_SETPARAMS command will fail. You are responsible for checking the error code and informing the user.
 
The above fragment modifies two bits in io_SerFlags and changes the baud rate. If the parameters you request are unacceptable or out of range, the SDCMD_SETPARAMS command will fail. You are responsible for checking the error code and informing the user.
   
<sub>b</sub>oxProper Time for Parameter Changes.A parameter change should not be performed while an I/O request is actually being processed because it might invalidate the request handling already in progress. To avoid this, you should use SDCMD_SETPARAMS only when you have no serial I/O requests pending.
+
{{Note|title=Proper Time for Parameter Changes|text=A parameter change should not be performed while an I/O request is actually being processed because it might invalidate the request handling already in progress. To avoid this, you should use SDCMD_SETPARAMS only when you have no serial I/O requests pending.}}
   
 
=== Serial Flags (Bit Definitions For io_SerFlags) ===
 
=== Serial Flags (Bit Definitions For io_SerFlags) ===
Line 515: Line 513:
 
There are additional serial device parameters which are controlled by flags set in the io_SerFlags field of the IOExtSer structure. The default state of all of these flags is zero. SERF_SHARED and SERF_7WIRE must always be set before OpenDevice(). The flags are defined in the include file devices/serial.h.
 
There are additional serial device parameters which are controlled by flags set in the io_SerFlags field of the IOExtSer structure. The default state of all of these flags is zero. SERF_SHARED and SERF_7WIRE must always be set before OpenDevice(). The flags are defined in the include file devices/serial.h.
   
  +
'''Serial Flags (io_SerFlags)'''
ll
 
   
  +
; SERF_XDISABLED
2c'''Serial Flags (io_SerFlags)'''<br />
 
  +
: Disable the xON/xOFF feature. xON/xOFF ''must'' be disabled during XModem transfers.
'''Flag Name''' &amp; '''Effect on Device Operation'''<br />
 
SERF_XDISABLED &amp; 8.8cmDisable the xON/xOFF feature. xON/xOFF ''must'' be disabled during XModem transfers.<br />
 
SERF_EOFMODE &amp; 8.8cmSet this bit if you want the serial device to check input characters against io_TermArray and to terminate the read immediately if an end-of-file character has been encountered. ''Note'': this bit may be set and reset directly in the user’s IOExtSer without a call to SDCMD_SETPARAMS.<br />
 
   
  +
; SERF_EOFMODE
  +
: Set this bit if you want the serial device to check input characters against io_TermArray and to terminate the read immediately if an end-of-file character has been encountered. ''Note'': this bit may be set and reset directly in the user’s IOExtSer without a call to SDCMD_SETPARAMS.
   
  +
; SERF_SHARED
ll
 
  +
: Set this bit if you want to allow other tasks to simultaneously access the serial port. The default is exclusive-access. Any number of tasks may have shared access. Only one task may have exclusive access. If someone already has the port for exclusive access, your OpenDevice() call will fail. This flag must be set before OpenDevice().
   
  +
; SERF_RAD_BOOGIE
SERF_SHARED
 
  +
: If set, this bit activates high-speed mode. Certain peripheral devices (MIDI, for example) require high serial throughput. Setting this bit high causes the serial device to skip certain of its internal checking code to speed throughput. Use SERF_RAD_BOOGIE only when you have:
  +
:* Disabled parity checking
  +
:* Disabled xON/xOFF handling
  +
:* Use 8-bit character length
  +
:* Do not wish a test for a break signal
   
  +
: Note that the Amiga is a multitasking system and has immediate processing of software interrupts. If there are other tasks running, it is possible that the serial driver may be unable to keep up with high data transfer rates, even with this bit set.
&amp; 8.8cmSet this bit if you want to allow other tasks to simultaneously access the serial port. The default is exclusive-access. Any number of tasks may have shared access. Only one task may have exclusive access. If someone already has the port for exclusive access, your OpenDevice() call will fail. This flag must be set before OpenDevice().<br />
 
SERF_RAD_BOOGIE &amp; 8.8cmIf set, this bit activates high-speed mode. Certain peripheral devices (MIDI, for example) require high serial throughput. Setting this bit high causes the serial device to skip certain of its internal checking code to speed throughput. Use SERF_RAD_BOOGIE only when you have:<br />
 
   
  +
; SERF_QUEUEDBRK
  +
: If set, every break command that you transmit will be enqueued. This means that all commands will be executed on a FIFO (first in, first out) basis.
   
  +
; SERF_7WIRE
&amp; 8.8cm
 
  +
: If set at OpenDevice() time, the serial device will use seven-wire handshaking for RS-232-C communications. Default is three-wire (pins 2, 3, and 7).
   
  +
; SERF_PARTY_ODD
* Disabled parity checking
 
  +
: If set, selects odd parity. If clear, selects even parity.
* Disabled xON/xOFF handling
 
* Use 8-bit character length
 
* Do not wish a test for a break signal
 
 
<br />
 
 
 
&amp; 8.8cmNote that the Amiga is a multitasking system and has immediate processing of software interrupts. If there are other tasks running, it is possible that the serial driver may be unable to keep up with high data transfer rates, even with this bit set.<br />
 
SERF_QUEUEDBRK &amp; 8.8cmIf set, every break command that you transmit will be enqueued. This means that all commands will be executed on a FIFO (first in, first out) basis.<br />
 
SERF_7WIRE &amp; 8.8cmIf set at OpenDevice() time, the serial device will use seven-wire handshaking for RS-232-C communications. Default is three-wire (pins 2, 3, and 7).<br />
 
SERF_PARTY_ODD &amp; 8.8cmIf set, selects odd parity. If clear, selects even parity.<br />
 
SERF_PARTY_ON &amp; 8.8cmIf set, parity usage and checking is enabled. Also see the SERF_MSPON bit described under io_ExtFlags above.<br />
 
   
  +
; SERF_PARTY_ON
  +
: If set, parity usage and checking is enabled. Also see the SERF_MSPON bit described under io_ExtFlags above.
   
 
== Querying the Serial Device ==
 
== Querying the Serial Device ==
Line 552: Line 549:
 
You query the serial device by passing an IOExtSer to the device with SDCMD_QUERY set in io_Command. The serial device will respond with the status of the serial port lines and registers, and the number of unread characters in the read buffer.
 
You query the serial device by passing an IOExtSer to the device with SDCMD_QUERY set in io_Command. The serial device will respond with the status of the serial port lines and registers, and the number of unread characters in the read buffer.
   
  +
<syntaxhighlight>
<pre>
 
  +
SerialIO->IOSer.io_Command = SDCMD_QUERY; /* indicate query */
UWORD Serial_Status;
 
  +
IExec->SendIO((struct IORequest *)SerialIO);
ULONG Unread_Chars;
 
 
SerialIO-&gt;IOSer.io_Command = SDCMD_QUERY; /* indicate query */
 
SendIO((struct IORequest *)SerialIO);
 
   
Serial_Status = SerialIO-&gt;io_Status; /* store returned status */
+
uint16 Serial_Status = SerialIO->io_Status; /* store returned status */
Unread_Chars = SerialIO-&gt;IOSer.io_Actual; /* store unread count */
+
uint32 Unread_Chars = SerialIO->IOSer.io_Actual; /* store unread count */
  +
</syntaxhighlight>
</pre>
 
   
 
The 16 status bits of the serial device are returned in io_Status; the number of unread characters is returned in io_Actual.
 
The 16 status bits of the serial device are returned in io_Status; the number of unread characters is returned in io_Actual.
   
'''Serial Device Status Bits'''
 
 
{| class="wikitable"
 
{| class="wikitable"
  +
|+ Serial Device Status Bits
 
! Bit
 
! Bit
 
! Active
 
! Active
Line 603: Line 597:
 
== Sending the Break Command ==
 
== Sending the Break Command ==
   
  +
You send a break through the serial device by passing an IOExtSer to the device with SDCMD_BREAK set in io_Command. The break may be immediate or queued. The choice is determined by the state of flag SERF_QUEUEDBRK in io_SerFlags.
   
  +
<syntaxhighlight>
  +
SerialIO->IOSer.io_Command = SDCMD_BREAK; /* send break */
  +
IExec->SendIO((struct IORequest *)SerialIO);
  +
</syntaxhighlight>
   
You send a break through the serial device by passing an IOExtSer to the device with SDCMD_BREAK set in io_Command. The break may be immediate or queued. The choice is determined by the state of flag SERF_QUEUEDBRK in io_SerFlags.
 
 
<pre>SerialIO-&gt;IOSer.io_Command = SDCMD_BREAK; /* send break */
 
SendIO((struct IORequest *)SerialIO);</pre>
 
 
The duration of the break (in microseconds) can be set in io_BrkTime. The default is 250,000 microseconds (0.25 seconds).
 
The duration of the break (in microseconds) can be set in io_BrkTime. The default is 250,000 microseconds (0.25 seconds).
   
Line 615: Line 610:
 
The serial device returns error codes whenever an operation is attempted.
 
The serial device returns error codes whenever an operation is attempted.
   
  +
<syntaxhighlight>
<pre>
 
SerialIO-&gt;IOSer.io_Command = SDCMD_SETPARAMS; /* Set parameters */
+
SerialIO->IOSer.io_Command = SDCMD_SETPARAMS; /* Set parameters */
if (DoIO((struct IORequest *)SerialIO))
+
if (IExec->DoIO((struct IORequest *)SerialIO))
printf(&quot;Set Params failed. Error: %ld &quot;,SerialIO-&gt;IOSer.io_Error);
+
IDOS->Printf("Set Params failed. Error: %ld ", SerialIO->IOSer.io_Error);
  +
</syntaxhighlight>
</pre>
 
   
 
The error is returned in the io_Error field of the IOExtSer structure.
 
The error is returned in the io_Error field of the IOExtSer structure.
 
Serial Device Error Codes
 
   
 
{| class="wikitable"
 
{| class="wikitable"
  +
|+ Serial Device Error Codes
 
! Error
 
! Error
 
! Value
 
! Value
Line 655: Line 649:
 
== Multiple Serial Port Support ==
 
== Multiple Serial Port Support ==
   
Applications that use the serial port should provide the user with a means to select the name and unit number of the driver. The defaults will be “serial.device” and unit number 0. Typically unit 0 refers to the user-selected default. Unit 1 refers to the built-in serial port. Numbers above 1 are for extended units. The physically lowest connector on a board will always have the lowest unit number.
+
Applications that use the serial port should provide the user with a means to select the name and unit number of the driver. The defaults will be “serial.device” and unit number 0. Typically unit 0 refers to the built-in serial port. Numbers above 0 are for extended units. The physically lowest connector on a board will always have the lowest unit number.
   
 
Careful attention to error handling is required to survive in a multiple port environment. Differing serial hardware will have different capabilities. The device will refuse to open non-existent unit numbers (symbolic name mapping of unit numbers is not provided at the device level). The SDCMD_SETPARAMS command will fail if the underlying hardware cannot support your parameters. Some devices may use quick I/O for read or write requests, others will not. Watch out for partially completed read requests; io_Actual may not match your requested read length.
 
Careful attention to error handling is required to survive in a multiple port environment. Differing serial hardware will have different capabilities. The device will refuse to open non-existent unit numbers (symbolic name mapping of unit numbers is not provided at the device level). The SDCMD_SETPARAMS command will fail if the underlying hardware cannot support your parameters. Some devices may use quick I/O for read or write requests, others will not. Watch out for partially completed read requests; io_Actual may not match your requested read length.
   
If the Tool Types mechanism is used for selecting the device and unit, the defaults of “DEVICE=serial.device” and “UNIT=0” should be provided. The user should be able to permanently set the device and unit in a configuration file.
+
If the Tool Types mechanism is used for selecting the device and unit, the defaults of "DEVICE=serial.device" and "UNIT=0" should be provided. The user should be able to permanently set the device and unit in a configuration file.
   
== Taking Over the Hardware ==
 
 
For some applications use of the device driver interface is not possible. By following the established rules, applications may take over the serial interface at the hardware level. This extreme step is not, however, encouraged. Taking over means losing the ability to work with additional serial ports, and will limit future compatibility.
 
 
Access to the hardware registers is controlled by the misc.resource. See the “Resources” chapter, and exec/misc.i for details. The MR_SERIALBITS and MR_SERIALPORT units control the serial registers.
 
 
One additional complication exists. The current serial device will not release the misc.resource bits until after an expunge. This code provides a work around:
 
 
<pre>/*
 
* A safe way to expunge ONLY a certain device.
 
* This code attempts to flush ONLY the named device out of memory and
 
* nothing else. If it fails, no status is returned (the information
 
* would have no valid use after the Permit().
 
*/
 
#include &lt;exec/types.h&gt;
 
#include &lt;exec/execbase.h&gt;
 
 
void FlushDevice(char *);
 
 
extern struct ExecBase *SysBase;
 
 
void FlushDevice(name)
 
char *name;
 
{
 
struct Device *devpoint;
 
 
Forbid(); /* ugly */
 
if (devpoint = (struct Device *)FindName(&amp;SysBase-&gt;DeviceList,name) )
 
RemDevice(devpoint);
 
Permit();
 
}</pre>
 
 
== Advanced Example of Serial Device Usage ==
 
== Advanced Example of Serial Device Usage ==
   
  +
<syntaxhighlight>
<pre>/*
 
  +
/*
 
* Complex_Serial.c
 
* Complex_Serial.c
 
*
 
*
 
* Complex tricky example of serial.device usage
 
* Complex tricky example of serial.device usage
*
 
* Compile with SAS C 5.10 lc -b1 -cfistq -v -y -L
 
 
*
 
*
 
* Run from CLI only
 
* Run from CLI only
 
*/
 
*/
   
#include &lt;exec/types.h&gt;
+
#include <exec/types.h>
#include &lt;exec/memory.h&gt;
+
#include <exec/memory.h>
#include &lt;exec/io.h&gt;
+
#include <exec/io.h>
#include &lt;devices/serial.h&gt;
+
#include <devices/serial.h>
   
#include &lt;clib/exec_protos.h&gt;
+
#include <proto/dos.h>
#include &lt;clib/alib_protos.h&gt;
+
#include <proto/exec.h>
   
  +
int main()
#include &lt;stdio.h&gt;
 
 
#ifdef LATTICE
 
int CXBRK(void) { return(0); } /* Disable SAS CTRL/C handling */
 
int chkabort(void) { return(0); } /* really */
 
#endif
 
 
 
void main(void)
 
 
{
 
{
struct MsgPort *SerialMP; /* Define storage for one pointer */
 
struct IOExtSer *SerialIO; /* Define storage for one pointer */
 
 
 
#define READ_BUFFER_SIZE 32
 
#define READ_BUFFER_SIZE 32
 
char SerialReadBuffer[READ_BUFFER_SIZE]; /* Reserve SIZE bytes of storage */
 
char SerialReadBuffer[READ_BUFFER_SIZE]; /* Reserve SIZE bytes of storage */
   
  +
uint32 Temp;
struct IOExtSer *SerialWriteIO = 0;
 
  +
uint32 WaitMask;
struct MsgPort *SerialWriteMP = 0;
 
 
ULONG Temp;
 
ULONG WaitMask;
 
   
  +
struct MsgPort *SerialMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
if (SerialMP=CreatePort(0,0) )
 
  +
if (SerialMP != NULL)
 
{
 
{
if (SerialIO=(struct IOExtSer *)
+
struct IOExtSer *SerialIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
CreateExtIO(SerialMP,sizeof(struct IOExtSer)) )
+
ASOIOR_ReplyPort, SerialMP,
  +
ASOIOR_Size, sizeof(struct IOExtSer),
  +
TAG_END);
  +
  +
if (SerialIO != NULL)
 
{
 
{
SerialIO-&gt;io_SerFlags=0; /* Example of setting flags */
+
SerialIO->io_SerFlags = 0; /* Example of setting flags */
   
if (OpenDevice(SERIALNAME,0L,SerialIO,0) )
+
if (IExec->OpenDevice(SERIALNAME, 0, SerialIO, 0) )
printf(&quot;%s did not open\n&quot;,SERIALNAME);
+
IDOS->Printf("%s did not open\n", SERIALNAME);
 
else
 
else
 
{
 
{
SerialIO-&gt;IOSer.io_Command = SDCMD_SETPARAMS;
+
SerialIO->IOSer.io_Command = SDCMD_SETPARAMS;
SerialIO-&gt;io_SerFlags &amp;= ~SERF_PARTY_ON;
+
SerialIO->io_SerFlags &= ~SERF_PARTY_ON;
SerialIO-&gt;io_SerFlags |= SERF_XDISABLED;
+
SerialIO->io_SerFlags |= SERF_XDISABLED;
SerialIO-&gt;io_Baud = 9600;
+
SerialIO->io_Baud = 9600;
if (Temp=DoIO(SerialIO))
+
if (Temp = IExec->DoIO(SerialIO))
printf(&quot;Error setting parameters - code %ld!\n&quot;,Temp);
+
IDOS->Printf("Error setting parameters - code %ld!\n",Temp);
   
SerialIO-&gt;IOSer.io_Command = CMD_WRITE;
+
SerialIO->IOSer.io_Command = CMD_WRITE;
SerialIO-&gt;IOSer.io_Length = -1;
+
SerialIO->IOSer.io_Length = -1;
SerialIO-&gt;IOSer.io_Data = (APTR)&quot;Amiga.&quot;;
+
SerialIO->IOSer.io_Data = (APTR)"Amiga.";
SendIO(SerialIO);
+
IExec->SendIO(SerialIO);
printf(&quot;CheckIO %lx\n&quot;,CheckIO(SerialIO));
+
IDOS->Printf("CheckIO %lx\n",CheckIO(SerialIO));
printf(&quot;The device will process the request in the background\n&quot;);
+
IDOS->Printf("The device will process the request in the background\n");
printf(&quot;CheckIO %lx\n&quot;,CheckIO(SerialIO));
+
IDOS->Printf("CheckIO %lx\n",CheckIO(SerialIO));
WaitIO(SerialIO);
+
IExec->WaitIO(SerialIO);
   
SerialIO-&gt;IOSer.io_Command = CMD_WRITE;
+
SerialIO->IOSer.io_Command = CMD_WRITE;
SerialIO-&gt;IOSer.io_Length = -1;
+
SerialIO->IOSer.io_Length = -1;
SerialIO-&gt;IOSer.io_Data = (APTR)&quot;Save the whales! &quot;;
+
SerialIO->IOSer.io_Data = (APTR)"Save the whales! ";
DoIO(SerialIO); /* execute write */
+
IExec->DoIO(SerialIO); /* execute write */
   
   
SerialIO-&gt;IOSer.io_Command = CMD_WRITE;
+
SerialIO->IOSer.io_Command = CMD_WRITE;
SerialIO-&gt;IOSer.io_Length = -1;
+
SerialIO->IOSer.io_Length = -1;
SerialIO-&gt;IOSer.io_Data = (APTR)&quot;Life is but a dream.&quot;;
+
SerialIO->IOSer.io_Data = (APTR)"Life is but a dream.";
DoIO(SerialIO); /* execute write */
+
IExec->DoIO(SerialIO); /* execute write */
   
SerialIO-&gt;IOSer.io_Command = CMD_WRITE;
+
SerialIO->IOSer.io_Command = CMD_WRITE;
SerialIO-&gt;IOSer.io_Length = -1;
+
SerialIO->IOSer.io_Length = -1;
SerialIO-&gt;IOSer.io_Data = (APTR)&quot;Row, row, row your boat.&quot;;
+
SerialIO->IOSer.io_Data = (APTR)"Row, row, row your boat.";
SerialIO-&gt;IOSer.io_Flags = IOF_QUICK;
+
SerialIO->IOSer.io_Flags = IOF_QUICK;
BeginIO(SerialIO);
+
IExec->BeginIO(SerialIO);
   
if (SerialIO-&gt;IOSer.io_Flags &amp; IOF_QUICK )
+
if (SerialIO->IOSer.io_Flags & IOF_QUICK )
 
{
 
{
   
Line 787: Line 740:
 
*/
 
*/
   
printf(&quot;Quick IO\n&quot;);
+
IDOS->Printf("Quick IO\n");
 
}
 
}
 
else
 
else
Line 797: Line 750:
 
*/
 
*/
   
printf(&quot;Regular IO\n&quot;);
+
IDOS->Printf("Regular IO\n");
 
}
 
}
   
WaitIO(SerialIO);
+
IExec->WaitIO(SerialIO);
   
   
SerialIO-&gt;IOSer.io_Command = CMD_UPDATE;
+
SerialIO->IOSer.io_Command = CMD_UPDATE;
SerialIO-&gt;IOSer.io_Length = -1;
+
SerialIO->IOSer.io_Length = -1;
SerialIO-&gt;IOSer.io_Data = (APTR)&quot;Row, row, row your boat.&quot;;
+
SerialIO->IOSer.io_Data = (APTR)"Row, row, row your boat.";
SerialIO-&gt;IOSer.io_Flags = IOF_QUICK;
+
SerialIO->IOSer.io_Flags = IOF_QUICK;
BeginIO(SerialIO);
+
IExec->BeginIO(SerialIO);
   
if (0 == SerialIO-&gt;IOSer.io_Flags &amp; IOF_QUICK )
+
if (0 == SerialIO->IOSer.io_Flags & IOF_QUICK )
 
{
 
{
   
Line 818: Line 771:
 
*/
 
*/
   
printf(&quot;Regular IO\n&quot;);
+
IDOS->Printf("Regular IO\n");
   
WaitIO(SerialIO);
+
IExec->WaitIO(SerialIO);
 
}
 
}
 
else
 
else
Line 830: Line 783:
 
*/
 
*/
   
printf(&quot;Quick IO\n&quot;);
+
IDOS->Printf("Quick IO\n");
 
}
 
}
   
Line 843: Line 796:
 
WaitMask = SIGBREAKF_CTRL_C|
 
WaitMask = SIGBREAKF_CTRL_C|
 
SIGBREAKF_CTRL_F|
 
SIGBREAKF_CTRL_F|
1L &lt;&lt; SerialMP-&gt;mp_SigBit;
+
1L << SerialMP->mp_SigBit;
   
SerialIO-&gt;IOSer.io_Command = CMD_READ;
+
SerialIO->IOSer.io_Command = CMD_READ;
SerialIO-&gt;IOSer.io_Length = READ_BUFFER_SIZE;
+
SerialIO->IOSer.io_Length = READ_BUFFER_SIZE;
SerialIO-&gt;IOSer.io_Data = (APTR)&amp;SerialReadBuffer[0];
+
SerialIO->IOSer.io_Data = (APTR)&SerialReadBuffer[0];
SendIO(SerialIO);
+
IExec->SendIO(SerialIO);
   
printf(&quot;Sleeping until CTRL-C, CTRL-F, or serial input\n&quot;);
+
IDOS->Printf("Sleeping until CTRL-C, CTRL-F, or serial input\n");
   
 
while (1)
 
while (1)
 
{
 
{
Temp = Wait(WaitMask);
+
Temp = IExec->Wait(WaitMask);
printf(&quot;Just woke up (YAWN!)\n&quot;);
+
IDOS->Printf("Just woke up (YAWN!)\n");
   
if (SIGBREAKF_CTRL_C &amp; Temp)
+
if (SIGBREAKF_CTRL_C & Temp)
 
break;
 
break;
   
if (CheckIO(SerialIO) ) /* If request is complete... */
+
if (IExec->CheckIO(SerialIO) ) /* If request is complete... */
 
{
 
{
WaitIO(SerialIO); /* clean up and remove reply */
+
IExec->WaitIO(SerialIO); /* clean up and remove reply */
   
printf(&quot;%ld bytes received\n&quot;,SerialIO-&gt;IOSer.io_Actual);
+
IDOS->Printf("%ld bytes received\n", SerialIO->IOSer.io_Actual);
 
break;
 
break;
 
}
 
}
 
}
 
}
   
AbortIO(SerialIO); /* Ask device to abort request, if pending */
+
IExec->AbortIO(SerialIO); /* Ask device to abort request, if pending */
WaitIO(SerialIO); /* Wait for abort, then clean up */
+
IExec->WaitIO(SerialIO); /* Wait for abort, then clean up */
   
   
Line 881: Line 834:
 
*/
 
*/
   
SerialWriteMP = CreatePort(0,0);
+
struct MsgPort *SerialWriteMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
SerialWriteIO = (struct IOExtSer *)
 
CreateExtIO( SerialWriteMP,sizeof(struct IOExtSer) );
 
   
if (SerialWriteMP &amp;&amp; SerialWriteIO )
+
struct IOExtSer *SerialWriteMP = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
{
+
ASOIOR_Duplicate, SerialIO,
  +
ASOIOR_ReplyPort, SerialWriteMP,
  +
TAG_END);
   
  +
if (SerialWriteMP && SerialWriteIO )
/* Copy over the entire old IO request, then stuff the
 
* new Message port pointer.
+
{
*/
+
SerialWriteIO->IOSer.io_Command = CMD_WRITE;
  +
SerialWriteIO->IOSer.io_Length = -1;
 
  +
SerialWriteIO->IOSer.io_Data = (APTR)"A poet's food is love and fame";
CopyMem( SerialIO, SerialWriteIO, sizeof(struct IOExtSer) );
 
SerialWriteIO-&gt;IOSer.io_Message.mn_ReplyPort = SerialWriteMP;
+
IExec->DoIO(SerialWriteIO);
 
SerialWriteIO-&gt;IOSer.io_Command = CMD_WRITE;
 
SerialWriteIO-&gt;IOSer.io_Length = -1;
 
SerialWriteIO-&gt;IOSer.io_Data = (APTR)&quot;A poet's food is love and fame&quot;;
 
DoIO(SerialWriteIO);
 
 
}
 
}
   
if (SerialWriteMP)
+
IExec->FreeSysObject(ASOT_PORT, SerialWriteMP);
  +
IExec->FreeSysObject(ASOT_IOREQUEST, SerialWriteIO);
DeletePort(SerialWriteMP);
 
   
if (SerialWriteIO)
+
IExec->CloseDevice(SerialIO);
DeleteExtIO(SerialWriteIO);
 
 
CloseDevice(SerialIO);
 
 
}
 
}
   
DeleteExtIO(SerialIO);
+
IExec->FreeSysObject(ASOT_IOREQUEST, SerialIO);
 
}
 
}
 
else
 
else
printf(&quot;Unable to create IORequest\n&quot;);
+
IDOS->Printf("Unable to create IORequest\n");
   
DeletePort(SerialMP);
+
IExec->FreeSysObject(ASOT_PORT, SerialMP);
 
}
 
}
 
else
 
else
printf(&quot;Unable to create message port\n&quot;);
+
IDOS->Printf("Unable to create message port\n");
  +
}</pre>
 
  +
return 0;
  +
}
  +
</syntaxhighlight>
  +
 
== Additional Information on the Serial Device ==
 
== Additional Information on the Serial Device ==
   
Additional programming information on the serial device can be found in the include files and the Autodocs for the serial device. Both are contained in the ''Amiga ROM Kernel Reference Manual: Includes and Autodocs''.
+
Additional programming information on the serial device can be found in the include files and the Autodocs for the serial device. Both are contained in the SDK.
   
  +
{| class="wikitable"
|ll|
 
  +
! Includes
  +
|-
  +
| devices/serial.h
  +
|}
   
  +
{| class="wikitable"
2c'''Serial Device Information'''<br />
 
  +
! AutoDocs
Includes &amp; devices/serial.h<br />
 
  +
|-
&amp; devices/serial.i<br />
 
AutoDocs &amp; serial.doc<br />
+
| serial.doc
  +
|}

Latest revision as of 03:46, 24 June 2020

Serial Device

The serial device provides a hardware-independent interface to the Amiga’s built-in RS-232C compatible serial port. Serial ports have a wide range of uses, including communication with modems, printers, MIDI devices, and other computers. The same device interface can be used for additional “byte stream oriented devices”—usually more serial ports. The serial device is based on the conventions of Exec device I/O, with extensions for parameter setting and control.

Serial Device Characteristics
Modes Exclusive, Shared Access
Baud Rates 110–292,000
Handshaking Three-Wire, Seven-Wire

Serial Device Commands and Functions

Command Command Operation
CMD_CLEAR Reset the serial port’s read buffer pointers.
CMD_FLUSH Purge all queued requests for the serial device (does not affect active requests).
CMD_READ Read a stream of characters from the serial port buffer. The number of characters can be specified or a termination character(s) used.
CMD_RESET Reset the serial port to its initialized state. All active and queued I/O requests will be aborted and the current buffer will be released.
CMD_START Restart all paused I/O over the serial port. Also sends an “xON”.
CMD_STOP Pause all active I/O over the serial port. Also sends an “xOFF”.
CMD_WRITE Write out a stream of characters to the serial port. The number of characters can be specified or a NULL-terminated string can be sent.
SDCMD_BREAK Send a break signal out the serial port. May be done immediately or queued. Duration of the break (in microseconds) can be set by the application.
SDCMD_QUERY Return the status of the serial port lines and registers, and the number of bytes in the serial port’s read buffer.
SDCMD_SETPARAMS Set the parameters of the serial port. This ranges from baud rate to number of microseconds a break will last.

Device Interface

The serial device operates like the other Amiga devices. To use it, you must first open the serial device, then send I/O requests to it, and then close it when finished. See Exec Device I/O for general information on device usage.

The I/O request used by the serial device is called IOExtSer.

struct IOExtSer
{
    struct   IOStdReq IOSer;
    ULONG    io_CtlChar;    /* control characters */
    ULONG    io_RBufLen;    /* length in bytes of serial read buffer */
    ULONG    io_ExtFlags;   /* additional serial flags */
    ULONG    io_Baud;       /* baud rate */
    ULONG    io_BrkTime;    /* duration of break in microseconds */
    struct   iOTArray  io_TermArray;  /* termination character array */
    UBYTE    io_ReadLen;    /* number of bits per read character */
    UBYTE    io_WriteLen;   /* number of bits per write character */
    UBYTE    io_StopBits;   /* number of stopbits for read */
    UBYTE    io_SerFlags;   /* serial device flags */
    UWORD    io_Status;     /* status of serial port and lines */
};

See the include file devices/serial.h for the complete structure definition.

Opening the Serial Device

Three primary steps are required to open the serial device:

  • Create a message port using AllocSysObject() and the ASOT_PORT type. Reply messages from the device must be directed to a message port.
  • Create an extended I/O request structure of type IOExtSer using AllocSysObject() and the ASOT_IOREQUEST type. AllocSysObject() will initialize the I/O request to point to your reply port.
  • Open the serial device. Call OpenDevice(), passing the I/O request.
struct MsgPort *SerialMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
 
if (SerialMP != NULL)
{
    struct IOExtSer *SerialIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
        ASOIOR_ReplyPort, SerialMP,
        ASOIOR_Size, sizeof(struct IOExtSer),
        TAG_END);
 
    if (SerialIO != NULL)
    {
        SerialIO->io_SerFlags = SERF_SHARED;  /* Turn on SHARED mode */
        if (IExec->OpenDevice(SERIALNAME, 0, (struct IORequest *)SerialIO, 0) )
            IDOS->Printf("%s did not open\n", SERIALNAME);

During the open, the serial device pays attention to a subset of the flags in the io_SerFlags field. The flag bits, SERF_SHARED and SERF_7WIRE, must be set before open. For consistency, the other flag bits should also be properly set. Full descriptions of all flags will be given later.

The serial device automatically fills in default settings for all parameters—stop bits, parity, baud rate, etc. For the default unit, the settings will come from Preferences. You may need to change certain parameters, such as the baud rate, to match your requirements. Once the serial device is opened, all characters received will be buffered, even if there is no current request for them.

Reading From the Serial Device

You read from the serial device by passing an IOExtSer to the device with CMD_READ set in io_Command, the number of bytes to be read set in io_Length and the address of the read buffer set in io_Data.

#define READ_BUFFER_SIZE 256
char SerialReadBuffer[READ_BUFFER_SIZE]; /* Reserve SIZE bytes of storage */
 
SerialIO->IOSer.io_Length   = READ_BUFFER_SIZE;
SerialIO->IOSer.io_Data     = (APTR)&SerialReadBuffer[0];
SerialIO->IOSer.io_Command  = CMD_READ;
IExec->DoIO((struct IORequest *)SerialIO);

If you use this example, your task will be put to sleep waiting until the serial device reads 256 bytes (or terminates early). Early termination can be caused by error conditions such as a break. The number of characters actually received will be recorded in the io_Actual field of the IOExtSer structure you passed to the serial device.

Writing to the Serial Device

You write to the serial device by passing an IOExtSer to the device with CMD_WRITE set in io_Command, the number of bytes to be written set in io_Length and the address of the write buffer set in io_Data.

To write a NULL-terminated string, set the length to -1; the device will output from your buffer until it encounters and transmits a value of zero (0x00).

SerialIO->IOSer.io_Length   = -1;
SerialIO->IOSer.io_Data     = (APTR)"Life is but a dream.";
SerialIO->IOSer.io_Command  = CMD_WRITE;
IExec->DoIO((struct IORequest *)SerialIO);             /* execute write */

The length of the request is -1, meaning we are writing a NULL-terminated string. The number of characters sent can be found in io_Actual.

Closing the Serial Device

Each OpenDevice() must eventually be matched by a call to CloseDevice(). When the last close is performed, the device will deallocate all resources and buffers.

All IORequests must be complete before CloseDevice(). Abort any pending requests with AbortIO().

if (!(IExec->CheckIO(SerialIO)))
    {
    IExec->AbortIO((struct IORequest *)SerialIO);  /* Ask device to abort request, if pending */
    }
IExec->WaitIO((struct IORequest *)SerialIO);       /* Wait for abort, then clean up */
IExec->CloseDevice((struct IORequest *)SerialIO);

A Simple Serial Port Example

/*
 * Simple_Serial.c
 *
 * This is an example of using the serial device.
 *
 * Run from CLI only
 */
 
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <devices/serial.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
 
int main()
{
struct MsgPort *SerialMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
 
/* Create the message port */
if (SerialMP != NULL)
    {
    /* Create the IORequest */
    struct IOExtSer *SerialIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
        ASOIOR_ReplyPort, SerialMP,
        ASOIOR_Size, sizeof(struct IOExtSer),
        TAG_END);
 
    if (SerialIO != NULL)
        {
        /* Open the serial device */
        if (IExec->OpenDevice(SERIALNAME, 0, (struct IORequest *)SerialIO, 0))
 
            /* Inform user that it could not be opened */
            IDOS->Printf("Error: %s did not open\n",SERIALNAME);
        else
            {
            /* device opened, write NULL-terminated string */
            SerialIO->IOSer.io_Length   = -1;
            SerialIO->IOSer.io_Data     = (APTR)"Amiga ";
            SerialIO->IOSer.io_Command  = CMD_WRITE;
            if (IExec->DoIO((struct IORequest *)SerialIO))     /* execute write */
                IDOS->Printf("Write failed.  Error - %ld\n", SerialIO->IOSer.io_Error);
 
            /* Close the serial device */
            IExec->CloseDevice((struct IORequest *)SerialIO);
            }
        /* Delete the IORequest */
        IExec->FreeSysObject(ASOT_IOREQUEST, SerialIO);
        }
    else
        /* Inform user that the IORequest could be created */
        IDOS->Printf("Error: Could create IORequest\n");
 
    /* Delete the message port */
    IExec->FreeSysOject(ASOT_PORT, SerialMP);
    }
else
    /* Inform user that the message port could not be created */
    IDOS->Printf("Error: Could not create message port\n");
 
    return 0;
}
DoIO() vs. SendIO()
The above example code contains some simplifications. The DoIO() function in the example is not always appropriate for executing the CMD_READ or CMD_WRITE commands. DoIO() will not return until the I/O request has finished. With serial handshaking enabled, a write request may never finish. Read requests will not finish until characters arrive at the serial port. The following sections will demonstrate a solution using the SendIO() and AbortIO() functions.

Alternative Modes for Serial Input or Output

As an alternative to DoIO() you can use an asynchronous I/O request to transmit the command. Asynchronous requests are initiated with SendIO(). Your task can continue to execute while the device processes the command. You can occasionally do a CheckIO() to see if the I/O has completed. The write request in this example will be processed while the example continues to run:

SerialIO->IOSer.io_Length   = -1;
SerialIO->IOSer.io_Data     = (APTR)"Save the whales! ";
SerialIO->IOSer.io_Command  = CMD_WRITE;
IExec->SendIO((struct IORequest *)SerialIO);
 
IDOS->Printf("CheckIO %lx\n", IExec->CheckIO((struct IORequest *)SerialIO));
IDOS->Printf("The device will process the request in the background\n");
IDOS->Printf("CheckIO %lx\n", IExec->CheckIO((struct IORequest *)SerialIO));
IExec->WaitIO((struct IORequest *)SerialIO);   /* Remove message and cleanup */

Most applications will want to wait on multiple signals. A typical application will wait for menu messages from Intuition at the same time as replies from the serial device. The following fragment demonstrates waiting for one of three signals. The Wait() will wake up if the read request ever finishes, or if the user presses Ctrl + C or Ctrl + F from the Shell. This fragment may be inserted into the above complete example.

/* Precalculate a wait mask for the CTRL-C, CTRL-F and message
 * port signals.  When one or more signals are received,
 * Wait() will return.  Press CTRL-C to exit the example.
 * Press CTRL-F to wake up the example without doing anything.
 * NOTE: A signal may show up without an associated message!
*/
uint32 WaitMask;
uint32 Temp;
WaitMask =	SIGBREAKF_CTRL_C|
				SIGBREAKF_CTRL_F|
				1L << SerialMP->mp_SigBit;
 
SerialIO->IOSer.io_Command = CMD_READ;
SerialIO->IOSer.io_Length = READ_BUFFER_SIZE;
SerialIO->IOSer.io_Data = (APTR)&SerialReadBuffer[0];
IExec->SendIO((struct IORequest *)SerialIO);
 
IDOS->Printf("Sleeping until CTRL-C, CTRL-F, or serial input\n");
 
while (1)
{
	Temp = IExec->Wait(WaitMask);
	IDOS->Printf("Just woke up (YAWN!)\n");
 
	if (SIGBREAKF_CTRL_C & Temp)
		break;
 
	if (IExec->CheckIO((struct IORequest *)SerialIO) ) /* If request is complete... */
	{
		IExec->WaitIO((struct IORequest *)SerialIO);   /* clean up and remove reply */
		IDOS->Printf("%ld bytes received\n", SerialIO->IOSer.io_Actual);
		break;
	}
}
IExec->AbortIO((struct IORequest *)SerialIO);   /* Ask device to abort request, if pending */
IExec->WaitIO((struct IORequest *)SerialIO);   /* Wait for abort, then clean up */
WaitIO() vs. Remove()
The WaitIO() function is used above, even if the request is already known to be complete. WaitIO() on a completed request simply removes the reply and cleans up. The Remove() function is not acceptable for clearing the reply port; other messages may arrive while the function is executing.

High Speed Operation

The more characters that are processed in each I/O request, the higher the total throughput of the device. The following technique will minimize device overhead for reads:

  • Use the SDCMD_QUERY command to get the number of characters currently in the buffer (see the devices/serial.h Autodocs for information on SDCMD_QUERY).
  • Use DoIO() to read all available characters (or the maximum size of your buffer). In this case, DoIO() is guaranteed to return without waiting.
  • If zero characters are in the buffer, post an asynchronous request (SendIO()) for 1 character. When at least one is ready, the device will return it. Now go back to the first step.
  • If the user decides to quit the program, AbortIO() any pending requests.

Use of BeginIO() with the Serial Device

Instead of transmitting the read command with either DoIO() or SendIO(), you might elect to use the low level BeginIO() interface to a device.

BeginIO() works much like SendIO(), i.e., asynchronously, except it gives you control over the quick I/O bit (IOB_QUICK) in the io_Flags field. Quick I/O saves the overhead of a reply message, and perhaps the overhead of a task switch. If a quick I/O request is actually completed quickly, the entire command will execute in the context of the caller. See the Exec Device I/O for more detailed information on quick I/O.

The device will determine if a quick I/O request will be handled quickly. Most non-I/O commands will execute quickly; read and write commands may or may not finish quickly.

SerialIO.IOSer.io_Flags |= IOF_QUICK;  /* Set QuickIO Flag */
 
IExec->BeginIO((struct IORequest *)SerialIO);
if (SerialIO->IOSer.io_Flags & IOF_QUICK )
    /* If flag is still set, I/O was synchronous and is now finished.
     * The IORequest was NOT appended a reply port.  There is no
     * need to remove or WaitIO() for the message.
     */
    IDOS->Printf("QuickIO\n");
else
     /* The device cleared the QuickIO bit.  QuickIO could not happen
      * for some reason; the device processed the command normally.
      * In this case BeginIO() acted exactly like SendIO().
      */
     IDOS->Printf("Regular I/O\n");
IExec->WaitIO(SerialIO);

The way you read from the device depends on your need for processing speed. Generally the BeginIO() route provides the lowest system overhead when quick I/O is possible. However, if quick I/O does not work, the same reply message overhead still exists.

Ending a Read or Write using Termination Characters

Reads and writes from the serial device may terminate early if an error occurs or if an end-of-file (EOF) is sensed. For example, if a break is detected on the line, any current read request will be returned with the error SerErr_DetectedBreak. The count of characters read to that point will be in the io_Actual field of the request.

You can specify a set of possible end-of-file characters that the serial device is to look for in the input stream or output using the SDCDMD_SETPARAMS command. These are contained in an io_TermArray that you provide. io_TermArray is used only when the SERF_EOFMODE flag is selected (see the “Serial Flags” section below).

If EOF mode is selected, each input data character read into or written from the user’s data block is compared against those in io_TermArray. If a match is found, the IOExtSer is terminated as complete, and the count of characters transferred (including the termination character) is stored in io_Actual.

To keep this search overhead as efficient as possible, the serial device requires that the array of characters be in descending order. The array has eight bytes and all must be valid (that is, do not pad with zeros unless zero is a valid EOF character). Fill to the end of the array with the lowest value termination character. When making an arbitrary choice of EOF character(s), you will get the quickest response from the lowest value(s) available.

/*
 * Terminate_Serial.c
 *
 * This is an example of using a termination array for reads from the serial
 * device. A termination array is set up for the characters Q, E, etx (CTRL-D)
 * and eot (CTRL-C).  The EOFMODE flag is set in io_SerFlags to indicate that
 * we want to use a termination array by sending the SDCMD_SETPARAMS command to
 * the device.  Then, a CMD_READ command is sent to the device with
 * io_Length set to 25.
 *
 * The read will terminate whenever one of the four characters in the termination
 * array is received or when 25 characters have been received.
 *
 * Run from CLI only
 */
 
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <devices/serial.h>
 
#include <proto/dos.h>
#include <proto/exec.h>
 
int main()
{
struct IOTArray Terminators =
{
0x51450403,   /* Q E etx eot */
0x03030303    /* fill to end with lowest value */
};
 
#define READ_BUFFER_SIZE 25
uint8 ReadBuff[READ_BUFFER_SIZE];
uint16 ctr;
 
struct MsgPort *SerialMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
if (SerialMP != NULL)
    {
    struct IOExtSer *SerialIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
        ASOIOR_ReplyPort, SerialMP,
        ASOIOR_Size, sizeof(struct IOExtSer),
        TAG_END);
 
    if (SerialIO != NULL)
        {
        if (IExec->OpenDevice(SERIALNAME, 0, (struct IORequest *)SerialIO, 0) )
            IDOS->Printf("%s did not open\n",SERIALNAME);
        else
            {
             /* Tell user what we are doing */
             IDOS->Printf("\fLooking for Q, E, EOT or ETX\n");
 
             /* Set EOF mode flag
              * Set the termination array
              * Send SDCMD_SETPARAMS to the serial device
              */
             SerialIO->io_SerFlags |= SERF_EOFMODE;
             SerialIO->io_TermArray = Terminators;
             SerialIO->IOSer.io_Command  = SDCMD_SETPARAMS;
             if (IExec->DoIO((struct IORequest *)SerialIO))
                 IDOS->Printf("Set Params failed ");   /* Inform user of error */
             else
                 {
                 SerialIO->IOSer.io_Length   = READ_BUFFER_SIZE;
                 SerialIO->IOSer.io_Data     = (APTR)&ReadBuff[0];
                 SerialIO->IOSer.io_Command  = CMD_READ;
                 if (IExec->DoIO((struct IORequest *)SerialIO))     /* Execute Read */
                     IDOS->Printf("Error: Read failed\n");
                 else
                     {
                      /* Display all characters received */
                      IDOS->Printf("\nThese characters were read:\n\t\t\tASCII\tHEX\n");
                      for (ctr = 0; ctr < SerialIO->IOSer.io_Actual; ctr++)
                           IDOS->Printf("\t\t\t  %c\t%x\n", ReadBuff[ctr], ReadBuff[ctr]);
                      IDOS->Printf("\nThe actual number of characters read: %d\n",
                                  SerialIO->IOSer.io_Actual);
                      }
                 }
            IExec->CloseDevice((struct IORequest *)SerialIO);
            }
 
        IExec->FreeSysObject(ASOT_IOREQUEST, SerialIO);
        }
    else
        IDOS->Printf("Error: Could not create IORequest\n");
 
    IExec->FreeSysObject(ASOT_PORT, SerialMP);
    }
else
    IDOS->Printf("Error: Could not create message port\n");
 
    return 0;
}

The read will terminate before the io_Length number of characters is read if a ‘Q’, ‘E’, ETX, or EOT is detected in the serial input stream.

Using Separate Read and Write Tasks

In some cases there are advantages to creating a separate IOExtSer for reading and writing. This allows simultaneous operation of both reading and writing. Some users of the device have separate tasks for read and write operations. The sample code below creates a separate reply port and request for writing to the serial device.

/*
 * If two tasks will use the same device at the same time, it is preferred
 * to use two OpenDevice() calls and SHARED mode. If exclusive access mode
 * is required, then you will need to copy an existing IORequest.
 *
 * Remember that two separate tasks will require two message ports.
 */
 
struct MsgPort *SerialWriteMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
 
struct IOExtSer *SerialWriteIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
    ASOIOR_Duplicate, SerialIO,       /* Copy over the entire old IO request */
    ASOIOR_ReplyPort, SerialWriteMP,  /* Override the message port */
    TAG_END);
 
if (SerialWriteMP && SerialWriteIO )
    {
    SerialWriteIO->IOSer.io_Command  = CMD_WRITE;
    SerialWriteIO->IOSer.io_Length   = -1;
    SerialWriteIO->IOSer.io_Data     = (APTR)"A poet's food is love and fame";
    IExec->DoIO(SerialWriteIO);
    }
Where’s OpenDevice()?
This code assumes that the OpenDevice() function has already been called. The initialized read request block is copied onto the new write request block.

Setting Serial Parameters

When the serial device is opened, default values for baud rate and other parameters are automatically filled in from the serial settings in Preferences. The parameters may be changed by using the SDCMD_SETPARAMS command. The flags are defined in the include file devices/serial.h.

Serial Device Parameters (IOExtSer)

io_CtlChar
Control characters to use for xON, xOFF, INQ, ACK respectively. Positioned within an unsigned longword in the sequence from low address to high as listed. INQ and ACK handshaking is not currently supported.
io_RBufLen
Recommended size of the buffer that the serial device should allocate for incoming data. For some hardware the buffer size will not be adjustable. Changing the value may cause the device to allocate a new buffer, which might fail due to lack of memory. In this case the old buffer will continue to be used.
io_ExtFlags
An unsigned long that contains the flags SEXTF_MSPON and SEXTF_MARK. SEXTF_MSPON enables either mark or space parity. SEXTF_MARK selects mark parity (instead of space parity). Unused bits are reserved.
io_Baud
The real baud rate you request. This is an unsigned long value in the range of 1 to 4,294,967,295. The device will reject your baud request if the hardware is unable to support it.
io_BrkTime
If you issue a break command, this variable specifies how long, in microseconds, the break condition lasts. This value controls the break time for all future break commands until modified by another SDCMD_SETPARAMS.
io_TermArray
A byte-array of eight termination characters, must be in descending order. If the EOFMODE bit is set in the serial flags, this array specifies eight possible choices of character to use as an end of file mark. See the section above titled “Ending a Read Using Termination Characters” and the SDCMD_SETPARAMS summary page in the Autodocs
io_ReadLen
How many bits per read character; typically a value of 7 or 8. Generally must be the same as io_WriteLen.
io_WriteLen
How many bits per write character; typically a value of 7 or 8. Generally must be the same as io_ReadLen.
io_StopBits
How many stop bits are to be expected when reading a character and to be produced when writing a character; typically 1 or 2. The built-in driver does not allow values above 1 if io_WriteLen is larger than 7.
io_SerFlags
See the “Serial Flags” section below.
io_Status
Contains status information filled in by the SDCMD_QUERY command. Break status is cleared by the execution of SDCMD_QUERY.

You set the serial parameters by passing an IOExtSer to the device with SDCMD_SETPARAMS set in io_Command and with the flags and parameters set to the values you want.

SerialIO->io_SerFlags      &= ~SERF_PARTY_ON;   /* set parity off */
SerialIO->io_SerFlags      |= SERF_XDISABLED;   /* set xON/xOFF disabled */
SerialIO->io_Baud           = 9600;             /* set 9600 baud */
SerialIO->IOSer.io_Command  = SDCMD_SETPARAMS;  /* Set params command */
if (IExec->DoIO((struct IORequest *)SerialIO))
    IDOS->Printf("Error setting parameters!\n");

The above fragment modifies two bits in io_SerFlags and changes the baud rate. If the parameters you request are unacceptable or out of range, the SDCMD_SETPARAMS command will fail. You are responsible for checking the error code and informing the user.

Proper Time for Parameter Changes
A parameter change should not be performed while an I/O request is actually being processed because it might invalidate the request handling already in progress. To avoid this, you should use SDCMD_SETPARAMS only when you have no serial I/O requests pending.

Serial Flags (Bit Definitions For io_SerFlags)

There are additional serial device parameters which are controlled by flags set in the io_SerFlags field of the IOExtSer structure. The default state of all of these flags is zero. SERF_SHARED and SERF_7WIRE must always be set before OpenDevice(). The flags are defined in the include file devices/serial.h.

Serial Flags (io_SerFlags)

SERF_XDISABLED
Disable the xON/xOFF feature. xON/xOFF must be disabled during XModem transfers.
SERF_EOFMODE
Set this bit if you want the serial device to check input characters against io_TermArray and to terminate the read immediately if an end-of-file character has been encountered. Note: this bit may be set and reset directly in the user’s IOExtSer without a call to SDCMD_SETPARAMS.
SERF_SHARED
Set this bit if you want to allow other tasks to simultaneously access the serial port. The default is exclusive-access. Any number of tasks may have shared access. Only one task may have exclusive access. If someone already has the port for exclusive access, your OpenDevice() call will fail. This flag must be set before OpenDevice().
SERF_RAD_BOOGIE
If set, this bit activates high-speed mode. Certain peripheral devices (MIDI, for example) require high serial throughput. Setting this bit high causes the serial device to skip certain of its internal checking code to speed throughput. Use SERF_RAD_BOOGIE only when you have:
  • Disabled parity checking
  • Disabled xON/xOFF handling
  • Use 8-bit character length
  • Do not wish a test for a break signal
Note that the Amiga is a multitasking system and has immediate processing of software interrupts. If there are other tasks running, it is possible that the serial driver may be unable to keep up with high data transfer rates, even with this bit set.
SERF_QUEUEDBRK
If set, every break command that you transmit will be enqueued. This means that all commands will be executed on a FIFO (first in, first out) basis.
SERF_7WIRE
If set at OpenDevice() time, the serial device will use seven-wire handshaking for RS-232-C communications. Default is three-wire (pins 2, 3, and 7).
SERF_PARTY_ODD
If set, selects odd parity. If clear, selects even parity.
SERF_PARTY_ON
If set, parity usage and checking is enabled. Also see the SERF_MSPON bit described under io_ExtFlags above.

Querying the Serial Device

You query the serial device by passing an IOExtSer to the device with SDCMD_QUERY set in io_Command. The serial device will respond with the status of the serial port lines and registers, and the number of unread characters in the read buffer.

SerialIO->IOSer.io_Command  = SDCMD_QUERY; /* indicate query */
IExec->SendIO((struct IORequest *)SerialIO);
 
uint16 Serial_Status = SerialIO->io_Status; /* store returned status */
uint32 Unread_Chars = SerialIO->IOSer.io_Actual; /* store unread count */

The 16 status bits of the serial device are returned in io_Status; the number of unread characters is returned in io_Actual.

Serial Device Status Bits
Bit Active Symbol Function
0 Reserved
1 Reserved
2 high (RI) Parallel Select on the A1000. On the A500 and A2000, Select is also connected to the serial port’s Ring Indicator. (Be cautious when making cables.)
3 low (DSR) Data set ready
4 low (CTS) Clear to send
5 low (CD) Carrier detect
6 low (RTS) Ready to send
7 low (DTR) Data terminal ready
8 high Read overrun
9 high Break sent
10 high Break received
11 high Transmit xOFFed
12 high Receive xOFFed
13-15 (reserved)

Sending the Break Command

You send a break through the serial device by passing an IOExtSer to the device with SDCMD_BREAK set in io_Command. The break may be immediate or queued. The choice is determined by the state of flag SERF_QUEUEDBRK in io_SerFlags.

SerialIO->IOSer.io_Command  = SDCMD_BREAK; /* send break */
IExec->SendIO((struct IORequest *)SerialIO);

The duration of the break (in microseconds) can be set in io_BrkTime. The default is 250,000 microseconds (0.25 seconds).

Error Codes from the Serial Device

The serial device returns error codes whenever an operation is attempted.

SerialIO->IOSer.io_Command  = SDCMD_SETPARAMS; /* Set parameters */
if (IExec->DoIO((struct IORequest *)SerialIO))
    IDOS->Printf("Set Params failed. Error: %ld ", SerialIO->IOSer.io_Error);

The error is returned in the io_Error field of the IOExtSer structure.

Serial Device Error Codes
Error Value Explanation
SerErr_DevBusy 1 Device in use
SerErr_BaudMismatch 2 Baud rate not supported by hardware
SerErr_BufErr 4 Failed to allocate new read buffer
SerErr_InvParam 5 Bad parameter
SerErr_LineErr 6 Hardware data overrun
SerErr_ParityErr 9 Parity error
SerErr_TimerErr 11 Timeout (if using 7-wire handshaking)
SerErr_BufOverflow 12 Read buffer overflowed
SerErr_NoDSR 13 No Data Set Ready
SerErr_DetectedBreak 15 Break detected
SerErr_UnitBusy 16 Selected unit already in use

Multiple Serial Port Support

Applications that use the serial port should provide the user with a means to select the name and unit number of the driver. The defaults will be “serial.device” and unit number 0. Typically unit 0 refers to the built-in serial port. Numbers above 0 are for extended units. The physically lowest connector on a board will always have the lowest unit number.

Careful attention to error handling is required to survive in a multiple port environment. Differing serial hardware will have different capabilities. The device will refuse to open non-existent unit numbers (symbolic name mapping of unit numbers is not provided at the device level). The SDCMD_SETPARAMS command will fail if the underlying hardware cannot support your parameters. Some devices may use quick I/O for read or write requests, others will not. Watch out for partially completed read requests; io_Actual may not match your requested read length.

If the Tool Types mechanism is used for selecting the device and unit, the defaults of "DEVICE=serial.device" and "UNIT=0" should be provided. The user should be able to permanently set the device and unit in a configuration file.

Advanced Example of Serial Device Usage

/*
 * Complex_Serial.c
 *
 * Complex tricky example of serial.device usage
 *
 * Run from CLI only
 */
 
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <devices/serial.h>
 
#include <proto/dos.h>
#include <proto/exec.h>
 
int main()
{
#define READ_BUFFER_SIZE 32
char SerialReadBuffer[READ_BUFFER_SIZE]; /* Reserve SIZE bytes of storage */
 
uint32 Temp;
uint32 WaitMask;
 
struct MsgPort *SerialMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
if (SerialMP != NULL)
    {
    struct IOExtSer *SerialIO = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
        ASOIOR_ReplyPort, SerialMP,
        ASOIOR_Size, sizeof(struct IOExtSer),
        TAG_END);
 
    if (SerialIO != NULL)
        {
        SerialIO->io_SerFlags = 0;    /* Example of setting flags */
 
        if (IExec->OpenDevice(SERIALNAME, 0, SerialIO, 0) )
            IDOS->Printf("%s did not open\n", SERIALNAME);
        else
            {
            SerialIO->IOSer.io_Command  = SDCMD_SETPARAMS;
            SerialIO->io_SerFlags      &= ~SERF_PARTY_ON;
            SerialIO->io_SerFlags      |= SERF_XDISABLED;
            SerialIO->io_Baud           = 9600;
            if (Temp = IExec->DoIO(SerialIO))
                IDOS->Printf("Error setting parameters - code %ld!\n",Temp);
 
            SerialIO->IOSer.io_Command  = CMD_WRITE;
            SerialIO->IOSer.io_Length   = -1;
            SerialIO->IOSer.io_Data     = (APTR)"Amiga.";
            IExec->SendIO(SerialIO);
            IDOS->Printf("CheckIO %lx\n",CheckIO(SerialIO));
            IDOS->Printf("The device will process the request in the background\n");
            IDOS->Printf("CheckIO %lx\n",CheckIO(SerialIO));
            IExec->WaitIO(SerialIO);
 
            SerialIO->IOSer.io_Command  = CMD_WRITE;
            SerialIO->IOSer.io_Length   = -1;
            SerialIO->IOSer.io_Data     = (APTR)"Save the whales! ";
            IExec->DoIO(SerialIO);             /* execute write */
 
 
            SerialIO->IOSer.io_Command  = CMD_WRITE;
            SerialIO->IOSer.io_Length   = -1;
            SerialIO->IOSer.io_Data     = (APTR)"Life is but a dream.";
            IExec->DoIO(SerialIO);             /* execute write */
 
            SerialIO->IOSer.io_Command  = CMD_WRITE;
            SerialIO->IOSer.io_Length   = -1;
            SerialIO->IOSer.io_Data     = (APTR)"Row, row, row your boat.";
            SerialIO->IOSer.io_Flags = IOF_QUICK;
            IExec->BeginIO(SerialIO);
 
            if (SerialIO->IOSer.io_Flags & IOF_QUICK )
                {
 
                /*
                 * Quick IO could not happen for some reason; the device processed
                 *  the command normally.  In this case BeginIO() acted exactly
                 * like SendIO().
                 */
 
                IDOS->Printf("Quick IO\n");
                }
            else
                {
 
                /* If flag is still set, IO was synchronous and is now finished.
                 * The IO request was NOT appended a reply port.  There is no
                 * need to remove or WaitIO() for the message.
                 */
 
                IDOS->Printf("Regular IO\n");
                }
 
            IExec->WaitIO(SerialIO);
 
 
            SerialIO->IOSer.io_Command  = CMD_UPDATE;
            SerialIO->IOSer.io_Length   = -1;
            SerialIO->IOSer.io_Data     = (APTR)"Row, row, row your boat.";
            SerialIO->IOSer.io_Flags = IOF_QUICK;
            IExec->BeginIO(SerialIO);
 
            if (0 == SerialIO->IOSer.io_Flags & IOF_QUICK )
                {
 
                /*
                 * Quick IO could not happen for some reason; the device processed
                 * the command normally.  In this case BeginIO() acted exactly
                 * like SendIO().
                 */
 
                IDOS->Printf("Regular IO\n");
 
                IExec->WaitIO(SerialIO);
                }
            else
                {
 
                /* If flag is still set, IO was synchronous and is now finished.
                 * The IO request was NOT appended a reply port.  There is no
                 * need to remove or WaitIO() for the message.
                 */
 
                IDOS->Printf("Quick IO\n");
                }
 
 
            /* Precalculate a wait mask for the CTRL-C, CTRL-F and message
             * port signals.  When one or more signals are received,
             * Wait() will return.  Press CTRL-C to exit the example.
             * Press CTRL-F to wake up the example without doing anything.
             * NOTE: A signal may show up without an associated message!
             */
 
            WaitMask = SIGBREAKF_CTRL_C|
                        SIGBREAKF_CTRL_F|
                         1L << SerialMP->mp_SigBit;
 
            SerialIO->IOSer.io_Command  = CMD_READ;
            SerialIO->IOSer.io_Length   = READ_BUFFER_SIZE;
            SerialIO->IOSer.io_Data     = (APTR)&SerialReadBuffer[0];
            IExec->SendIO(SerialIO);
 
            IDOS->Printf("Sleeping until CTRL-C, CTRL-F, or serial input\n");
 
            while (1)
                   {
                   Temp = IExec->Wait(WaitMask);
                   IDOS->Printf("Just woke up (YAWN!)\n");
 
                   if (SIGBREAKF_CTRL_C & Temp)
                       break;
 
                   if (IExec->CheckIO(SerialIO) ) /* If request is complete... */
                       {
                       IExec->WaitIO(SerialIO);   /* clean up and remove reply */
 
                       IDOS->Printf("%ld bytes received\n", SerialIO->IOSer.io_Actual);
                       break;
                       }
                   }
 
            IExec->AbortIO(SerialIO);  /* Ask device to abort request, if pending */
            IExec->WaitIO(SerialIO);   /* Wait for abort, then clean up */
 
 
            /*
             * If two tasks will use the same device at the same time, it is preferred
             * use two OpenDevice() calls and SHARED mode.  If exclusive access mode
             * is required, then you will need to copy an existing IO request.
             *
             * Remember that two separate tasks will require two message ports.
             */
 
            struct MsgPort  *SerialWriteMP = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
 
            struct IOExtSer *SerialWriteMP = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
                ASOIOR_Duplicate, SerialIO,
                ASOIOR_ReplyPort, SerialWriteMP,
                TAG_END);
 
            if (SerialWriteMP && SerialWriteIO )
                {
                SerialWriteIO->IOSer.io_Command  = CMD_WRITE;
                SerialWriteIO->IOSer.io_Length   = -1;
                SerialWriteIO->IOSer.io_Data     = (APTR)"A poet's food is love and fame";
                IExec->DoIO(SerialWriteIO);
                }
 
            IExec->FreeSysObject(ASOT_PORT, SerialWriteMP);
            IExec->FreeSysObject(ASOT_IOREQUEST, SerialWriteIO);
 
            IExec->CloseDevice(SerialIO);
            }
 
        IExec->FreeSysObject(ASOT_IOREQUEST, SerialIO);
        }
    else
        IDOS->Printf("Unable to create IORequest\n");
 
    IExec->FreeSysObject(ASOT_PORT, SerialMP);
    }
else
    IDOS->Printf("Unable to create message port\n");
 
return 0;
}

Additional Information on the Serial Device

Additional programming information on the serial device can be found in the include files and the Autodocs for the serial device. Both are contained in the SDK.

Includes
devices/serial.h
AutoDocs
serial.doc