Copyright (c) Hyperion Entertainment and contributors.

AmigaDOS Packets

From AmigaOS Documentation Wiki
Jump to navigation Jump to search
WIP.png This page is currently being updated to AmigaOS 4.x. Some of the information contained here may not yet be applicable in part or totally.

Introduction

Packet passing handles all communication performed by AmigaDOS between processes. The function diagram below shows how packets fit in with the other components of the Amiga operating system.

   +--------------+
   | User Process +-----------------------+
   +-------+------+                       |
           |                              |
           |                              |
    Function Calls                        |
           |                              |
          \|/                             |
 +---------+--------+                     |
 | AmigaDOS Open(), |                     |
 |   Close(), etc.  |                     |
 +---------+--------+                     |
           |                              |
           |                              |
      +-Packets---+--------+     +-----Packets--------+
      |           |        |     |        |           |
     \|/         \|/      \|/   \|/      \|/         \|/
 +----+----+ +----+----+ +-+--+--+-+ +----+----+ +----+----+
 | FFS/OFS | | FFS/OFS | | FFS/OFS | |   CON:  | |   CON:  |
 |   DH0:  | |   DF0:  | |   DF1:  | | Window1 | | Window2 |
 | Handler | | Handler | | Handler | | Handler | | Handler |
 | Process | | Process | | Process | | Process | | Process |
 +----+----+ +-------+-+ +----+----+ +----+----+ +----+----+
      |              |        |           |           |
     \|/            \|/      \|/         \|/         \|/
 +----+--------+  +--+--------+----+ +----+-----------+----+
 |Hddisk.device|  |Trackdisk.device| |    Console.device   |
 +-------------+  +----------------+ +---------------------+

A StandardPacket (defined in <dos/dosextens.h>) is used to send packet commands to a process's MsgPort. The StandardPacket structure contains an Exec Message structure and an AmigaDOS DOSPacket structure:

   struct StandardPacket
   {   struct Message   sp_Msg;
       struct DOSPacket sp_Pkt;
   };

This structure must be longword-aligned, and initialized to link the Message and DOSPacket sections to each other:

   packet->sp_Msg.mn_Node.ln_Name = (char *) &(packet->sp_Pkt);
   packet->sp_Pkt.dp_Link         = &(packet->sp_Msg);

Packets must also be initialized with a ReplyPort which can be created with the amiga.lib function CreatePort():

   if (replyport = (struct MsgPort *) CreatePort(NULL, 0))
       packet->sp_Pkt.dp_Port = replyport;

The DOSPacket portion of the StandardPacket structure is used to pass the packet type and arguments, and to receive the results of the packet. The argument types, number of arguments, and results vary for different packet types and are documented with each packet description. A DOSPacket must be longword-aligned and has the following general structure:

Type Name Description
struct Message* dp_Link Pointer back to Exec message structure
struct Message* dp_Port Reply port for the packet. Must be filled in each send
LONG dp_Type Packet type
LONG dp_Res1 For filesystem calls this is the result that would have been returned by the function; eg. Write("W") returns actual length written.
LONG dp_Res2 For filesystem calls this is what would have been returned by IoErr()
LONG dp_Arg1 Argument 1 (depends on packet type)
LONG dp_Arg2 Argument 2 (depends on packet type)
LONG dp_Arg3 Argument 3 (depends on packet type)
LONG dp_Arg4 Argument 4 (depends on packet type)
LONG dp_Arg5 Argument 5 (depends on packet type)
LONG dp_Arg6 Argument 6 (depends on packet type)
LONG dp_Arg7 Argument 7 (depends on packet type)

The format of a specific packet depends on its type; but in all cases it contains a back-pointer to the Message structure, the MsgPort for the reply, and two result fields. When AmigaDOS sends a packet, the reply port is overwritten with the process ID of the sender so that the packet can be returned. Thus, when sending a packet to an AmigaDOS handler process, you must fill in the reply MsgPort each time; otherwise when the packet returns, AmigaDOS has overwritten the original port. AmigaDOS maintains all other fields except the result fields.

