
/*******************************************************************
 $CRT 03 Jun 1996 : hb

 $AUT Holger Burkarth
 $DAT >>PortHd.c<<   15 Mar 1997    09:21:39 - (C) ProDAD
*******************************************************************/

//##ex mcpp:cppc -gs -o pos:pos/devs/PortHd.ddv p:pLib/DevCode.o p:/pOS_RKRM/pDos/PortHd.c -l pOSStub -l pOS -l CPPList

/***********************************************************
  pOS programing example - Copyright (C) 1995-97 proDAD

  This code was written as an easy to understand example,
  how to program pOS features. It is provided 'as-is',
  without any express or implied warranty.

  Permission is hereby granted to use, copy and modify
  this source code for any purpose, without fee, subject
  to the following conditions:

    (1) This notice may not be removed or altered from any
        source distribution.

    (2) Altered source versions must be plainly marked as
        such, and must not be misrepresented as being
        the original source code.

    (3) If only executable code is distributed, then the
        accompanying documentation have to state that
        "this software is based in part on examples of
        the pOS developer packet".

    (4) Permission for use of this code is granted only
        if the user accepts full responsibility for any
        undesirable consequences. proDAD accept NO LIABILITY
        for damages of any kind.

  ©proDAD
***********************************************************/

/*\
*** Mount:
***
*** TYPE_PICO DDNAME="devs:PortHd.ddv"
*** STARTUP="DEVICE=pserial.device UNIT=0 TYPE_SER"
***
\*/


#define __COMPUTER_AMIGA 1
#define NOMYDEBUG

#include "p:pExec/Types.h"
#include "p:pExec/Memory.h"
#include "p:pExec/Diagnos.h"
#include "p:pExec/TstTags.h"
#include "p:pDOS/ArgTags.h"
#include "p:pDOS/DosErrors.h"
#include "p:pDOS/DosDev.h"
#include "p:pDOS/DosBase.h"
#include "p:pDOS/Segment.h"
#include "p:pExec/Sema.h"
#include "p:pExec/Task.h"
#include "p:pDOS/Files.h"
#include "p:pDevice/Serial.h"
#include "p:pProto/pLibExt.h"
#include "p:pProto/pExec2.h"
#include "p:pProto/pDOS2.h"
#include "p:pProto/pList.h"

#ifdef _____ME_____
  #include "grund/inc_string.h"
  #include "grund/inc_limits.h"
#else
 #ifdef __cplusplus
 extern "C" {
 #endif
  #include <string.h>
  #include <limits.h>
 #ifdef __cplusplus
 }
 #endif
#endif


#define __WWW(a) ;
//#define __WWW(a) a;



#define	MAX(a,b)     ( (a)>(b)?(a):(b) )
#define	MIN(a,b)     ( (a)<(b)?(a):(b) )

struct pOS_ExecBase        *gb_ExecBase;
struct pOS_ExecLibFunction *gb_ExecLib;
struct pOS_DosBase         *gb_DosBase;



struct PortDosDevice
{
  pOS_DosDevice       pdd_Dev;
  pOS_Task           *pdd_Task;
  pOS_Semaphore       pdd_Sem;
  pOS_FileHandle      pdd_FH;
  pOS_MsgPort         pdd_UnitPort;
  pOS_MsgPort         pdd_ReadPort;
  pOS_MsgPort         pdd_WritePort;
  pOS_IOStdReq       *pdd_ReadIO;
  pOS_IOStdReq       *pdd_WriteIO;
  pOS_ExList          pdd_RdList;
  pOS_DosIOReq       *pdd_ActReadIO;

  UBYTE   *pdd_Buffer;
  size_t   pdd_BufSize;
  size_t   pdd_ActSize;
  UBYTE    pdd_FhIsOpen; /* Boolean */
  UBYTE    pdd_SerMode;  /* Boolean */

  CHAR     pdd_StdControlString[8];
};

/*\
*** Allgemeines:
*** In der pdd_RdList-Liste werden alle Read-IOs eingereiht, die nicht sofort
*** bearbeitet werden können. Der aktuelle ReadIO wird in pdd_ReadIO vermerkt.
*** Der pdd_ReadIO befindet sich in keiner Liste und kann somit sofort
*** ge-replyed werden. Wogegen der Abort eines pdd_RdList-IOs ein pOS_Remove
*** und dann ein pOS_ReplyMsg benötigt.
***
\*/

extern ULONG *FuncTable[];
extern BOOL pDevMain(_R_LB pOS_ExecBase*,_R_A0 PortDosDevice*);

