home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of A1200
/
World_Of_A1200.iso
/
datafiles
/
text
/
c_manual
/
devices
/
paralleldevice
/
paralleldevice.doc
< prev
next >
Wrap
Text File
|
1995-02-27
|
48KB
|
1,448 lines
8 PARALLEL DEVICE
8.1 INTRODUCTION
All Amiga models have a parallel port to which you can connect
external devices like a printer, a video digitizer or a sound
sampler. The most common external device for the parallel port
is undoubtedly a printer, although some printers are connected
to the serial device.
The parallel port can send and receive eight bits
simultaneously. This can be compared with the serial port which
only can send/receive a stream of bits. The parallel port
is because of this much faster and is therefore often used for
video digitizers or sound samplers.
The parallel device helps you to work with the printer at a
very low level, but is still easy to handle. The parallel
device can as the serial device be locked for exclusive access
or you can allow other programs to use the device
simultaneously.
It is important to note that the parallel device should only be
used when you want to handle the port directly at a low level.
This can be useful when you want to collect data from a video
digitizer, or send untranslated printer codes. However, if you
simply want to use the printer you should use the printer
device instead. The printer device will automatically take care
of all printer handling, and is using the preference settings.
See next chapter (29) "Printer Device".
8.2 PARALLEL PORT
A parallel port sends a whole byte each time as explained above,
and is therefore very fast. Data that is sent to or received
from the parallel port does not need to be translated in any
way, it is immediately usable.
The Amiga's parallel (Centronics) port is a 25-pin D-female-
type connector. (On the old A1000s the parallel port have a
male connector.) Below is an almost complete list of the pin
assignment, together with a short description. (See
illustration "Centronics".)
Pin Name Direction Description
-----------------------------------------------------
1 STROBE Out Used to coordinate the events
2 Data 0 In/Out Bit 0
3 Data 1 In/Out Bit 1
4 Data 2 In/Out Bit 2
5 Data 3 In/Out Bit 3
6 Data 4 In/Out Bit 4
7 Data 5 In/Out Bit 5
8 Data 6 In/Out Bit 6
9 Data 7 In/Out Bit 7
10 ACK In Data acknowledge
11 BUSY In/Out General Input/Output pin
12 POUT In/Out General Input/Output pin
13 SEL In/Out General Input/Output pin
14 +5V - +5 Volt
15 NC In/Out No connection pin
16 RESET Out The system resets
17 GND - Signal ground
18 GND - Signal ground
19 GND - Signal ground
20 GND - Signal ground
21 GND - Signal ground
22 GND - Signal ground
23 GND - Signal ground
24 GND - Signal ground
25 GND - Signal ground
8.3 PARALLEL DEVICE
The parallel device is very similar to the serial device. It
can either be used in exclusive mode, or several programs may
use the port simultaneously, shared access. When you want that
the parallel device to do something you simply send an already
initialized request block (struct IOExtPar), and the device
will send a message to the reply port when the request has been
done. Exactly as all other devices.
8.3.1 THE PARALLEL REQUESTBLOCK
The request block you should use with the parallel device look
like this: (defined in header file "devices/parallel.h")
struct IOExtPar
{
struct IOStdReq IOPar;
ULONG io_PExtFlags;
UBYTE io_Status;
UBYTE io_ParFlags;
struct IOPArray io_PTermArray;
};
IOPar: This is the standard request block. The IOStdReq
structure is defined in header file "exec/io.h",
and is fully documented in chapter 17 "Devices".
io_PExtFlags: This is currently not used, but will maybe be
used in the future when more parallel flags are
needed.
io_Status: The status of the parallel port and parallel
device. There exist for the moment four
status flags:
IOPTF_RWDIR If this flag is set the device
is currently writing to the
parallel device. On the other
hand, if the flag is not set
the device is collecting data
at the parallel port.
IOPTF_PARSEL Printer selected.
IOPTF_PAPEROUT The printer ran out of paper.
Inform the user!
IOPTF_PARBUSY The parallel port is currently
busy.
Use the command "PDCMD_QUERY" before you look at
these fields to make sure everything is up to
date.
io_ParFlags: This field contains all special parallel flags.
There exist only three flags for the moment, and
one of these is still not usable. Here is the
complete list:
PARF_SHARED Set this flag if you want to
share the parallel port with
other programs. Note that this
flag should only be altered
before you have opened the
parallel device, and should
NOT be changed later on.
If you want to change status
you should close the parallel
device, alter the status and
then try to open the device
again.
PARF_RAD_BOOGIE This flag is currently not
used. It is supposed to be
set when you want to send/
receive data at a very high
speed.
PARF_EOFMODE This is actually the only
flag you may alter after you
have opened the device. If
the flag is set the parallel
device will immediately
stop the transmission of data
when it finds one of the
specified end-of-file
characters.
io_PTermArray: This field contains eight characters which will
be treated as the end-of-file characters if the
PARF_EOFMODE flag is set.
8.3.2 OPEN THE PARALLEL DEVICE
As with all devices you have to open a message port through
which the parallel device can communicate with you, and
allocate a request block (a IOExtPar structure), before you
may open the device itself.
1. Open a message port: (Since it is only our task and the
device that will use the message port, we do not need
to make it "public", hence no name. Priority should as
usual be set to 0, normal priority.)
struct MsgPort *replymp;
replymp = (struct MsgPort *)
CreatePort( NULL, 0 );
if( !replymp )
clean_up( "Could not create the reply port!" );
2. Allocate a request block of type IOExtPar structure.
(The IOExtPar structure is an extended version of the
normal request block, and should therefore be allocated
with help of the CreateExtIO() function with the size
set to sizeof( struct IOExtPar ).
struct IOExtPar *parallel_req;
parallel_req = (struct IOExtPar *)
CreateExtIO( replymp, sizeof( struct IOExtPar ) );
if( !parallel_req )
clean_up( "Not enough memory!" );
Once the message port and the request block have successfully
been created, you need to decide if you want exclusive or
shared access. If you want shared access, other programs may
also use the parallel port, should you set the flag
"PARF_SHARED" in the io_ParFlags" field. If you want exclusive
access you do not need to set any flags, since it is the
default mode.
3. Either set shared or exclusive access, and open the
device.
UBYTE error;
/* We want shared access: */
parallel_req->io_ParFlags = PARF_SHARED;
/* Open the parallel device: */
error = OpenDevice( PARALLELNAME, 0, parallel_req, 0 );
if( error )
clean_up( "Could not open the Parallel Device!" );
8.3.3 SET PARALLEL PARAMETERS
Once you have successfully opened the parallel device you
may set some parameters that tells the device how it should
work. Luckily, the parallel device is much simpler than the
serial device.
For the moment there exist only one parallel flag you may use,
and that is PARF_EOFMODE. It should be set if you want the
device to immediately stop the current communication if one of
the eight specified end-of-file characters appears. To set the
flag do like this:
/* Look for end-of-file characters: */
parallel_req->io_ParFlags += PARF_EOFMODE;
Note that we used the sign "+=". This is very useful if you
want to set one flag (bit), but keep all others unchanged. If
we simply used the sign "=" the "PARF_SHARED" flag we
previously had set would be erased. We could of course have
written it like this:
/* Set both the end-of-file and shared mode: */
parallel_req->io_ParFlags = PARF_EOFMODE | PARF_SHARED;
This is however not equally good, since it is easy to forget
one flag, and then it would be erased. So if you want to
add flags use the command "+=". To erase one flag you only
have to do the opposite and subtract it ("-=").
If you want to use the end-of-file mode, and have set the
"PARF_EOFMODE" flag, you must also tell the device which
characters should be treated as end-of-file characters. The
list of these characters is stored in the "IOPArra" array
which is placed at the bottom of the request structure, and
looks like this:
struct IOPArray
{
ULONG PTermArray0;
ULONG PTermArray1;
};
Each ULONG data consists of four bytes, and in total you can
store eight bytes (end-of-file characters) in the array. To
make the checking routine efficient you must store the
characters in descending order! To copy the desired
characters into the array do like this:
/* Here is an array with all EOF characters: */
/* NOTE! They MUST be in descending order! */
UBYTE eof_char[8]={ 0x54, 0x32, 0x16, 0x15, 0x12, 0x03, 0x00, 0x00 };
/* Declare a unsigned byte pointer: */
UBYTE *ptr;
/* Simple loop variable: */
int loop;
...
/* Get the address of the IOTArray: */
ptr = (UBYTE *) &(ioreq->io_PTermArray);
/* Set all eight end of file characters: */
for( loop=0; loop < 8; loop++ )
{
/* Copy character after character: */
*ptr = eof_chars[ loop ];
/* Step one byte forward: */
ptr++;
}
Once you have set all desired parameters in the request block
(IOExtPar structure) you set the IO command to PDCMD_SETPARAMS
and tell the parallel device to do your request by calling the
DoIO() function. If something went wrong DoIO() will return
with an error number, else 0 is returned which means that your
request have successfully been executed. (See below for a
complete list of error messages.) Here is an example:
/* We want to set the parallel device's parameters: */
ioreq->IOPar.io_Command = PDCMD_SETPARAMS;
/* Do our request, and return when done: */
error = DoIO( ioreq );
8.3.4 WRITE DATA
To send data to the parallel device you have to:
1. Set the command flag "CMD_WRITE" in the "io_Command"
field.
2. Tell the parallel device how many bytes you want to send
by setting the "io_Length" field as desired. If you set it
to -1 the device will continuously send data and stop
first when an end-of-file character is found. (The end-of-
file character will also be sent.) Note that the parallel
flag "PARF_EOFMODE" must have been set, or the device will
never stop sending data.
3. Give the field "io_Data" the address of the first byte of
data that should be sent.
4. Finally you send the request to the parallel device. If
you want to wait for the request to be completed
(synchronous) you should use the DoIO() function. On the
other hand, if you want that your program continues to
work while your request is completed, you should use the
SendIO() function.
Here is an example on how to write data to the parallel port:
(In this example the program will be put to sleep while the data
is sent to the parallel port.)
error: (UBYTE) is a simple unsigned byte variable.
ioreq: (struct IOExtPar *) is a pointer to an IOExtPar
structure.
data: (BYTE) is a pointer to a block of memory where all
data you want to send is located.
/* We want to send (write) some data: */
ioreq->IOPar.io_Command = CMD_WRITE;
/* Give the start address of our data buffer: */
ioreq->IOPar.io_Data = data;
/* We want to send 150 bytes/characters: */
ioreq->IOPar.io_Length = 150;
/* Do our request: */
error = DoIO( ioreq );
/* Everything OK? */
if( error )
printf( "Problems while writing!\n" );
If you do not want to wait for the request to be completed you
should use the SendIO() function instead. To check later if the
request have been completed or not you can either use the
function CheckIO(), or wait for a message to arrive at the
request block's reply port. (Since other request may also send
messages to this port, it is usually easiest to use CheckIO().)
Once the request have been completed you can look at the
io_Error field of the request block to check if everything
was OK. If the field is zero everything was executed without
any problems, but if it is non zero the request failed.
Here is an example:
/* Declare a pointer and set it to NULL: */
struct IOExtPar *ptr = NULL;
...
/* We want to send (write) some data: */
ioreq->IOPar.io_Command = CMD_WRITE;
/* Give the start address of our data buffer: */
ioreq->IOPar.io_Data = data;
/* We want to send 280 bytes/characters: */
ioreq->IOPar.io_Length = 280;
/* Do our request and return immediately: */
SendIO( ioreq );
/* As long as the pointer is not pointing to */
/* the request we should stay in the loop: */
while( ptr == NULL )
{
... do something ...
/* Check if the request has been completed: (If the */
/* request has been completed CheckIO() will return */
/* a pointer to the request, else NULL is returned.) */
ptr = CheckIO( ioreq );
}
/* Remove the requst block's message. (The ptr and ioreq */
/* are in this example identical, so it does not matter */
/* whichever you will use. The parenthesis around the */
/* expression is actually unnecessary, but this looks */
/* better.) */
Remove( &(ptr->IOPar.io_Message.mn_Node) );
/* Everything OK? Check the io_Error filed: */
if( ioreq->IOPar.io_Error )
printf( "Problems while writing!\n" );
8.3.5 READ DATA
Although most people use the parallel port to send data to their
printer, more and more users connect other external devices
like video digitzers and sound samplers. With these devices
you do not only need to send data, you also have to collect
data from the parallel port.
The process of collecting (reading) is very similar to reading.
Here is what you have to do:
1. Set the command flag "CMD_READ" in the "io_Command"
field.
2. Tell the parallel device how many bytes you want to read
by setting the "io_Length" field as desired. If you set it
to -1 the device will continuously read data and stop
first when an end-of-file character is received. (The end-
of-file character will also be collected.) Note that the
parallel flag "PARF_EOFMODE" must have been set, or the
device will never stop collecting data.
3. Give the field "io_Data" a pointer to your data buffer
where all collected data should be placed. Note that the
buffer must be big enough so all data will fit!
4. Finally you send the request to the parallel device. If
you want to wait for the request to be completed
(synchronous) you should as usual use the DoIO() function.
On the other hand, if you want that your program
continues to work while your request is completed, you
should use the SendIO() function.
Here is an example on how to read data: (Your program will be
put to sleep while the data is collected.)
error: (UBYTE) is a simple unsigned byte variable.
ioreq: (struct IOExtPar *) is a pointer to an IOExtPar
structure.
data: (BYTE) is a pointer to a block of memory where all
data which is collected will be stored. Note that the
data buffer must be big enough so all data will fit.
/* We want to read some data: */
ioreq->IOPar.io_Command = CMD_READ;
/* Give the start address of our data buffer: */
ioreq->IOPar.io_Data = data;
/* We want to read 400 bytes/characters: (The buffer must */
/* then be at least 400 bytes.) */
ioreq->IOPar.io_Length = 400;
/* Do our request: */
error = DoIO( ioreq );
/* Everything OK? */
if( error )
printf( "Problems while reading!\n" );
The program above will go to sleep while the data is collected
from the parallel port. If you want to do something while the
data is fetched and not go to sleep you should use the
asynchronous function SendIO() function as explained above.
Here is an example:
/* Declare a pointer and set it to NULL: */
struct IOExtPar *ptr = NULL;
...
/* We want to read some data: */
ioreq->IOPar.io_Command = CMD_READ;
/* Give the start address of our data buffer: */
ioreq->IOPar.io_Data = data;
/* We want to read 400 bytes/characters: (The buffer must */
/* then be at least 400 bytes.) */
ioreq->IOPar.io_Length = 400;
/* Do our request and return immediately: */
SendIO( ioreq );
/* As long as the pointer is not pointing to */
/* the request we should stay in the loop: */
while( ptr == NULL )
{
... do something ...
/* Check if the request has been completed: (If the */
/* request has been completed CheckIO() will return */
/* a pointer to the request, else NULL is returned.) */
ptr = CheckIO( ioreq );
}
/* Remove the requst block's message. (The ptr and ioreq */
/* are in this example identical, so it does not matter */
/* whichever you will use. The parenthesis around the */
/* expression is actually unnecessary, but this looks */
/* better.) */
Remove( &(ptr->IOPar.io_Message.mn_Node) );
/* Everything OK? Check the io_Error filed: */
if( ioreq->IOPar.io_Error )
printf( "Problems while reading!\n" );
8.3.6 HOW TO HANDLE SEVERAL REQUESTS SIMULTANIOUSLY
Since you usually will want to read and write data at the same
time, you have to use two separate request blocks. If you are
using shared access mode you simply create two request blocks
and open the parallel device twice. (Do not forget to close
both requests later on.) Here is an example:
struct MsgPort *replymp;
struct IOExtPar *parallel_req_read;
struct IOExtPar *parallel_req_write;
UBYTE error;
/* We use only one reply message port: */
replymp = (struct MsgPort *)
CreatePort( NULL, 0 );
if( !replymp )
clean_up( "Could not create the reply port!" );
/* Create the request block "read": */
parallel_req_read = (struct IOExtPar *)
CreateExtIO( replymp, sizeof( struct IOExtPar ) );
if( !parallel_req_read )
clean_up( "Not enough memory!" );
parallel_req_read->io_ParFlags = PARF_EOFMODE | PARF_SHARED;
/* Create the request block "write": */
parallel_req_write = (struct IOExtPar *)
CreateExtIO( replymp, sizeof( struct IOExtPar ) );
if( !parallel_req_write )
clean_up( "Not enough memory!" );
parallel_req_write->io_ParFlags = PARF_EOFMODE | PARF_SHARED;
/* Open the parallel device for the read request: */
error = OpenDevice( PARALLELNAME, 0, parallel_req_read, 0 );
if( error )
clean_up( "Could not open the parallel device (Read)!" );
/* Open the parallel device for the write request: */
error = OpenDevice( PARALLELNAME, 0, parallel_req_write, 0 );
if( error )
clean_up( "Could not open the parallel device (Write)!" );
If you are using exclusive access mode you can of course not
open the parallel device twice. Instead we have to copy the
first request block to the other request block, byte for byte.
Here is an example:
struct MsgPort *replymp;
struct IOExtPar *parallel_req_read;
struct IOExtPar *parallel_req_write;
BYTE *r_ptr;
BYTE *w_ptr;
UBYTE error;
int loop;
/* We use only one reply message port: */
replymp = (struct MsgPort *)
CreatePort( NULL, 0 );
if( !replymp )
clean_up( "Could not create the reply port!" );
/* Create the request block "read": */
parallel_req_read = (struct IOExtPar *)
CreateExtIO( replymp, sizeof( struct IOExtPar ) );
if( !parallel_req_read )
clean_up( "Not enough memory!" );
parallel_req_read->io_ParFlags = PARF_EOFMODE | PARF_SHARED;
/* Create the request block "write": */
parallel_req_write = (struct IOExtPar *)
CreateExtIO( replymp, sizeof( struct IOExtPar ) );
if( !parallel_req_write )
clean_up( "Not enough memory!" );
parallel_req_write->io_ParFlags = PARF_EOFMODE | PARF_SHARED;
/* Open the parallel device for the read request: */
error = OpenDevice( PARALLELNAME, 0, parallel_req_read, 0 );
if( error )
clean_up( "Could not open the Parallel Device (Read)!" );
/* Since we can not open the parallel device once again */
/* for the write request, we have to copy the whole */
/* read request block into the write request block. */
/* Get the start address of both request blocks: */
r_ptr = (BYTE *) parallel_req_read;
w_ptr = (BYTE *) parallel_req_write;
/* Copy the request block, byte by byte: */
for( loop=0; loop < sizeof( struct IOExtPar ); loop++ )
{
/* Copy one byte: */
*w_ptr = *r_ptr;
/* Step one byte forward: */
w_ptr++;
r_ptr++;
}
Now we have two request blocks, and can therefore use both
read and write requests at the same time. If you also want to
use the special extra functions described further down, you
may want to create even one more request block (or maybe
even more...). The procedure is the same as described above.
8.3.7 ERRORS
While you are using the parallel device you may sometimes
encounter an error message. Usually it is easy to guess what
went wrong, but it is always good to check what really happened.
There exit for the moment seven different types of parallel
port errors, all defined in the header file "devices/
parallel.h".
You will either receive the error message from the function you
just called (for example, DoIO() returns 0 or an error number),
or you can check the request block to see if there were any
problems. (The io_Error filed of the request block either
contains 0, which means everything is OK, or an error number.)
Here is a complete list of the parallel error messages:
ParErr_DevBusy Some other task/request is already using
the parallel device.
ParErr_BufTooBig: Buffer too big. Hey, what is this?
ParErr_InvParam: The request block's parameters were not
properly initialized.
ParErr_LineErr: There were some problems with the
communication. The parallel cable is
faulty or the other device is not
properly initialized or not connected.
Tell the user to check the cables!
ParErr_NotOpen: The parallel device was not open!
ParErr_PortReset: The parallel device have just been
resetted. Someone just sent a CMD_Reset
request. Default parameters are
set.
ParErr_InitErr: The parallel device could not be
initialized with your requirements.
(Probably forgot to clear all unused
flags.)
While you are using the parallel device it may happen that you
also receive error messages from Exec. (Exec is handling all
stuff like messages, requests, tasks and so on.) Here is
a complete list of exec error messages: (defined in the header
file "exec/errors.h")
IOERR_OPENFAIL The device (unit) could not be opened. (If you
are denied access to the parallel device you
should receive the ParErr_DevBusy flag instead
of this exec message, but internally this flag
is used.)
IOERR_ABORTED When you abort a previously started request by
calling the AbortIO() function, the io_Error
filed of that request is set to IOERR_ABORTED.
If you find a request block with this flag set,
you know that it has been aborted.
IOERR_NOCMD You tried to use a command that is not
supported by the parallel device.
IOERR_BADLENGTH The length of the request was not valid.
8.3.8 CLEAN UP
As usual on the Amiga you must remember to close and return
everything you have opened or allocated. If you do not close
the parallel device after you, other programs will then not
be able to use the it. PLEASE be very careful about this!
The routine is very similar to how you close the serial device
which was described in the previous chapter. However this is
so important that it can not be said too often.
Here is a list of what you have to do:
1. All requests you have started with SendIO() or BeginIO()
(asynchronous commands) must either have been completed
or aborted before you may close the device. It is a very
common error to forget this, and it can be hard to find
this bug. Usually the program will work fine (the command
was completed in time), but now and then your program will
crash (the command was completed after the device have
been closed).
A simple way is to abort all commands that have not
reported that they have been completed, but this is not
always good way to do it. (The last commands may be
important and should therefore not be aborted.)
If you do not want to abort the command, you should
instead wait for it to be completed. The WaitIO()
function is simple to use, and will put your program
to sleep while waiting, so no computer time is wasted.
If the request has already been completed, the function
will return immediately. WaitIO() will also remove the
message from the reply port. It is a very useful and
simple function to use, but do NOT try to wait for
a request that has not been started!
Here is an example on how to wait for a request to be
completed: (If the request already has been completed
it does not matter, WaitIO() will then simply return
immediately. Note that we do not have to remove any
messages from the reply port if we use WaitIO().)
/* Store possible error numbers here: */
UBYTE error;
/* ... */
/* Wait for the request to be completed: */
error = WaitIO( ioreq );
/* Everything OK? */
if( error )
printf( "Something went wrong!" );
/* Well, successful or not, we may now */
/* close the device! */
To abort a request, simply use the AbortIO() function:
/* Try to abort a previously started request: */
/* (Do not try to abort a request that has not */
/* been started.) */
AbortIO( ioreq );
2. When all requests have been completed or aborted you may
close the parallel device. (Requests that have been
executed by calling the DoIO() function have already been
completed before your program wakes up, and thus you do
not need to wait for these.)
The parallel device is closed as all other devices, by
calling the CloseDevice() function. Here is an example:
/* Close the Parallel Device: */
if( !parallel_dever )
CloseDevice( ioreq );
3. You should now return all request blocks you have
allocated. If you allocated a standard sized request block
by calling the CreateStdIO() function, you should free
it by calling the DeleteStdIO() function. However, if
you have allocated an extended request block (like the
parallel device's request blocks) by calling the
CreateExtIO() function, you MUST call the DeleteExtIO(),
and of course remove the same amount of data as you
allocated.
How to delete a standard request block: (Since it is of
the standard size, you do not need to specify any size.)
/* Deallocate a standard sized request block: */
DeleteStdIO( ioreq );
With extended request block you have to specify the size
as you did when you allocated it:
/* Deallocate an extended request block: */
/* (The size may vary depending on what */
/* device it was used for.) */
DeleteExtIO( ioreq, sizeof( struct IOExtSer ) );
Note that ALL request blocks that have been allocated,
must be removed!
4. Finally you should close all message ports you have
previously opened. Simply use the DeletePort() function
as this example demonstrates:
/* Remove the replyport: */
DeletePort( replymp);
Please be careful with how your program terminates! Your
program should not only run fine, but it should also allow
other programs to run after and simultaneously. Remember that
your program must also be able to quit nice and neatly even if
it had to terminate too early because of some fatal error. The
cleaning up should only be done where it is needed, and if you
have not allocated the memory or opened the device before your
program quits, you should of course NOT try to free these
resources! If you do the Amiga will most certainly crash! Too
many programs contain this very annoying error. Make sure yours
will not be the same.
The best way to manage this cleaning up routine is to write a
separate function which checks each thing before it frees it.
The idea is that you may call this routine at any time, and it
will still manage to clear everything properly. Here is an
example: (Note how we check each thing to see if it should be
removed!)
/* Close and return everything that has been */
/* opened and allocated before we quit: */
void clean_up()
{
/* 1. Close the Parallel Device: */
if( !parallel_dever )
CloseDevice( parallel_req );
/* 2. Deallocate the parallel request block: */
if( parallel_req )
DeleteExtIO( parallel_req, sizeof( struct IOExtPar ) );
/* 3. Remove the replyport: */
if( replymp )
DeletePort( replymp);
/* 4. Quit: */
exit( 0 );
}
Comments: 1. The parallel_dever is a variable that we have
declared ourself, and is used only to determine
if the device has been opened or not. (The
variable is set to TRUE before the program
starts, and is only changed to FALSE after
a successful opening of the device. See the
included examples for more information.) We
should of course only close the device if
the parallel_dever is FALSE. ("parallel_dever"
stands for "parallel device error".)
2. The parallel_req is a pointer which is either
pointing to a request block, or NULL if no
request block has been allocated. We should
of course only free the request block if there
exist one.
3. The replymp is a pointer which is either
pointing to a message port, or NULL if no message
port has been opened. And of course, we should
only close the message port if it has been
opened.
4. Finally our program may terminate. The exit()
function is placed here so we are sure the
program will quit. (If we did not have this
exit(), our function would then return the
control to our program again, and then we would
be in deep s*#^%.)
8.4 A COMPLETE EXAMPLE
Here is a complete example that opens the parallel device, sets
all necessary parameters, sends some data, collects some data
(well actually only 0 bytes) and finally cleans up and quits.
Since we send the read and write requests after each other (the
first is completed before the other is started) we only need to
use one request block.
#include <exec/types.h>
#include <devices/parallel.h>
/* Declare a pointer to our reply port: */
struct MsgPort *replymp = NULL;
/* Declare a pointer to our parallel request block: */
struct IOExtPar *parallel_req = NULL;
/* Store the parallel device error here: */
UWORD parallel_dever = TRUE;
/* Declare our own data buffer: */
/* (Last byte is a NULL sign.) */
BYTE buffer[] = "Anders Bjerin was here...";
/* Declare our functions: */
void main();
void clean_up( STRPTR text );
void main()
{
int loop;
UBYTE *ptr;
UBYTE error;
/* The eight end-of-file characters: */
UBYTE eof_char[8]={ 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 };
/* OPEN THE PARALLEL DEVICE: */
/* Get a reply port: (No name, priority 0) */
replymp = (struct MsgPort *)
CreatePort( NULL, 0 );
if( !replymp )
clean_up( "Could not create the reply port!" );
/* Create a parallel request block: */
parallel_req = (struct IOExtPar *)
CreateExtIO( replymp, sizeof( struct IOExtPar ) );
if( !parallel_req )
clean_up( "Not enough memory for the parallel request block!" );
/* Since we want exclusive access mode, we do not set the */
/* parallel flag "PARF_SHARED", which otherwise must have */
/* been set before the device is opened. To make sure the */
/* field is empty we set it to 0: */
parallel_req->io_ParFlags = 0;
/* Open the Parallel Device: */
parallel_dever = OpenDevice( PARALLELNAME, 0, parallel_req, 0 );
if( parallel_dever )
clean_up( "Could not open the Parallel Device!" );
/* SET THE REST OF THE PARALLEL PARAMETERS (not many): */
/* Check for end-of-file characters: */
parallel_req->io_ParFlags = PARF_EOFMODE;
/* No additional flags: */
parallel_req->io_PExtFlags = NULL;
/* Set all eight end of file characters: */
ptr = (UBYTE *) &(parallel_req->io_PTermArray);
for( loop=0; loop < 8; loop++ )
{
/* Copy character after character: */
*ptr = eof_char[ loop ];
/* Step one byte forward: */
ptr++;
}
/* All values have now been set, lets */
/* do a PDCMD_SETPARAMS request: */
parallel_req->IOPar.io_Command = PDCMD_SETPARAMS;
/* Do our request: */
error = DoIO( parallel_req );
if( error )
clean_up( "Could not set the parallel parameters!" );
/* SEND DATA TO THE PARALLEL PORT: */
/* We want to send (write) some data: */
parallel_req->IOPar.io_Command = CMD_WRITE;
/* Give the start address of our data: */
parallel_req->IOPar.io_Data = (APTR) buffer;
/* We want to send data until we find a NULL sign: */
/* (The NULL sign was specified in the EOF-char.) */
parallel_req->IOPar.io_Length = -1;
/* Do our request: */
error = DoIO( parallel_req );
if( error )
clean_up( "Could not send data to the parallel port!" );
/* READ DATA FROM THE PARALLEL DEVICE: */
/* We want to read some data: */
parallel_req->IOPar.io_Command = CMD_READ;
/* Give the start address of our buffer: */
parallel_req->IOPar.io_Data = (APTR) buffer;
/* We want to read 0 bytes: */
/* (Since most of you do not have anything which sends data */
/* to the parallel device, I do not want to wait for more */
/* than 0 bytes to arrive.) */
parallel_req->IOPar.io_Length = 0;
/* Do our request: */
error = DoIO( parallel_req );
if( error )
clean_up( "Could not read data from the parallel port!" );
/* THE END: */
clean_up( "The End" );
}
/* Close and return everything that has been */
/* opened and allocated before we quit: */
void clean_up( STRPTR text )
{
/* Close the Parallel Device: */
if( !parallel_dever )
CloseDevice( parallel_req );
/* Deallocate the parallel request block: */
if( parallel_req )
DeleteExtIO( parallel_req, sizeof( struct IOExtPar ) );
/* Remove the replyport: */
if( replymp )
DeletePort( replymp);
/* Print the message: */
printf( "\n%s\n", text );
/* Quit: */
exit( 0 );
}
8.5 OTHER USEFUL COMMANDS
Although reading and writing are most commonly used commands,
there exist some other functions that can sometimes be needed.
Here is a complete list of commands that can be sent with help
of a request block:
1. Flush, removes all queued requests.
2. Query, get some information from the parallel device.
3. Reset, reinitializes the parallel device.
4. Start, restarts the parallel communication.
5. Stop, temporary stops the parallel communication.
8.5.1 FLUSH
If several requests are sent to the parallel device they are
all queued on a FIFO (First In First Out) basis. The command
"CMD_FLUSH" can then be used to remove all these queued
commands. Here is an example:
/* We want to remove all queued requests: */
ioreq->IOPar.io_Command = CMD_FLUSH;
/* Do our request: */
error = DoIO( ioreq );
/* OK? */
if( error )
printf( "Could not remove the queued requests!\n" );
8.5.2 QUERY
The command "PDCMD_QUERY" can be used to get some information
from the parallel device. It is useful if you want to see if
the paper is out, printer is busy or if it is currently writing
or reading. All this can be found in the "io_Status" field of
the request structure, as this table shows:
Name Bit Hex Active Description
------------------------------------------------------------
IOPTF_PARBUSY 0 0x01 Low Printer is selected
IOPTF_PAPEROUT 1 0x02 Low Paper out
IOPTF_PARSEL 2 0x04 Low Printer is busy
IOPTF_RWDIR 3 0x08 - Reading (0) or Writing (1)
- 4 0x10 - Reserved
- 5 0x20 - Reserved
- 6 0x40 - Reserved
- 7 0x80 - Reserved
------------------------------------------------------------
Here is an example:
/* Check the parallel device: */
ioreq->IOPar.io_Command = PDCMD_QUERY;
/* Do our request: */
error = DoIO( ioreq );
/* OK? */
if( error )
printf( "Could not get any information from the device!\n" );
else
{
/* Check the "io_Status" field: */
if( ioreq->io_Status & IOPTF_PARBUSY )
printf( "Printer is busy.\n" );
if( ioreq->io_Status & IOPTF_PAPEROUT )
printf( "Paper out!\n" );
if( ioreq->io_Status & IOPTF_PARSEL )
printf( "Printer selected!\n" );
printf( "Device is %s\n",
ioreq->io_Status & IOPTF_RWDIR ? "Writing" : "Reading" );
}
8.5.3 RESET
Send the command "CMD_RESET" to reset the parallel device. All
commands that are queued to the device will be removed, commands
that are currently executed will be aborted all parallel flags
are resetted. Here is an example:
/* We want to reset the parallel device: */
ioreq->IOPar.io_Command = CMD_RESET;
/* Do our request: */
error = DoIO( ioreq );
/* OK? */
if( error )
printf( "Could not reset the parallel device!\n" );
8.5.4 START
After you have stopped the parallel communication by sending an
CMD_STOP command, you may want to start the communication again.
It is done by sending a CMD_START command. Here is an example:
/* We want to start parallel communication again: */
ioreq->IOPar.io_Command = CMD_START;
/* Do our request: */
error = DoIO( ioreq );
/* OK? */
if( error )
printf( "Could not start the parallel communication!\n" );
8.5.5 STOP
To temporary stop all parallel communication you send a CMD_STOP
command. The communication will then first start again when a
CMD_START command is broadcasted. Here is an example:
/* We want to temporary stop all parallel communication: */
ioreq->IOPar.io_Command = CMD_STOP;
/* Do our request: */
error = DoIO( ioreq );
/* OK? */
if( error )
printf( "Could not stop the parallel communication!\n" );
8.6 FUNCTIONS
DoIO()
DoIO() is used to send requests to a device, and waits for it
to be completed. While the program is waiting it is put to
sleep so it will not waste any computer time. DoIO() will
return first when the request have been completed or failed,
and no message is therefore sent to the reply port.
Synopsis: error = DoIO( req );
error: (long) DoIO() will return first when the request has
been completed or something has failed. If the
request was successfully completed zero is returned,
else an error number is returned. What error number
depends on which device was used.
req: (struct IORequest *) Pointer to the request you
want to have executed.
SendIO()
SendIO() is used to send requests to a device, but will
return immediately without any delay. To check if the request
have been completed use the CheckIO() function, or look
at the request's reply port for any messages. Once the
request has been completed you must remove the message at
the reply port. (CheckIO() will not do it.) To remove a
message use the function Remove(). Note that you may NOT
close the device before all requests have been completed or
aborted!
Synopsis: SendIO( req )
req: (struct IORequest *) Pointer to the request you
want to have executed.
CheckIO()
CheckIO() is used to check if a previously started request
has been completed. Note that this function will not remove
the message at the reply port. This must be done with the
Remove() function.
Synopsis: ptr = CheckIO( req );
ptr: (long) CheckIO() will either return NULL if the
request have not been completed or it will return a
pointer to the request block.
req: (struct IORequest *) Pointer to the request you
want to check.
WaitIO()
WaitIO() will wait for the request to be completed, and while
the program is waiting it is put to sleep so no computer time
is wasted.
Synopsis: error = WaitIO( req );
error: (long) WaitIO() will return first when the request,
that has previously been sent, has been completed or
something has failed. If the request was successfully
completed zero is returned, else an error number is
returned. What error number depends on which device
was used.
req: (struct IORequest *) Pointer to the request you
want to wait for to be completed. Note that the
request must have already been sent to the device
by either a SendIO() or BeginIO() function call.
BeginIO()
BeginIO() is a low level form of the SendIO() function. The
advantage with BeginIO() is that no fields of the request
block will be altered as which is the case with SendIO().
With the parallel device you should only use SendIO().
Synopsis: BeginIO( req )
req: (struct IORequest *) Pointer to the request you
want to have executed.
AbortIO()
AbortIO() will try to abort a previously started request. This
function should be used sparsely since it does not look so
good if you start a request and the try to stop it. (Better
not start it at all.) However, it is easy, and can sometimes
be very useful.
A request that is aborted will have its io_Error field set
to IOERR_ABORTED (defined in header file "exec/errors.h").
Synopsis: AbortIO( req )
req: (struct IORequest *) Pointer to the request you
want to abort.
CloseDevice()
CloseDevice() will (surprise!) close a device. Note that you
should NOT close the device before all started asynchronous
requests have either been completed or aborted.
Synopsis: CloseDevice( ioreq );
ioreg: (struct IORequest *) Pointer to the device's
request block.
OpenDevice()
OpenDevice() will try to open the specified device.
Synopsis: error = OpenDevice( name, unit, req, flags );
error: (long) If OpenDevice() managed to open the device
it returns 0, else an error number is returned.
If you try to open the parallel device, and there is
already a program that is using it, the error
message "ParErr_DevBusy" is returned.
name: (char *) Name of the device you want to open.
The name of the parallel device is defined as
PARALLELNAME in header file "devices/parallel.h".
unit: (long) Which unit you want to open. Since there
exist only one parallel port, this field is ignored.
req: (struct IORequest *) Pointer to a request block.
For the parallel device it must be a pointer to an
extender parallel request block (struct IOExtPar).
flags: (long) Any special mode is set here. Ignored by
the parallel device.
8.7 COMMANDS
Here is a complete list of commands you may send to the
parallel device. For full documentation se examples above.
The special parallel device commands: (Defined in header file
"devices/parallel.h")
PDCMD_QUERY Check the status of the parallel device.
PDCMD_SETPARAMS Set the parameters of the parallel device.
The rest of the commands you may use are normal exec commands,
and are defined in header file "exec/io.h".
CMD_RESET Resets all parameters of the parallel device.
CMD_READ Read data from the parallel port.
CMD_WRITE Write data to the parallel port.
CMD_STOP Temporary stops all parallel communication.
CMD_START Restarts parallel communication.
CMD_FLUSH Removes all queued requests.
8.8 EXAMPLES
Example 1
This program demonstrates how you can use the Parallel Device.
It does not do very much since I do not know what you have
connected to your parallel port, but with small modifications
you should be able to write your own parallel communication
packages.
Example 2
This example is rather similar to Example 1, but this time
we do not wait for the parallel port to complete our request.
Instead we do somethings (well not very much) and now and
then checks if the request has been completed.
Example 3
This example is rather similar to Example 1, but this time
we do not wait for the parallel port to complete our request.
We are also trying to read and write at the same time. To be
able to do several requests simultaneously we need one request
block for each command. In this example we use three separate
request blocks.
Example4
This example does not do anything, but it consists of
several useful functions that you can use yourself after
small modifications. The functions demonstrates all
commands there exist for the parallel device, so if you
had problems in understanding how a command was used you
can look here.