All AmigaDOS packets are sent to the message port created as part of a process; this message port is initialized so that arriving messages cause signal bit 8 to be set. An AmigaDOS process that is waiting for a message waits for signal 8 to be set. When the process wakes up because this event has occurred, GetMsg() takes the message from the message port and extracts the packet address. If the process is an AmigaDOS handler process, then the packet contains a value in the PktType field that indicates an action to be performed, such as reading some data. The argument fields contain specific information such as the size of the buffer where the characters go.

When the handler process has completed the work required to satisfy this request, the packet returns to the sender, using the same message structure. Both the message structure and the packet structure must be allocated by the client and not deallocated before the reply has been received. Normally AmigaDOS is called by the client to send the packet, such as when a call to Read() is made. However, there are cases where asynchronous I/O is required, and in this case the client may send packets to the handler process as required. The packet and message structures must be allocated, and the process ID field filled in with the message port where this packet must return. A call to PutMsg() then sends the message to the destination. Note that many packets may be sent out, returning to either the same or different message ports.

Packet Types

Packets sent to a filesystem or handler can be divided into several basic categories:

Basic Input/Output.
These actions deal with transferring data to and from objects controlled by the handler.
File/Directory Manipulation/Information.
These actions are used to gain access to and manipulate the high-level structures of the filesystem.
Volume Manipulation/Information.
These actions allow access to the specific volume controlled by the filesystem.
Handler Maintenance and Control.
These actions allow control over the handler/filesystem itself, independent of the actual volume or structure underneath.
Handler Internal.
These actions are never sent to the handler directly. Instead they are generally responses to I/O requests made by the handler. The handler makes these responses look like packets to simplify processing.
Obsolete Packets.
These packets are no longer valid for use by handlers and filesystems.
Console Only Packets.
These packets are specific to console handlers. Filesystems can ignore these packets.

Each packet type documented in this section is listed with its action name, its corresponding number, any AmigaDOS routines that use this packet, and the list of parameters that the packet uses. The C variable types for the packet parameters are one of the following types:

Type Description
BPTR This is a BCPL pointer (the address of the given object shifted right by 2).
Note: This means that the object must be aligned on a longword boundary.
LOCK This is a BPTR to a FileLock structure returned by a previous ACTION_LOCATE_OBJECT. A lock of 0 is legal, indicating the root of the volume for the handler.
BSTR This is a BPTR to a string where the first byte indicates the number of characters in the string. A byte of this length is unsigned but because the information is stored in a byte, the strings are limited to 255 characters in length.
BOOL A 32-bit boolean value either containing DOSTRUE (-1) or DOSFALSE (0).
Note: Equality comparisons with DOSTRUE should be avoided.
CODE A 32-bit error code as defined in the <dos/dos.h> include file. Handlers should not return error codes besides those defined in <dos/dos.h>.
ARG1 The FileHandle->fh_Arg1 field.
LONG A 32-bit integer value.

Basic Input/Output

The Basic Input/Output actions are supported by both handlers and file systems. In this way, the application can get a stream level access to both devices and files. One difference that arises between the two is that a handler will not necessarily support an ACTION_SEEK while it is generally expected for a file system to do so.

These actions work based on a FileHandle which is filled in by one of the three forms of opens:

Packet Mnemonic          ID      Function Syntax
======================== ======= ==========================
ACTION_FINDINPUT         1005    Open(..., MODE_OLDFILE) 
ACTION_FINDOUTPUT        1006    Open(..., MODE_NEWFILE)
ACTION_FINDUPDATE        1004    Open(..., MODE_READWRITE)
ARG1:   BPTR    FileHandle to fill in
ARG2:   LOCK    Lock on directory that ARG3 is relative to
ARG3:   BSTR    Name of file to be opened (relative to ARG1)

