HomeProtocolsFont protocolLTL protocol

15.6 GDPS: Gerti's Driver Piping System

As of: 1990-08-23

15.6.1 General description

This driver concept is intended to enable calling of memory-resident device drivers of various types from any desired programs. The communication between the programs and the drivers takes place via a data structure made available by the driver. A fixed kernel for the data structure is prescribed for this, which must be adhered to by all drivers that comply with this standard. In addition, some extensions are possible. From the version numbers the calling program then can establish if and which extensions of the data structure are present. All data structures are upwardly compatible.

  1. Preliminary remarks

    All values that require more than 1 byte are defined always in the order MSB...LSB. The sample codes are formulated in 68000 Assembler. The description for ATARI ST follows.

15.6.2 Definition of the driver daisy chain

Basis of the GDPS is the vector $41C which is unused by the operating system. Normally this vector contains 0L. In GDPS this vector points to the header of the first device driver in the chain. This header is built up as follows (rAdr=relative Address, S=Size):

     rAdr S  Meaning

      OH  L  Pointer to the next driver header, or 0 in last driver
             of the chain
      4H  L  'GDPS' = 0x47445053 magic string as identifier for GDPS

From this follows the method for linking a driver in and out of the chain.

  1. Linking in of a driver

    The driver copies the vector placed at 0x41C into its header. Subsequently it enters the address of its header into 0x41C.

    Assembler example (to be executed in supervisor-mode):

        len     header(pe),a0  * Address of the GDPS header
        clr.l   (a0)           * Initialize NEXT to 0
        cmp.l   0x41C,a0       * = old value
        beq     unten          * Yes, no endless chaining!
        move.l  0x41C,(a0)     * sonst NEXT eintragen
        move.l  a0,0x41C       * and link header into the chain
    

  2. Unlinking of a driver

    The driver searches the chain for the pointer to its header. It then replaces it with a pointer found in its header.

    Assembler example (to be executed in supervisor-mode):

                   move.l  #0x41C,a0
                   lea     header(pe),a1
       search:     cmp.l   (a0),a1
                   beq     found
                   move.l  (a0),a0
                   bra     search
       found:      move.l  (a1),(a0)
    

  3. Searching for a driver

    Searches for a driver too should be performed in supervisor-mode. As the operating system does not set the variable 0x41C to 0 at a warm start, one has to check for the magic string for each vector found.

    There follows a sample routine in GFA Basic notation that writes the found and valid vectors into an integer array:

       DIM vector%(31)                   ! Here addresses of the drivers lie
       index%=0                          ! Initialize counter to 0
       adr%=SLPEEK($41C)                 ! Address of the first driver
       WHILE adr%<>0 AND SLPEEK(adr%+4)=$47445053
                                         ! Test: Address valid and magic OK?
          vector%(index%)=adr%           ! If yes, remember address
          inc index%                     ! Increment counter by one
       WEND
       PRINT index%;" Driver found!"
    

15.6.3 Data structure

 rAdr S  Meaning
  0H  L  Pointer to the next driver header, or 0 in last driver of
         the chain
  4H  L  'GDPS' = 0x47445053 magic string as identifier for GDPS
  8H  W  100 = Version number (Data structure definition) * 100
  AH  W  Driver type
  CH  L  Pointer to driver info, 0-terminated string,  max. 32 chars.
 10H  L  Pointer to programmer/Copyright, 0-terminated string, max.
         32 characters
 14H onwards follows the driver-specific data structure.

15.6.4 Driver types

There may be any number of drivers in memory, even drivers of the same type!

The driver types are first subdivided into groups:

Group Driver type Meaning
0 000-0FF Graphical input devices
1 100-1FF Graphical output devices
2 200-2FF Various input interfaces (ports)
3 300-3FF Various output interfaces (ports)
4 400-4FF I/O interfaces
5 500-5FF Mass storage
6-F 600-FFF Reserved
10-FF 1000-FFFF 'Private' drivers, whose data structure is not
    contained in the DPS definitions

15.6.4.1 Scanner driver group