const CHAR DeviceName[]  ="PortHandler.ddv";
const CHAR DeviceIDName[]="PortHandler.ddv 1.0 ("__DATE2__") (Created by Holger Burkarth)";
const pOS_TagItem DeviceDescribe[]=
{
  EXTSTTAG_MainOSID, pOS_MAINOSID,
  TAG_END
};



pOS_ResidentLibInit InitTable=
{
  sizeof(PortDosDevice),
  FuncTable,
  NULL,
  (BOOL(*)(_R_LB pOS_ExecBase*,_R_A0 pOS_Library*))pDevMain
};



BOOL OpenDev(PortDosDevice* dev);
VOID CloseDev(PortDosDevice* dev);




/*----------------------------------
-----------------------------------*/
BOOL pDevMain(_R_LB pOS_ExecBase* exec,_R_A0 PortDosDevice* dev)
{
  extern struct pOS_ExecBase        *gb_ExecBase;
  extern struct pOS_ExecLibFunction *gb_ExecLib;
  extern struct pOS_DosBase         *gb_DosBase;

  gb_ExecBase=(struct pOS_ExecBase*)exec;

/* Pre-pOS-Trick */
  gb_ExecLib=*((struct pOS_ExecLibFunction**)dev->pdd_Dev.ddv_Dev.lib_Segm->sel_Seg.seg_Reserved2);

  gb_DosBase =(pOS_DosBase*)pOS_OpenLibrary("pDos.library",0);

  return( OpenDev(dev) );
}


/*----------------------------------
-----------------------------------*/
pOS_SegmentLst* Expunge_func(_R_LB PortDosDevice* dev)
{
  if(dev->pdd_Dev.ddv_Dev.lib_OpenCnt==0) {
    pOS_SegmentLst* Seg=dev->pdd_Dev.ddv_Dev.lib_Segm;

    pOS_ListRemove(&dev->pdd_Dev.ddv_Dev.lib_Node);
    CloseDev(dev);
    pOS_CloseLibrary((struct pOS_Library*)gb_DosBase);
    _pOS_FreeLibraryMem2(&dev->pdd_Dev.ddv_Dev);
    return(Seg);
  }
  return(0);
}


/*----------------------------------
-----------------------------------*/
ULONG Reserved_func(_R_LB PortDosDevice*)
{  return(0); }


/*----------------------------------
-----------------------------------*/
VOID AbortIO_func(_R_LB PortDosDevice* dev,_R_A0 pOS_DosIOReq* io)
{
/*\
*** Ein IO kann jederzeit abgebrochen werden. Damit kein 'Speichersalat'
*** durch mehrere Tasks in (PortDosDevice) entsteht, wird eine Semaphore
*** über (PortDosDevice) aufgespannt.
*** Der Abort ist eine sensible Funktion und wird leider sehr oft fehlerhaft
*** implementiert.
*** Zuerst wird geprüft, ob unser 'io' in der aktuelle ReadIO ist. In diesem
*** Fall können wird den 'io' als aborted kennzeichnen und zurücksenden (reply).
*** Obwohl der Reply-Port unser Port ist, müssen wir pOS_ReplyMsg verwenden,
*** nur so wird das Standard-Device-Verhalten aufrechterhalten.
*** Damit das Device weiterarbeiten kann, legen wir den nächsten ReadIO aus
*** der Warteschlange nach. Ist kein IO in der Warteliste, dann liefert
*** pOS_ListRemHead null und pdd_ActReadIO wird autom. genullt.
*** War unser 'io' nicht der aktuelle ReadIO, so existieren 3 mögliche Zustände.
*** 1. Der 'io' wurde bereits als vollendet oder ReadWrite-Error an den ReplyPort
*** zurückgesendet. Da nach ReplyMsg der ln_Type immer auf NTYP_REPLYMSG steht
*** kann dies einfach ermittelt werden. Aber Vorsicht, die Abfrage darf auf
*** keinem Fall ln_Type!=NTYP_REPLYMSG lauten, da ln_Type noch weitere Zustände
*** wiedergibt. Es ist aber sicher ln_Type==NTYP_MESSAGE zu prüfen, nach einem
*** pOS_PutMsg ist ln_Type immer auf NTYP_MESSAGE gesetzt.
*** 2. Der 'io' befindet sich noch im pdd_UnitPort und hatte noch keinen Kontakt
*** mit dem Handler-Task.
*** 3. Der 'io' wurde vom Handler in die pdd_RdList-Warteschlange eingereiht.
*** Fall 2 und 3 sind für uns als Abort-er gleichbedeutend. Der 'io' befindet
*** sich in einer Liste und muß von uns erst mittels pOS_ListRemove aus der
*** selbigen entfernt werden, denn ein Node kann zur gleich Zeit nur einer
*** Liste angehören.
\*/

__WWW( kprintf("AbortIO_func io=0x%lx\n",io) );

  pOS_ObtainSemaphore(&dev->pdd_Sem);
  if(io==dev->pdd_ActReadIO) {
    io->dr_Error2=DOSERR_Aborted;
    pOS_ReplyMsg(&io->dr_Message);
    dev->pdd_ActReadIO=(pOS_DosIOReq*)pOS_ListRemHead(&dev->pdd_RdList);
  }
  else if(io->dr_Message.mn_Node.ln_Type==NTYP_MESSAGE) {
    pOS_ListRemove(&io->dr_Message.mn_Node);
    io->dr_Error2=DOSERR_Aborted;
    pOS_ReplyMsg(&io->dr_Message);
  }
  pOS_ReleaseSemaphore(&dev->pdd_Sem);
}