RES1:   BOOL    Success/Failure (DOSTRUE/DOSFALSE)
RES2:   CODE    Failure code if RES1 is DOSFALSE

All three actions use the lock (ARG2) as a base directory location from which to open the file. If this lock is NULL, then the file name (ARG3) is relative to the root of the current volume. Because of this, file names are not limited to a single file name but instead can include a volume name (followed by a colon) and multiple slashes allowing the file system to fully resolve the name. This eliminates the need for AmigaDOS or the application to parse names before sending them to the file system. Note that the lock in ARG2 must be associated with the file system in question. It is illegal to use a lock from another file system.

The calling program owns the file handle (ARG1). The program must initialize the file handle before trying to open anything (in the case of a call to Open(), AmigaDOS allocates the file handle automatically and then frees it in Close() ). All fields must be zero except the fh_Pos and fh_End fields which should be set to -1. The Open() function fills in the fh_Type field with a pointer to the MsgPort of the handler process. Lastly, the handler must initialize fh_Arg1 with something that allows the handler to uniquely locate the object being opened (normally a file). This value is implementation specific. This field is passed to the READ/WRITE/SEEK/ END/TRUNCATE operations and not the file handle itself.

FINDINPUT and FINDUPDATE are similar in that they only succeed if the file already exists. FINDINPUT will open with a shared lock while FINDUPDATE will open it with a shared lock but if the file doesn't exist, FINDUPDATE will create the file. FINDOUTPUT will always open the file (deleting any existing one) with an exclusive lock.

Packet Mnemonic          ID      Function Syntax
======================== ======= ==========================
ACTION_READ              'R'     Read(...)
ARG1:   ARG1    fh_Arg1 field of the opened FileHandle
ARG2:   APTR    Buffer to put data into
ARG3:   LONG    Number of bytes to read

RES1:   LONG    Number of bytes read.
       0 indicates EOF.
      -1 indicates ERROR
RES2:   CODE    Failure code if RES1 is -1

This action extracts data from the file (or input channel) at the current position. If fewer bytes remain in the file than requested, only those bytes remaining will be returned with the number of bytes stored in RES1. The handler indicates an error is indicated by placing a -1 in RES1 and the error code in RES2. If the read fails, the current file position remains unchanged. Note that a handler may return a smaller number of bytes than requested, even if not at the end of a file. This happens with interactive type file handles which may return one line at a time as the user hits return, for example the console handler, CON:.

Packet Mnemonic          ID      Function Syntax
======================== ======= ==========================
ACTION_WRITE             'W'     Write(...)
ARG1:   ARG1    fh_Arg1 field of the opened file handle
ARG2:   APTR    Buffer to write to the file handle
ARG3:   LONG    Number of bytes to write

RES1:   LONG    Number of bytes written.
RES2:   CODE    Failure code if RES1 not the same as ARG3

This action copies data into the file (or output channel) at the current position. The file is automatically extended if the write passes the end of the file. The handler indicates failure by returning a byte count in RES1 that differs from the number of bytes requested in ARG3. In the case of a failure, the handler does not update the current file position (although the file may have been extended and some data overwritten) so that an application can safely retry the operation.

Packet Mnemonic          ID      Function Syntax
======================== ======= ==========================
ACTION_SEEK              1008    Seek(...)
ARG1:   ARG1    fh_Arg1 field of the opened FileHandle
ARG2:   LONG    New Position
ARG3:   LONG    Mode:   OFFSET_BEGINNING,OFFSET_END, or  OFFSET_CURRENT

RES1:   LONG    Old Position.   -1 indicates an error
RES2:   CODE    Failure code if RES1 = -1

This packet sets the current file position. The new position (ARG2) is relative to either the beginning of the file (OFFSET_BEGINNING), the end of the file (OFFSET_END), or the current file position (OFFSET_CURRENT), depending on the mode set in ARG3. Note that ARG2 can be negative. The handler returns the previous file position in RES1. Any attempt to seek past the end of the file will result in an error and will leave the current file position in an unknown location.