Introduction: With bi-level data, a set bit corresponds to a pixel that is present. With multi-value data, a 0 corresponds to `nothing` and with increasing values the relevant colour intensity or brightness rises.

Warning: With commands in the 10xH group, greyscale images are inverted!

  14H  W  Scanner description
          If this WORD contains the value 0 then the scanner/driver is
          not yet initialized, execute command 105H
          Bit 0: Bi-level possible
              1: Dithering possible
              2: Multi-value possible
              3: Multi-value and dithering possible
              4: Bi-level colour possible
              5: Colour dithering possible
              6: Multi-value in colour possible
              7: Multi-value in colour and dithering possible
              8: Compression of data (see below) possible
              9: Block-wise return possible
             10: Single sheet feed with separate command
             11: Single sheet feed automatic
             12: Prescan possible
             13: Virtual memory management possible
                 (GDPS-Version >= 1.10)
             14: Reserved (0)
             15: Reserved (0)
            Multi-value and dithering: Special mode, always 2 bits/pixel
  16H  W  Number of colours
  18H  W  Possible multi-value bit depths
          Makeup:  Bit 0 set: Monochrome possible
                   Bit 1 set: 2 grey-steps possible
                   Bit 2 set: 4 grey-steps possible
                   Bit 3 set: 8 grey-steps possible
                   etc...
  1AH  W  Reserve scanner
  1CH  W  Command to scanner (0=Ready, 1-FFH reserved!!!)
  1EH  L  Pointer to command structure

A scanner driver with its own user interface is assumed here. If a program wants to address the scanner driver, then this is done as follows:

   a) Wait until WORD 1AH = 0 (scanner free)
   b) Reserve scanner be entering a WORD in 1AH
   c) Create a command structure
   d) Enter the pointer to the command structure in 1EH
   e) Enter the command in 1CH
   f) In an EVENT loop (!!) (timer- or multi-event) wait until the
      command WORD of the scanner driver is set to 0 again
   g) Repeat steps from c) onwards as often as required
   h) Release scanner (enter 0 in 1AH)

The command structure to be made available by the calling program has three tasks:

   a) Passsing of the command structure
   b) Return of a success or error message
   c) Return of the parameters used

Makeup of the command structure:

   00H  W  Return value, is initialized by the calling program to 0.
           After processing the command, the scanner driver enters
           0xFFFF (=OK) or a positive error-message here.
           If the scanner is operating in block mode, then 0xFFFE is
           entered after a block and 0xFFFF after the last block. If
           working in block mode AND in colour, there are two options:
           a) The scanner scans the colour separations individually;
              then the passing will proceed as above, but several
              times corresponding to the number of colours.
           b) The scanner scans all colours simultaneously; in that
              case 0xFFFD is entered after the blocks. The returned
              block then consists of 'number of colours' equal-sized
              areas.
   02H  W  Permitted scan modes. Makeup of this WORD is as for 14H;
           a set bit denotes that the scanner driver may use the
           corresponding mode.
   04H  W  Permitted bit depths (makeup as above for 18H)
   06H  L  Pointer to memory
   0AH  L  Number of bytes in memory (Return: Number of bytes used)
   0EH  W  Bytes per scanline, or 0 (always mod 2!)
   10H  W  Number of scanlines, or 0
   12H  W  Width of the image in 1/10 mm, or 0
   14H  W  Height of the image in 1/10 mm, or 0
   16H  W  Resolution in main scan direction (X) in dpi, or 0
   18H  W  Resolution in sub scan direction (Y) in dpi, or 0
           (For return: Resolution = Resolution * zoom factor!)
   1AH  W  Modulo value for scanlines (in bytes, e.g. 2 = 2*n bytes
           per scanline)
   1CH  W  X-position of the image in 1/10 mm
   1EH  W  Y-position of the image in 1/10 mm

Since GDPS Version >= 1.10:
   20H  L  Serial number of the calling program
   24H  W  (!I)add_bits:(!i) Additional memory required by program (in bits
           per pixel)
   26H  L  Pointer to 'Dchange_pointer' (function of the vmm)
   2AH  L  Pointer to 'Dupdate' (function of the vmm)
   2EH  W  (!I)v_handle(!i) READ (vmm)
   30H  W  (!I)v_handle(!i) WRITE (vmm)
   32H  W  (!I)virt_flag(!i) (1 = vmm being used)