/*----------------------------------
-----------------------------------*/
VOID BeginIO_func(_R_LB PortDosDevice* dev,_R_A0 pOS_DosIOReq* io)
{
/*\
*** Der Handler-Task soll den 'io' bearbeiten. Wir reichen den 'io' nur weiter.
\*/
  io->dr_Error2=DOSERR_None;
  io->dr_Flags &= ~IOREQF_Quick;
__WWW( kprintf("BeginIO_func io=0x%lx\n",io) );
  pOS_PutMsg(&dev->pdd_UnitPort,&io->dr_Message);
}






ULONG *FuncTable[]=
{
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Expunge_func,
// ------------
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
// ------------
 (ULONG*)BeginIO_func,
 (ULONG*)AbortIO_func,
// ------------
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
 (ULONG*)Reserved_func,
// ------------

 (ULONG*)ULONG_MAX
};

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

/*----------------------------------
-----------------------------------*/
VOID CloseIODev(PortDosDevice* Dev,BOOL DevIsOpen)
{
  if(DevIsOpen) {
/*\
*** Damit ein noch laufender Ser-IO uns nicht 'abschießt', wird sichergestellt,
*** daß alle IOs ihre Tätigkeit beendet haben.
*** Die pOS_Funktion pOS_CheckIO prüft, ob der IO bereits fertig ist. Läuft
*** der IO noch, wir er mittels AbortIO abgebrochen. Um alle Probleme im
*** Keim zu ersticken folgt pOS_WaitIO.
\*/
    if(!pOS_CheckIO((pOS_IORequest*)Dev->pdd_ReadIO))
        pOS_AbortIO((pOS_IORequest*)Dev->pdd_ReadIO);
    if(!pOS_CheckIO((pOS_IORequest*)Dev->pdd_WriteIO))
        pOS_AbortIO((pOS_IORequest*)Dev->pdd_WriteIO);

    pOS_WaitIO((pOS_IORequest*)Dev->pdd_ReadIO);
    pOS_WaitIO((pOS_IORequest*)Dev->pdd_WriteIO);
    pOS_CloseDevice((pOS_IORequest*)Dev->pdd_ReadIO);
  }
  if(Dev->pdd_ReadIO)  pOS_DeleteIORequest((pOS_IORequest*)Dev->pdd_ReadIO);
  if(Dev->pdd_WriteIO) pOS_DeleteIORequest((pOS_IORequest*)Dev->pdd_WriteIO);
  if(Dev->pdd_Buffer)  pOS_FreeMem(Dev->pdd_Buffer,Dev->pdd_BufSize);
  Dev->pdd_ReadIO= Dev->pdd_WriteIO= NULL;
  Dev->pdd_Buffer=NULL;
}