Packet Mnemonic          ID      Function Syntax
======================== ======= ==========================
ACTION_END               1007    Close(...)
ARG1:   ARG1    fh_Arg1 field of the opened FileHandle

RES1:   LONG    DOSTRUE

This packet closes an open file handle. This function generally returns a DOSTRUE as there is little the application can do to recover from a file closing failure. If an error is returned under 2.0, DOS will not deallocate the file handle. Under 1.3, it does not check the result.

Packet Mnemonic          ID      Function Syntax
======================== ======= ==========================
ACTION_LOCK_RECORD       2008    LockRecord(fh,pos,len,mod,tim)
ARG1:   BPTR    FileHandle to lock record in
ARG2:   LONG    Start position (in bytes) of record in the file
ARG3:   LONG    Length (in bytes) of record to be locked
ARG4:   LONG    Mode
                 0 = Exclusive
                 1 = Immediate Exclusive (timeout is ignored)
                 2 = Shared
                 3 = Immediate Shared (timeout is ignored)
ARG5:   LONG    Timeout period in AmigaDOS ticks (0 is legal)

RES1:   BOOL    Success/Failure (DOSTRUE/DOSFALSE)
RES2:   CODE    Failure code if RES1 is DOSFALSE

This function locks an area of a file in either a sharable (indicating read-only) or exclusive (indicating read/write) mode. Several sharable record locks from different file handles can exist simultaneously on a particular file area but only one file handle can have exclusive record locks on a particular area at a time. The exclusivity of an exclusive file lock only applies to record locks from other file handles, not to record locks within the file handle. One file handle can have any number of overlapping exclusive record locks. In the event of overlapping lock ranges, the entire range must be lockable before the request can succeed. The timeout period (ARG5) is the number of AmigaDOS ticks (1/50 second) to wait for success before failing the operation.

Packet Mnemonic          ID      Function Syntax
======================== ======= ==========================
ACTION_FREE_RECORD       2009    UnLockRecord(file,pos,len)
ARG1:   BPTR    FileHandle to unlock record in
ARG2:   LONG    Start position (in bytes) of record in the file
ARG3:   LONG    Length of record (in bytes) to be unlocked

RES1:   BOOL    Success/Failure (DOSTRUE/DOSFALSE)
RES2:   CODE    Failure code if RES1 is DOSFALSE

This function unlocks any previous record lock. If the given range does not represent one that is currently locked in the file, ACTION_FREE_RECORD returns an error. In the event of multiple locks on a given area, only one lock is freed.

Packet Mnemonic          ID      Function Syntax
======================== ======= ==========================
ACTION_SET_FILE_SIZE     1022    SetFileSize(file,off,mode)
ARG1:   BPTR    FileHandle of opened file to modify
ARG2:   LONG    New end of file location based on mode
ARG3:   LONG    Mode.  One of OFFSET_CURRENT, OFFSET_BEGIN, or OFFSET_END

RES1:   BOOL    Success/Failure (DOSTRUE/DOSFALSE)
RES2:   CODE    Failure code if RES1 is DOSFALSE

This function is used to change the physical size of an opened file. ARG2, the new end-of-file position, is relative to either the current file position (OFFSET_CURRENT), the beginning of the file (OFFSET_BEGIN), or the end of the file (OFFSET_END), depending on the mode set in ARG3. The current file position will not change unless the current file position is past the new end-of-file position. In this case, the new file position will move to the new end of the file. If there are other open file handles on this file, ACTION_SET_FILE_SIZE sets the end-of-file for these alternate file handles to either their respective current file position or to the new end-of-file position of the file handle in ARG1, whichever makes the file appear longer.

Directory/File Manipulation/Information

Volume Manipulation/Information

Handler Maintenance and Control

Handler Internal

Obsolete Packets

Console Only Packets

Summary of Defined Packet Numbers