vmm = virtual memory management

After processing a command the scanner driver enters the actually used values into this data structure.

If the image size is to remain fixed, then this can be defined with the WORDS 0EH, 10H or 12H, 14H, 1CH, 1EH. If both value pairs are preset, the default in bytes will be used. If the scanner is not able to scan in this format, a change may be made to suitable values if necessary; therefore, check the return vales!!

Compression:

Bi-level and dither data are always packed in such a way that 8 pixels are present in a byte. For this the first pixel is entered in the most significant bit.

Multi-value data are present either uncompressed, in which case one pixel occupies exactly one byte. Here (with fewer than 256 grey steps) the data is always formatted to the MS bit, and the LS bits are filled if appropriate (depending on the scanner with 0 or 1). In compressed form the multi-value data are packed in such a way that byte borders are not exceeded; if necessary empty bits are filled with (depending on the scanner) with 0 or 1:

                 +--+--+--+--+--+--+--+--+
   2 bits/pixel  |B1|B0|B1|B0|B1|B0|B1|B0|   -> 4 pixels/byte
                 +--+--+--+--+--+--+--+--+

                 +--+--+--+--+--+--+--+--+
   3 bits/pixel  |B2|B1|B0|--|B2|B1|B0|--|   -> 2 pixels/byte
                 +--+--+--+--+--+--+--+--+

                 +--+--+--+--+--+--+--+--+
   4 bits/pixel  |B3|B2|B1|B0|B3|B2|B1|B0|   -> 2 pixels/byte
                 +--+--+--+--+--+--+--+--+

                 +--+--+--+--+--+--+--+--+
   5 bits/pixel  |B4|B3|B2|B1|B0|--|--|--|   -> 1 pixel/byte
                 +--+--+--+--+--+--+--+--+

                 +--+--+--+--+--+--+--+--+
   6 bits/pixel  |B5|B4|B3|B2|B1|B0|--|--|   -> 1 pixel/byte
                 +--+--+--+--+--+--+--+--+
   etc.

If the compression bit is not set in the command structure, then the data are decompressed by the scanner driver if necessary. If the bit is set, however, then after processing the command one has to test whether the data are really compressed.

Fixed commands:

   100H: Scan
           The user dialog is called up, and if appropriate the scan
           process is performed
   101H: Continue
           In block mode this command continues the scan
   102H: Scan without dialog
           The scanning process is initiated without further user
           intervention; the same user-set parameters are used as
           for the previous scan
   103H: Next sheet
           The next sheet is fed in from the single-sheet tray
   104H: Prescan
           Executes a prescan with set parameters
   105H: Initialize scanner/driver
           Gets device description from scanner

Commands 102H and 103H are meant specially for automatic optical character recognition (OCR).

Since GDPS Version 1.10 new commands exist (as above, but instead of 10xH, 20xH is used). If these commands are used, one can work in a virtual mode provided virt_flag=1. Furthermore greyscale images with these commands are delivered as follows:

      0=black
    255=white

Warning: With 20xH commands the calling program must pass the extended command structure according to GDPS 1.10 !!!

Error-messages in return value:

   FFFEH: Block ready, further blocks follow
   FFFFH: Scan process (possibly for one colour) completed, last (or
          only) data block is present
      0H: Wait and have a cup of tea (if appropriate wait with an
          EVENT loop)
      1H: Unknown command
      2H: Scanner error
      3H: Abort by the user
      4H: Out of paper (only with single-sheet feed)
      5H: Out of memory: The user has set too large a paper format
      6H: Scanner not yet initialized

The error-messages are displayed to the user of the driver. They only serve as information for the calling program.

15.6.4.2 GDPS, virtual memory management

#define VOR              1
#define ZURUECK         -1
#define MITTE            0