/*----------------------------------
-----------------------------------*/
BOOL OpenIODev(PortDosDevice* Dev)
{
  pOS_DosArgs* Args;
  const pOS_DosMountDevice *const MD=Dev->pdd_Dev.ddv_U.ddv_Handler.ddvh_Mount;
  ULONG Ops[5]={0,0,0,1024,(ULONG)"9600/8N1N"};
  BOOL DevIsOpen=FALSE;

__WWW( kprintf("OpenIODev\n") );

/*\
*** Mount-Startup mittels der ReadArgs parsen. pOS_ReadDosArgs benötigt nicht
*** unbedingt einen pOS_Process.
*** Damit kein Speicherplatz unnötig verschwendet wird, werden die Args sofort
*** wieder freigegeben. Beachte, daß nach pOS_DeleteDosArgs alle eventuellen
*** Strings freigegeben werden.
\*/
  if(Args=pOS_ReadDosArgs(
// 0          1        2            3          4
"DEVICE/A, UNIT/N, TYPE_SER/S, BUFFERS/N/K, CONTROL/K",
  Ops,sizeof(Ops)/sizeof(ULONG),
  ARGTAG_Arguments, (ULONG)MD->dmd_U.dmd_Pico.dmpic_Startup,
  TAG_DONE))
  {
    Dev->pdd_BufSize=*((ULONG*)Ops[3]);
    if(Dev->pdd_BufSize<512) Dev->pdd_BufSize=512;
    Dev->pdd_Buffer=(UBYTE*)pOS_AllocMem(Dev->pdd_BufSize,MEMF_PUBLIC);

    strncpy(Dev->pdd_StdControlString,(CHAR*)Ops[4],sizeof(Dev->pdd_StdControlString)-1);

    if(Dev->pdd_Buffer && Ops[2]) {
      Dev->pdd_SerMode=TRUE;
      Dev->pdd_ReadIO=(pOS_IOStdReq*)
          pOS_CreateIORequest(&Dev->pdd_ReadPort,sizeof(pOS_SerialIO));
      Dev->pdd_WriteIO=(pOS_IOStdReq*)
          pOS_CreateIORequest(&Dev->pdd_WritePort,sizeof(pOS_SerialIO));
    }

    if(Dev->pdd_ReadIO && Dev->pdd_WriteIO) {
      pOS_OpenDevice((CHAR*)Ops[0],*((ULONG*)Ops[1]),(pOS_IORequest*)Dev->pdd_ReadIO,0,0);
      if(Dev->pdd_ReadIO->io_Error==0) {
        *Dev->pdd_WriteIO = *Dev->pdd_ReadIO;
        Dev->pdd_WriteIO->io_Message.mn_ReplyPort=&Dev->pdd_WritePort;
        DevIsOpen=TRUE;
__WWW( kprintf("OpenIODev ok\n") );
      }
      else {
__WWW( kprintf("OpenIODev Device-Error %ld\n",Dev->pdd_ReadIO->io_Error) );
      }
    }
    pOS_DeleteDosArgs(Args);  /* Args freigeben */
  }
  else {
__WWW( kprintf("OpenIODev Args-Error |%s|\n",MD->dmd_U.dmd_Pico.dmpic_Startup) );
  }
  return(DevIsOpen);
}





/*----------------------------------
-----------------------------------*/
BOOL SetParam(PortDosDevice* Dev,const CHAR* name)
{
  pOS_DosArgs* Args;
  ULONG Ops[2]={9600,(ULONG)"8N1N"};
  BOOL Result=FALSE;

  if(Dev->pdd_SerMode) {
    if(Args=pOS_ReadDosArgs(
// 0      1
"BAUD/N, MODE",
    Ops,sizeof(Ops)/sizeof(ULONG),
    ARGTAG_Arguments, (ULONG) (name ? name:Dev->pdd_StdControlString),
    ARGTAG_Seperator, (ULONG)'/',
    TAG_DONE))
    {
      pOS_SerialIO *const IO=(pOS_SerialIO*)Dev->pdd_ReadIO;
      const CHAR *const Control=(CHAR*)Ops[1];
      const size_t ControlLen=strlen(Control);

      IO->sio_Baud = *((ULONG*)Ops[0]);


__WWW( kprintf("SetParam-SER Baud=%ld Control=|%s|\n",IO->sio_Baud,Control) );


      if(Control[0]=='7') IO->sio_ReadLen = 7;
      else                IO->sio_ReadLen = 8;

      switch(ControlLen>1 ? Control[1]:' ') {
        case  'O':
        case  'o':
          IO->sio_SerFlags |= SERF_XDisabled | SERF_PartyOdd | SERF_PartyOn;
          break;

        case  'E':
        case  'e':
          IO->sio_SerFlags |= SERF_XDisabled | SERF_PartyOn;
          IO->sio_SerFlags &= ~SERF_PartyOdd;
          break;

        default:
          IO->sio_SerFlags |= SERF_XDisabled;
          IO->sio_SerFlags &= ~(SERF_PartyOn | SERF_PartyOdd);
      }

      if(ControlLen>2 && Control[2]=='2')
           IO->sio_StopBits = 2;
      else IO->sio_StopBits = 1;


      switch(ControlLen>3 ? Control[3]:' ') {
        case  'X':
        case  'x':
          IO->sio_SerFlags &= ~(SERF_XDisabled | SERF_7Wire);
          break;

        case  'R':
        case  'r':
          IO->sio_SerFlags |= (SERF_XDisabled | SERF_7Wire);
          break;

        default:
          IO->sio_SerFlags |= SERF_XDisabled;
          IO->sio_SerFlags &= ~SERF_7Wire;
      }


      IO->sio_WriteLen=IO->sio_ReadLen;

      IO->sio_Command=SERCMD_SetParams;
      if(0==pOS_DoIO((pOS_IORequest*)IO)) {
        Result=TRUE;
      }

      pOS_DeleteDosArgs(Args);  /* Args freigeben */
    }
  }
  else Result=TRUE;
  return(Result);
}