Using Packets Directly

AmigaDOS contains many features that can only be accessed by sending a packet directly to a process. For example, the ACTION_DISK_INFO packet may be used to find the Intuition window pointer of a CON: or RAW: window. This is useful for redirecting system requesters so that they appear where the user can see them (see "Redirecting System Requesters" above). The Window pointer will be returned in the ID_VolumeNode field, and a pointer to the console's I/O request will be returned in the ID_InUse field. Note that auxilary consoles (AUX:) can return a NULL Window pointer, and also may have no ConUnit (io_Unit) associated with their I/O request block. Be careful to check for these possibilities when you use this packet. If your application runs in a CLI window, a user may be running you in an auxilary (AUX:) CLI.

Another example is the ACTION_SCREENMODE_MODE packet which can be sent to the handler process of a CON: window to put the console into raw or cooked mode.

By default, CON: provides mapped keyboard input which is filtered, buffered, and automatically echoed. Many of the special key escape sequences (such as those generated by the function, cursor, and help keys) are filtered out; all strokes are buffered and held back from the reader until the user hits the RETURN key; and the nonfiltered keypresses (such as alphanumeric keys and backspace) are automatically echoed to the CON: window. This "cooked" mode is perfect for general line input from a user because it provides automatic line editing features (same as in the Shell command line).

Sometimes, however, an application needs to get individual keys immediately from a CON: window, or control its own echoing, or receive the escape strings that the keymap generates for special keys such as the Help key or cursor keys.

In this case, an ACTION_SCREEN_MODE packet with the argument DOSTRUE (-1) may be sent to the MsgPort of a CON: window to put the CON: into "raw" mode. In raw mode, a CON: behaves much like a RAW: window. Keyboard console input is not automatically filtered, buffered, or echoed. When reading a CON: which has been set to "raw" mode, each keypress can be read immediately as the ASCII value or string to which the key is mapped by the keymap.

For some applications, it may be convenient to toggle a CON: window between cooked and raw modes, to use cooked mode for use line input, and raw mode when keypresses should cause immediate actions.

ACTION_SCREEN_MODE with the argument DOSFALSE (0L) will place a CON: window in cooked mode. Note that the ACTION_SCREEN_MODE packet may also be used on auxilary (AUX:) consoles.

The handler MsgPort of most named AmigaDOS devices (like DF0:) can be found with the DeviceProc() function. Note that DeviceProc() cannot be used to find a CON: or RAW: handler because there may be many handlers for each of these. The handler MsgPort (ProcessID) of a CON: or RAW: window is in its FileHandle structure (fh_Type). The MsgPort of a CLI process's "*" window is process->pr_ConsoleTask.

Here's how to find the MsgPort of a handler process (in all cases make sure that port is non-NULL before using it):

Finding the MsgPort of a unique named handler process such as "DF0:":

    port = (struct MsgPort *) DeviceProc("DF1:");

Finding the MsgPort of the handler process for an open file:

    fh = Open("CON:0/40/640/140/Test", MODE_NEWFILE);
    if ((fh) && (fh->Type))
    {   /* if Open() succeeded and fh_Type is non-NULL */
        port = (struct MsgPort *)
               (((struct FileHandle *) (fh << 2))->fh_Type);
    }

Finding the MsgPort of your process's console handler:

    struct Task* task = FindTask(NULL);
    if (task->tc_Node.ln_Type == NT_PROCESS)
    {   /* port may be NULL - check before using! */
        port = ((struct Process *) task)->pr_ConsoleTask;
    }

Packets are sent by initializing a longword-aligned StandardPacket structure and sending the packet to the MsgPort of a handler process.

The dos.library provides new simple functions for sending and replying to packets:

   SendPkt() - asynchronously send your initialized packet
   WaitPkt() - wait for asynchronous packet to complete
   ReplyPkt() - reply a packet which has been sent to you
   DoPkt() - creates and sends a packet, and waits for completion