/********************************************************************/
/*                        Scanner structures                        */
/********************************************************************/
typedef struct                   /* Passing structure for scanner   */
{
   void  *next;                  /* Pointer to the next driver      */
   char  ident[4];               /* Magic GDPS as driver identifier */
   int   version;                /* Version number, currently < 200 */
   int   type;                   /* Driver type, 0 for scanner      */
   char  *info;                  /* Pointer to driver info and to   */
   char  *copyright;             /* the Copyright message           */
   int   devdescr;               /* Device description flags        */
   int   colours;                /* Number of colours, 0=B/W        */
   int   deep;                   /* Possible bit-depths             */
   int   free;                   /* Flag whether scanner is free    */
   int   scommand;               /* Command to scanner              */
   void  *command;               /* Pointer to the command structure*/
} SCANHEADER;

typedef struct                 /* Command structure for GDPI scanners*/
{
   int   result;                 /* Result that reports drivers     */
   int   modes;                  /* Permitted scan modes            */
   int   depth;                  /* Image depth in bits/pixel       */
   void  *vmemory:               /* Where image should be placed    */
   long  vmaxlen;                /* Available memory                */
   int   bytewidth;              /* Width of a line in bytes        */
   int   height;                 /* Height of the image in lines    */
   int   mmwidth;                /* Width (!U)and(!u)                       */
   int   mmheight;               /* Height in 1/10 millimeters      */
   int   xdpi;                   /* Resolution in X (!U)and(!u)             */
   int   ydpi;                   /* Y direction                     */
   int   modulo;                 /* 2=>image becomes word-aligned   */
   int   start_x;                /* Top left corner X in 1/10 mm    */
   int   start_y;                /* Top left corner Y in 1/10 mm    */
   long  ser_no;                 /* Serial number                   */

/****
The following part is defined only for calls of the scanners with
the commands of the 0x2XX series
******/
   int     add_bits;              /* Specifies how many additional   */
                                  /* bits are required. For instance */
                                  /* CRANACH Studio has 2 masks for  */
                                  /* each image. If one scans a bit- */
                                  /* map, the program requires not   */
                                  /* just 1 bit per pixel, but three */
                                  /* bits. In a similar way, with a  */
                                  /* greyscale picture one has to    */
                                  /* calculate 8 + 2 = 10 pixels. If */
                                  /* this value is not allowed for,  */
                                  /* then it may happen that after   */
                                  /* scanning CRANACH Studio can not */
                                  /* open its window, because though */
                                  /* memory is available for the     */
                                  /* scanned image, there is none    */
                                  /* for the required masks.         */
                                  /* One needs, for exampple, two    */
                                  /* additional bits for the mask.   */
   void *Dchange_pointer;         /* Pointer to this function        */
   void *Dupdate;                 /* Pointer to that function        */
   int  read;                     /* Virtual read buffer index       */
   int  write;                    /* Virtual write buffer index      */
   int  virt_flag;                /* Flag whether working with       */
                                  /* virtual memory management(1=yes)*/
} SCANCOM;
SCANCOM scancom;

void *Dchange_pointer(
                        void *pointer,
                         int v_handle,
                         int richtung,
                         long *max_vor,
                         long *max_zurueck
                       );

void Dupdate(int v_handle);

All tms products work with virtual memory management. This means that it does not have to access memory areas that are present in RAM. The virtual memory management implemented in tms products was optimized for the requirements of images.

General procedure: The driver passes in scancom.write a virtual handle. With this handle one can then access the virtual data. The data are obtained automatically from the hard drive when required, and stored.

One possible application would be:

UCHAR *real;
long max_forward, max_back;

scancom->vmemory points to the free virtual memory. This is to be treated just like normal memory. so, say, free memory from addr 16MB to addr 50MB with scancom->vmemory = addr 30MB. As this address does not really exist, the pointer must be mapped to the real memory and the data loaded from the hard drive. This is achieved with the function Dchange_pointer.

 real = (UCHAR*)Dchange_pointer(
                                   scancom->vmemory,
                                   scancom->write,
                                   FORWARD,
                                   &max_forward,
                                   &max_back
                                 );

So real now points to an area of RAM.

scancom->write is the memory handle passed by the program.