/*----------------------------------
-----------------------------------*/
VOID AbortAllPendingIOs(PortDosDevice* Dev)
{
  pOS_DosIOReq *IO;

__WWW( kprintf("AbortAllPendingIOs\n") );

  while(IO=(pOS_DosIOReq*)pOS_ListRemHead(&Dev->pdd_RdList)) {
    IO->dr_Error2=DOSERR_Aborted;
    pOS_ReplyMsg(&IO->dr_Message);
  }
  if(IO=Dev->pdd_ActReadIO) {
    IO->dr_Error2=DOSERR_Aborted;
    pOS_ReplyMsg(&IO->dr_Message);
  }
}



/*----------------------------------
-----------------------------------*/
VOID HandlerFunc(VOID)
{
  pOS_Task *const ThisTask=pOS_FindTask(NULL);
  PortDosDevice *const Dev=(PortDosDevice*)ThisTask->tc_UserData[0];
  BOOL DevIsOpen=FALSE;
  UBYTE ReadChar;

__WWW( kprintf("HandlerFunc start\n") );

  pOS_ConstructMsgPort(&Dev->pdd_UnitPort);
  pOS_ConstructMsgPort(&Dev->pdd_ReadPort);
  pOS_ConstructMsgPort(&Dev->pdd_WritePort);
  pOS_ListInit(&Dev->pdd_RdList);

/*\
*** Standard-FileHandle vorbereiten. Da der Port-Handler immer nur ein File
*** zur gleichen Zeit zuläßt, muß die FH nicht dynamisch Verwaltet werden.
\*/
  pOS_ConstructDosFH(&Dev->pdd_FH,&Dev->pdd_Dev,0);
  Dev->pdd_FH.fh_Flags = FILEHDF_Interactive;

  if(1 /*OpenIODev(Dev)*/) {
    const ULONG WaitSig=   (1<<Dev->pdd_UnitPort.mp_SigBit)
                         | (1<<Dev->pdd_ReadPort.mp_SigBit) ;

    pOS_SendSignal(ThisTask->tc_FriendTask,SIGF_Signal); // *** OK
    ThisTask->tc_FriendTask=NULL;

    while(ThisTask->tc_FriendTask==NULL) {
      ULONG Sig;
      Sig=pOS_WaitSignal(WaitSig);

/*\
*** Der Zugriff auf (PortDosDevice) wird durch die pdd_Sem-Semaphore geschützt.
*** Damit der Semaphore-Mechanismus nicht unnötige Systemzeit verschlingt, erfolgt
*** der Lock/Unlock nur in der äußersten Schleife. Der Schutz ist nötig, da die
*** Abort-Funktion ebenfalls auf die (PortDosDevice) zugreift.
\*/
      pOS_ObtainSemaphore(&Dev->pdd_Sem);


/*\
*** IO-Read ist vollendet.
\*/
      if(Sig & (1<<Dev->pdd_ReadPort.mp_SigBit)) {
__WWW( kprintf("HandlerFunc ReadPort\n") );
        if(pOS_GetMsg(&Dev->pdd_ReadPort)) { /* WICHTIG, ReplyPort wird geleert */
          if(Dev->pdd_ReadIO->io_Error==0) {

#define t_RD Dev->pdd_ActReadIO->dr_U.dr_ReadFH

/*\
*** Befindet sich ein ReadIO in der Bearbeitung, dann werden alle hereinkommenden
*** Daten sofort dem IO zugewiesen. Ist der aktuelle IO fertigt, wird er Replyed
*** und der nächste in der Warteschlange wird nachgelegt und wird so zum aktuellen.
*** Gibt es keinen aktuellen ReadIO, dann werden alle Daten in den Lesepuffer
*** geschrieben.
\*/
            if(Dev->pdd_ActReadIO) {
              ((UBYTE*)t_RD.drrd_Adr)[t_RD.drrd_ResSize]=ReadChar;
              ++t_RD.drrd_ResSize;
              for(;;) {
                /**if(t_RD.drrd_ResSize == t_RD.drrd_Size) **/
                if(t_RD.drrd_ResSize != 0)
                {
                  pOS_ReplyMsg(&Dev->pdd_ActReadIO->dr_Message);
                  Dev->pdd_ActReadIO=(pOS_DosIOReq*)pOS_ListRemHead(&Dev->pdd_RdList);
                }
                else if(Dev->pdd_SerMode) {
                  Dev->pdd_ReadIO->io_Command=SERCMD_Query;
                  pOS_DoIO((pOS_IORequest*)Dev->pdd_ReadIO);
                  if(Dev->pdd_ReadIO->io_Actual>0) {
                    size_t Size;
                    Size=t_RD.drrd_Size-t_RD.drrd_ResSize;
                    Size=MIN(Size,Dev->pdd_ReadIO->io_Actual);
                    Dev->pdd_ReadIO->io_Command=CMD_READ;
                    Dev->pdd_ReadIO->io_Length=Size;
                    Dev->pdd_ReadIO->io_Data=(APTR) ((ULONG)t_RD.drrd_Adr + t_RD.drrd_ResSize);
                    pOS_DoIO((pOS_IORequest*)Dev->pdd_ReadIO);
                    t_RD.drrd_ResSize+=Size;
                    continue;
                  }
                }
                break;
              }
            }
            else if(Dev->pdd_ActSize<Dev->pdd_BufSize) {
              Dev->pdd_Buffer[ Dev->pdd_ActSize++ ] = ReadChar;
            }
#undef t_RD
          }

/*\
*** Lesefehler vom Ser-Device. Der aktuelle ReadIO wird mit gesetztem Error
*** abgebrochen.
\*/
          else {
__WWW( kprintf("Read-Error %ld\n",Dev->pdd_ReadIO->io_Error) );

            if(Dev->pdd_ActReadIO) {
              if(Dev->pdd_ReadIO->io_Error==SERIOERR_DetectedBreak) {
                Dev->pdd_ActReadIO->dr_Error2=0;
                Dev->pdd_ActReadIO->dr_U.dr_ReadFH.drrd_ResSize=0;
              }
              else {
                Dev->pdd_ActReadIO->dr_Error2=DOSERR_ReadWriteError;
              }
              pOS_ReplyMsg(&Dev->pdd_ActReadIO->dr_Message);
              Dev->pdd_ActReadIO=(pOS_DosIOReq*)pOS_ListRemHead(&Dev->pdd_RdList);
            }
          }

/*\
*** Ser-Device wieder starten, damit das nächste Byte gelesen werden kann.
\*/
          Dev->pdd_ReadIO->io_Command=CMD_READ;
          Dev->pdd_ReadIO->io_Length=1;
          Dev->pdd_ReadIO->io_Data=(APTR)&ReadChar;
          pOS_SendIO((pOS_IORequest*)Dev->pdd_ReadIO);
        }
      }

/*\
*** Von Dos kommende Msg.
\*/
      if(Sig & (1<<Dev->pdd_UnitPort.mp_SigBit)) {
        pOS_DosIOReq *IO;

__WWW( kprintf("HandlerFunc UnitPort\n") );

        while(IO=(pOS_DosIOReq*)pOS_GetMsg(&Dev->pdd_UnitPort)) {
          switch(IO->dr_Command) {

/*\
*** Ein Anwenderprogramm hat pOS_OpenFile aufgerufen. Der Port-Handler unterstützt
*** nur ein FH zur gleichen Zeit und frägt dies mittels pdd_FhIsOpen ab.
*** Damit kein falscher Dateipfad entstehen kann, werden nur absolute Pfade
*** (IO->dr_U.dr_OpenFH.drop_CurrLock==NULL) zugelassen.
*** [absoluter Pfad => a:xxx oder con: oder par:]
*** Konnten alle Resourcen alloziert werden, wird das Ser-Device gestartet.
\*/
            case DOSCMD_OpenFH:
__WWW( kprintf(
"DOSCMD_OpenFH<%lx> \n"
" pdd_FhIsOpen = %ld\n"
" drop_Name     =0x%lx\n"
" drop_CurrDir  =0x%lx\n"
,IO,Dev->pdd_FhIsOpen,IO->dr_U.dr_OpenFH.drop_Name
,IO->dr_U.dr_OpenFH.drop_CurrLock) );

              if(Dev->pdd_FhIsOpen==0) {
                if(   IO->dr_U.dr_OpenFH.drop_Name!=NULL
                   && IO->dr_U.dr_OpenFH.drop_CurrLock==NULL)
                {
                  IO->dr_U.dr_OpenFH.drop_ResFH=&Dev->pdd_FH;
                  Dev->pdd_FH.fh_Size=0; Dev->pdd_FH.fh_Pos=0;
                  Dev->pdd_FH.fh_Access=IO->dr_U.dr_OpenFH.drop_Mode & FILEHDMOD_AcMsk;
                  if(    (DevIsOpen=OpenIODev(Dev))
                      && SetParam(Dev,IO->dr_U.dr_OpenFH.drop_Name))
                  {
                    Dev->pdd_ActSize=0;
                    Dev->pdd_ReadIO->io_Command=CMD_READ;
                    Dev->pdd_ReadIO->io_Data=(APTR)&ReadChar;
                    Dev->pdd_ReadIO->io_Length=1;
__WWW( kprintf("Send-ReadIO\n") );
                    pOS_SendIO((pOS_IORequest*)Dev->pdd_ReadIO);

                    Dev->pdd_FhIsOpen=1;
                  }
                  else {
                    CloseIODev(Dev,FALSE);
                    IO->dr_Error2=DOSERR_ObjectNotFound;
                  }
                }
                else IO->dr_Error2=DOSERR_WrongObjectType;
              }
              else IO->dr_Error2=DOSERR_ObjectExists;
              break;


/*\
*** Das Anwenderprogramm ruft pOS_CloseFile() auf.
*** Alle noch ausstehende IOs (Read-Requests) werden abgebrochen.
*** Eigentlich dürften bei Close nie IOs ausstehen, dennoch wird hier
*** dieser Fall als Anschauungsbeispiel verwendet.
\*/
            case DOSCMD_CloseFH:
__WWW( kprintf("DOSCMD_CloseFH pdd_FhIsOpen=%ld\n",Dev->pdd_FhIsOpen) );

              if(Dev->pdd_FhIsOpen) {
                Dev->pdd_FhIsOpen=0;
                AbortAllPendingIOs(Dev);
                CloseIODev(Dev,DevIsOpen); DevIsOpen=FALSE;
              }
              else IO->dr_Error2=DOSERR_ObjectNotFound;
              break;


/*\
*** Das Anwenderprogramm ruft pOS_ReadFile() auf.
*** Befindet sich bereits ein IO in der Bearbeitung, so wird der neue IO
*** in die Warteschlange eingereiht.
*** Ist kein ReadIO in der Bearbeitung, dann wird zuerst ein eventueller
*** Datenstrom, der in der Zwischenzeit von pOS_OpenFile() bis pOS_ReadFile()
*** eingegangen ist, dem IO zugeordnet. Konnte der IO vollständig bedient werden,
*** wird er sofort ge-replyed. Ansonsten wird der IO zum aktuellen.
\*/
            case DOSCMD_ReadFH:
__WWW( kprintf(
"DOSCMD_ReadFH\n"
" drrd_Size     = %ld\n"
" pdd_ActReadIO = 0x%lx\n"
" pdd_ActSize   = %ld\n"
,IO->dr_U.dr_ReadFH.drrd_Size,Dev->pdd_ActReadIO,Dev->pdd_ActSize) );

              if(Dev->pdd_ActReadIO) {
                IO->dr_U.dr_ReadFH.drrd_ResSize=0;
                pOS_ListAddTail(&Dev->pdd_RdList,&IO->dr_Message.mn_Node);
                IO=NULL;
              }
              else {
#define t_RD IO->dr_U.dr_ReadFH
                IO->dr_U.dr_ReadFH.drrd_ResSize=
                         MIN(Dev->pdd_ActSize,IO->dr_U.dr_ReadFH.drrd_Size);
                memcpy(t_RD.drrd_Adr,Dev->pdd_Buffer,IO->dr_U.dr_ReadFH.drrd_ResSize);
                if(Dev->pdd_ActSize>IO->dr_U.dr_ReadFH.drrd_ResSize) {
                  memmove(Dev->pdd_Buffer,
                          &Dev->pdd_Buffer[IO->dr_U.dr_ReadFH.drrd_ResSize],
                          Dev->pdd_ActSize-IO->dr_U.dr_ReadFH.drrd_ResSize);
                }
                Dev->pdd_ActSize-=IO->dr_U.dr_ReadFH.drrd_ResSize;

                /** if(IO->dr_U.dr_ReadFH.drrd_ResSize<IO->dr_U.dr_ReadFH.drrd_Size) **/

                if(IO->dr_U.dr_ReadFH.drrd_ResSize==0)
                {
                  Dev->pdd_ActReadIO=IO;
                  IO=NULL;
                }
#undef t_RD
              }
              break;

/*\
*** Das Anwenderprogramm ruft pOS_WriteFile() auf.
*** Da der Port-Handler nur eine FileHandle unterstützt, darf der Write
*** synchron erfolgen. Damit kein Konflikt mit einem parallel laufendem Read
*** erfolgt, kommt beim Write ein eigener Ser-IO zum Einsatz.
\*/
            case DOSCMD_WriteFH:
__WWW( kprintf(
"DOSCMD_WriteFH\n"
" drwd_Size = %ld\n"
,IO->dr_U.dr_WriteFH.drwd_Size) );

              Dev->pdd_WriteIO->io_Command=CMD_WRITE;
              Dev->pdd_WriteIO->io_Data=(APTR)IO->dr_U.dr_WriteFH.drwd_Adr;
              Dev->pdd_WriteIO->io_Length=IO->dr_U.dr_WriteFH.drwd_Size;
              pOS_DoIO((pOS_IORequest*)Dev->pdd_WriteIO);
              IO->dr_U.dr_WriteFH.drwd_ResSize=Dev->pdd_WriteIO->io_Actual;
              if(Dev->pdd_WriteIO->io_Error)
                   IO->dr_Error2=DOSERR_ReadWriteError;
              break;


            default: IO->dr_Error2=DOSERR_NotImplemented;
__WWW( kprintf("UnknwonDos %ld\n",IO->dr_Command));
          }

          if(IO) pOS_ReplyMsg(&IO->dr_Message);
        }
      }

      pOS_ReleaseSemaphore(&Dev->pdd_Sem);
    }
  }
  else {
__WWW( kprintf("HandlerFunc error-1\n") );
  }

  CloseIODev(Dev,DevIsOpen);
  pOS_DestructDosFH(&Dev->pdd_FH);

/*\
*** Sämtliche MsgPorts, die mit pOS_ConstructMsgPort erzeugt wurden, müssen
*** nicht freigeben werden, da sie keinen Speicherplatz belegen und das mp_SigBit
*** wird autom. beim return() bzw. DeleteTask() freigegeben.
\*/

__WWW( kprintf("HandlerFunc end\n") );

  Dev->pdd_Task=NULL;
  pOS_DeleteTask(ThisTask,ThisTask->tc_FriendTask,SIGF_Signal);
}