FORWARD tells the memory management that we want to move as far as possible to the start of the memory. This optimizes disk accesses.

max_forward returns how many bytes from real one may move forward in the memory.

max_back returns how many bytes from real one may move backwards in the memory.

If these limits are reached then Dchange_pointer must be called anew. The minimum lengths for max_forward and max_back are:

  FORWARD BACK MID
max_forward: 32k 0k 16k
max_back: 0k 32k 16k

Der Ram Speicher ist in 6 Blöcke unterteilt, von denen jeder einen he RAM memory is subdivided into 6 blocks, which each can represent a separate or also an overlapping portion of the virtual memory. To guarantee with overlapping blocks that after a memory alteration all blocks will reproduce the current memory contents, call the function Dupdate(scancom->write). This updates the other blocks. However, one only needs to use Dupdate before accessing a different block. For a scanner driver that only uses the block scancom->write, it suffices therefore to call Dupdate(scancom->write) at the end of the scanning process.

Sample code for the deletion of 10 MB as of address 32 MB:

v_pointer=32MB
size=10MB

while(size>0)
{
        real=Dchange_pointer(
                              v_pointer,
                              scancom->write,
                              FORWARD,
                              &max_forward,
                              &max_back);
        if(max_forward<=size)
        {
                memset(real,0,max_forward);
                size-=max_forward;
                v_pointer+=max_forward;
        }
        else
        {
                memset(real,0,size);
                size=0;
                v_pointer+=size;
        }
}
Dupdate(scancom->write);  !!!!!!!!!!!!!



****************************************************************************
Old programs call the scanner with the command 0x100 (and not 0x200).
These programs (e.g. tms CRANACH) also do not yet use virtual memory
management. Therefore the SCANCOM structure is not defined from the
position marked onwards. The functions Dchange_pointer or Dupdate
should then be replaced in the driver by dummy functions.

/********************************************************************/

Possible application as ACC:

/********************************************************************/
/*    main()                                                        */
/*                                                                  */
/*    Heart of the program                                          */
/********************************************************************/

int main( void )
{
   int work_in[12],work_out[58],dummy;
   int buffer[20];

   appl_id = appl_init();

   /* Open an own workstation */
   handle=graf_handle(&dummy,&dummy,&dummy,&dummy);
   for ( dummy=0; dummy<10; work_in[dummy++]=1 );
   work_in[10]=2;
   v_opnvwk(work_in,&handle,work_out);

   if(!rsrc_load("SCANNER.RSC"))
   {
      form_alert(0,NO_RSC_FILE);
      goto FOREVER;
   }

   if( appl_id != -1 )
   {
      if( !_app )
      {
         scanner_moeglichkeiten();
         menu_id = menu_register( appl_id, "  SCANNER" );
         event_loop();   /* Here one waits for the call of the    */
                         /* driver by the program; the dialog is  */
                         /* handled, the image is scanned and     */
                         /* the values placed in vmemory          */
      }

   }
FOREVER: /* Initialization has not worked */
   while(1)
        evnt_mesag((int*)buffer);
}

/********************************************************************/
/* Initialize scanner                  */
/********************************************************************/

void scanner_moeglichkeiten()
{
        long
                **zeiger;
        long
                stack;

/********** Divert pointer to GDPS *******************************/

        stack=Super(0);

        zeiger = (long **)0x41c;

        if(*(*zeiger+1)== 0x47445053l)
                header.next = *zeiger;
        else
                header.next = NULL;

        *zeiger = (void *)&header;

        Super((void *)stack);

/********************************************************************/

        sprintf(header.ident,"GDPS");
        header.version          =100;
        header.type             =0;          /* Scanner */
        header.info             =info;
        header.copyright        =copyright;
        header.devdescr         =1 | 2 | 4;  /* Device description flags */
        header.colours          =1;          /* Number of colours, 0=B/W */
        header.deep             =1 | 16;     /* Possible bit-depths      */
        header.free             =0;
        header.scommand         =0;          /* Command to scanner       */
        header.command          =&command;

}

HomeProtocolsFont protocolLTL protocol