/*----------------------------------
-----------------------------------*/
BOOL OpenDev(PortDosDevice* dev)
{
  const pOS_DosMountDevice *const MD=dev->pdd_Dev.ddv_U.ddv_Handler.ddvh_Mount;

  if(MD->dmd_Type==DMDTYP_Pico && MD->dmd_U.dmd_Pico.dmpic_Startup) {
    pOS_InitSemaphore(&dev->pdd_Sem);
    pOS_SetSignal(0,SIGF_Signal); // *** Signal löschen
    dev->pdd_Task=pOS_CreateTask(MD->dmd_Node.ln_Name,5,
                            (APTR)HandlerFunc,4000,sizeof(pOS_Task),(ULONG)dev,NULL);
    if(dev->pdd_Task) pOS_WaitSignal(SIGF_Signal);
  }
  return(dev->pdd_Task!=NULL);
}


/*----------------------------------
-----------------------------------*/
VOID CloseDev(PortDosDevice* dev)
{
  if(dev->pdd_Task) {
/*\
*** Der Port-Handler verwendet keine Volumen, dennoch wird hier aufgezeigt,
*** wie am Ende ein Volume gelöscht werden muß.
\*/
    if(dev->pdd_Dev.ddv_U.ddv_Handler.ddvh_ActVolume) {
      pOS_CloseDOSVolume(dev->pdd_Dev.ddv_U.ddv_Handler.ddvh_ActVolume);
      dev->pdd_Dev.ddv_U.ddv_Handler.ddvh_ActVolume=NULL;
    }

/*\
*** Dem Handler-Task wird die Ausstiegsmarke tc_FriendTask gesetzt und
*** damit der Handler sofort aggieren kann, wird er durch ein Signal
*** aufgeweckt. Um Fehler auszuschließen warten wir, bis der Handler seine
*** Tätigkeit beendet hat.
\*/
    dev->pdd_Task->tc_FriendTask=pOS_FindTask(NULL);
    pOS_SendSignal(dev->pdd_Task,1<<dev->pdd_UnitPort.mp_SigBit);
    while(dev->pdd_Task) pOS_WaitSignal(SIGF_Signal);
  }
}


