/* Copyright 1989 - 1995 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

/* Forward declarations in DeviceMgr.h (DO NOT DELETE THIS LINE) */

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_device[] =
		"$Id: device.c 63 2004-12-24 18:19:43Z ctm $";
#endif

#include "rsys/common.h"
#include "DeviceMgr.h"
#include "FileMgr.h"
#include "MemoryMgr.h"
#include "OSUtil.h"
#include "ResourceMgr.h"
#include "MenuMgr.h"
#include "ToolboxEvent.h"
#include "rsys/glue.h"
#include "rsys/mman.h"
#include "rsys/device.h"
#include "rsys/file.h"
#include "rsys/serial.h"

/*
 * NOTE:  The device manager now executes "native code" and code read
 *	  in from resources.  The latter is "RAM" based, the former is
 *	  our version of "ROM" based.  We use the same bit that the Mac
 *	  uses to distinguish between the two, but our implementation of
 *	  ROM based is incompatible with theirs.  NOTE: even in our
 *	  incompatible ROM based routines, we still byte swap the pointers
 *	  that get us to the routines.
 */

A4(PUBLIC, OSErr, ROMlib_dispatch, ParmBlkPtr, p,		/* INTERNAL */
		    BOOLEAN, async, DriverRoutineType, routine, INTEGER, trapn)
{
    int devicen;
    ramdriverhand ramdh;
    DCtlHandle h;
    OSErr retval;
    typedef OSErr (*devfp_t)(ParmBlkPtr, DCtlPtr);
    devfp_t procp;

    devicen = -CW(p->cntrlParam.ioCRefNum) - 1;
    if (devicen < 0 || devicen >= NDEVICES)
	retval = badUnitErr;
    else if (UTableBase == (DCtlHandlePtr) (long) CLC(0xFFFFFFFF) ||
		(h = MR(MR(UTableBase)[devicen].p)) == 0 || (*h).p == 0)
	retval =  unitEmptyErr;
    else {
	HLock((Handle) h);
	p->ioParam.ioTrap = CW(trapn);
	if (async)
	    p->ioParam.ioTrap |= CWC(asyncTrpBit);
	else
	    p->ioParam.ioTrap |= CWC(noQueueBit);
	if (!(HxX(h, dCtlFlags) & CWC(RAMBASEDBIT))) {
	    switch (routine) {
	    case Open:
		retval = (* (devfp_t) MR(HxP(h, dCtlDriver)->udrvrOpen)) 
							       (p, STARH(h));
		break;
	    case Prime:
		retval = (* (devfp_t) MR(HxP(h, dCtlDriver)->udrvrPrime))
							       (p, STARH(h));
		break;
	    case Ctl:
		retval = (* (devfp_t) MR(HxP(h, dCtlDriver)->udrvrCtl))
							       (p, STARH(h));
		break;
	    case Stat:
		retval = (* (devfp_t) MR(HxP(h, dCtlDriver)->udrvrStatus))
							       (p, STARH(h));
		break;
	    case Close:
		retval = (* (devfp_t) MR(HxP(h, dCtlDriver)->udrvrClose))
							       (p, STARH(h));
		break;
	    default:
		retval = fsDSIntErr;
		break;
	    }
	} else {
	    ramdh = (ramdriverhand) HxP(h, dCtlDriver);
	    LoadResource((Handle) ramdh);
	    HLock((Handle) ramdh);
	    switch (routine) {
	    case Open:
		procp = (devfp_t) STARH(ramdh) + Hx(ramdh, drvrOpen);
		break;
	    case Prime:
		procp = (devfp_t) STARH(ramdh) + Hx(ramdh, drvrPrime);
		break;
	    case Ctl:
		procp = (devfp_t) STARH(ramdh) + Hx(ramdh, drvrCtl);
		break;
	    case Stat:
		procp = (devfp_t) STARH(ramdh) + Hx(ramdh, drvrStatus);
		break;
	    case Close:
		procp = (devfp_t) STARH(ramdh) + Hx(ramdh, drvrClose);
		break;
	    default:
		procp = 0;
		break;
	    }
	    if (procp) {
	      LONGINT saved1, saved2, saved3, saved4, saved5, saved6, saved7,
	      savea2, savea3, savea4, savea5, savea6;

		savea2 = EM_A2;
		EM_A0 = (LONGINT) (long) US_TO_SYN68K(p);
		EM_A1 = (LONGINT) (long) US_TO_SYN68K(STARH(h));
		EM_A2 = (LONGINT) (long) US_TO_SYN68K(procp);	/* for compatibility with above */
		saved1 = EM_D1;
		saved2 = EM_D2;
		saved3 = EM_D3;
		saved4 = EM_D4;
		saved5 = EM_D5;
		saved6 = EM_D6;
		saved7 = EM_D7;
		savea3 = EM_A3;
		savea4 = EM_A4;
		savea5 = EM_A5;
		savea6 = EM_A6;
		CALL_EMULATOR((syn68k_addr_t) (long) US_TO_SYN68K (procp));
		EM_D1 = saved1;
		EM_D2 = saved2;
		EM_D3 = saved3;
		EM_D4 = saved4;
		EM_D5 = saved5;
		EM_D6 = saved6;
		EM_D7 = saved7;
		EM_A3 = savea3;
		EM_A4 = savea4;
		EM_A5 = savea5;
		EM_A6 = savea6;
		retval = EM_D0;
		EM_A2 = savea2;
	    } else
		retval = fsDSIntErr;
	    if (routine == Open)
		HxX(h, dCtlFlags) |= CWC(DRIVEROPENBIT);
	    else if (routine == Close) {
		HxX(h, dCtlFlags) &= ~CWC(DRIVEROPENBIT);
		HUnlock((Handle) h);
		HUnlock((Handle) ramdh);
		MBarEnable = 0;
		/* NOTE: It's not clear whether we should zero out this
		   field or just check for DRIVEROPEN bit up above and never
		   send messages except open to non-open drivers.  */
		MR(UTableBase)[devicen].p = CLC(0);
	    }

	    if (routine < Close)
		retval = CW(p->ioParam.ioResult);	/* see II-193 */
	}
    }
    fs_err_hook (retval);
    return retval;
}

/* PBOpen, PBClose, PBRead and PBWrite are part of the file manager */

A2(PUBLIC, OSErr, PBControl, ParmBlkPtr, pbp, BOOLEAN, a)	/* IMII-186 */
{
  OSErr err;

  err = ROMlib_dispatch(pbp, a, Ctl, 0);
  fs_err_hook (err);
  return err;
}

A2(PUBLIC, OSErr, PBStatus, ParmBlkPtr, pbp, BOOLEAN, a)	/* IMII-186 */
{
  OSErr err;

  err = ROMlib_dispatch(pbp, a, Stat, 0);
  fs_err_hook (err);
  return err;
}

A2(PUBLIC, OSErr, PBKillIO, ParmBlkPtr, pbp, BOOLEAN, a)	/* IMII-187 */
{
  OSErr err;

  pbp->cntrlParam.csCode = CWC(killCode);
  err = ROMlib_dispatch(pbp, a, Ctl, 0);
  fs_err_hook (err);
  return err;
}

/*
 * OpenDriver is defined below ROMlib_driveropen.
 * CloseDriver is defined below ROMlib_driverclose.
 */

/* FSRead, FSWrite are part of the file manager */

A3(PUBLIC, OSErr, Control, INTEGER, rn, INTEGER, code,
						    Ptr, param) /* IMII-179 */
{
    ParamBlockRec pb;
    OSErr err;

    pb.cntrlParam.ioVRefNum = 0;
    pb.cntrlParam.ioCRefNum = CW(rn);
    pb.cntrlParam.csCode = CW(code);
    if (param)
	BlockMove(param, (Ptr) pb.cntrlParam.csParam,
					 (Size) sizeof(pb.cntrlParam.csParam));
    err = PBControl(&pb, FALSE);
    fs_err_hook (err);
    return err;
}

A3(PUBLIC, OSErr, Status, INTEGER, rn, INTEGER, code, Ptr, param) /* IMII-179 */
{
    ParamBlockRec pb;
    OSErr retval;

    pb.cntrlParam.ioVRefNum = 0;
    pb.cntrlParam.ioCRefNum = CW(rn);
    pb.cntrlParam.csCode = CW(code);
    retval = PBStatus(&pb, FALSE);
    if (param)
	BlockMove((Ptr) pb.cntrlParam.csParam, param,
					 (Size) sizeof(pb.cntrlParam.csParam));
    fs_err_hook (retval);
    return retval;
}

A1(PUBLIC, OSErr, KillIO, INTEGER, rn)	/* IMII-179 */
{
    ParamBlockRec pb;
    OSErr err;

    pb.cntrlParam.ioCRefNum = CW(rn);
    err = PBKillIO(&pb, FALSE);
    fs_err_hook (err);
    return err;
}

A1(PUBLIC, DCtlHandle, GetDCtlEntry, INTEGER, rn)
{
    int devicen;

    devicen = -rn - 1;
    return (devicen < 0 || devicen >= NDEVICES) ?
	       0
	   :
	       MR(MR(UTableBase)[devicen].p);
}

/*
 * ROMlib_driveropen will be called by PBOpen if it encounters a name
 * beginning with * a period.
 */

PUBLIC driverinfo *ROMlib_otherdrivers = 0;	/* for extensibility */

PRIVATE driverinfo knowndrivers[] = {
#if defined (LINUX) || defined (NEXTSTEP) || defined (MSDOS) || defined (CYGWIN32)
  { ROMlib_serialopen, ROMlib_serialprime, ROMlib_serialctl,
    ROMlib_serialstatus, ROMlib_serialclose, (StringPtr) "\004.AIn",  -6, },

  { ROMlib_serialopen, ROMlib_serialprime, ROMlib_serialctl,
    ROMlib_serialstatus, ROMlib_serialclose, (StringPtr) "\005.AOut", -7, },

  { ROMlib_serialopen, ROMlib_serialprime, ROMlib_serialctl,
    ROMlib_serialstatus, ROMlib_serialclose, (StringPtr) "\004.BIn",  -8, },

  { ROMlib_serialopen, ROMlib_serialprime, ROMlib_serialctl,
    ROMlib_serialstatus, ROMlib_serialclose, (StringPtr) "\005.BOut", -9, },
#endif
};

A2(PUBLIC, OSErr, ROMlib_driveropen, ParmBlkPtr, pbp,		/* INTERNAL */
								   BOOLEAN, a)
{
  driverinfo *dip, *edip;
  OSErr err;
  INTEGER devicen;
  umacdriverptr up;
  DCtlHandle h;
  ramdriverhand ramdh;
  ResType typ;
  BOOLEAN alreadyopen;

  ZONE_SAVE_EXCURSION
    (SysZone,
     {
       err = noErr;
	 
       if ((ramdh =
	    (ramdriverhand) GetNamedResource(TICK("DRVR"),
					     MR(pbp->ioParam.ioNamePtr)))) {
	 LoadResource((Handle) ramdh);
	 GetResInfo((Handle) ramdh, &devicen, &typ, (StringPtr) 0);
	 devicen = CW(devicen);
	 h = MR(MR(UTableBase)[devicen].p);
	 alreadyopen = h && (HxX(h, dCtlFlags) & CWC(DRIVEROPENBIT));
	 if (!h && !(h = MR(MR(UTableBase)[devicen].p =
			    RM((DCtlHandle) NewHandle(sizeof(DCtlEntry))))))
	   err = MemError();
	 else if (!alreadyopen) {
	   memset((char *) STARH(h), 0, sizeof(DCtlEntry));
	   HxX(h, dCtlDriver)   = (umacdriverptr) RM(ramdh);
	   HxX(h, dCtlFlags)    = HxX(ramdh, drvrFlags) | CWC(RAMBASEDBIT);
	   HxX(h, dCtlRefNum)   = CW(- (devicen + 1));
	   HxX(h, dCtlDelay)    = HxX(ramdh, drvrDelay);
	   HxX(h, dCtlEMask)    = HxX(ramdh, drvrEMask);
	   HxX(h, dCtlMenu)     = HxX(ramdh, drvrMenu);
	   if (HxX(h, dCtlFlags) & CWC(NEEDTIMEBIT))
	     HxX(h, dCtlCurTicks) = CL(TickCount() + Hx(h, dCtlDelay));
	   else
	     HxX(h, dCtlCurTicks) = CLC(0x7FFFFFFF);
	   /*
	    * NOTE: this code doesn't check to see if something is already open.
	    *	 TODO:  fix this
	    */
	   pbp->cntrlParam.ioCRefNum = HxX(h, dCtlRefNum);
	   err = ROMlib_dispatch(pbp, a, Open, 0);
	 } else
	   {
	     pbp->cntrlParam.ioCRefNum = HxX(h, dCtlRefNum);
	     err = noErr;
	   }
       } else {
	   
	 dip = 0;
	 if (ROMlib_otherdrivers) {
	   for (dip = ROMlib_otherdrivers; dip->open &&
		!EqualString(dip->name, MR(pbp->ioParam.ioNamePtr), FALSE, TRUE);
		dip++)
	     ;
	   if (!dip->open)
	     dip = 0;
	 }
	 if (!dip) {
	   for (dip = knowndrivers, edip = dip + NELEM(knowndrivers);
		dip != edip &&
		!EqualString(dip->name, MR(pbp->ioParam.ioNamePtr), FALSE, TRUE);
		dip++)
	     ;
	   if (dip == edip)
	     dip = 0;
	 }
	 if (dip) {
	   devicen = -dip->refnum -1;
	   if (devicen < 0 || devicen >= NDEVICES)
	     err = badUnitErr;
	   else if (MR(UTableBase)[devicen].p)
	     err = noErr;	/* note:  if we choose to support desk */
	   /*	  accessories, we will have to */
	   /*	  check to see if this is one and */
	   /*	  call the open routine if it is */
	   else {
	     if (!(h = MR(MR(UTableBase)[devicen].p =
			  RM((DCtlHandle) NewHandle(sizeof(DCtlEntry))))))
	       err = MemError();
	     else {
	       memset((char *) STARH(h), 0, sizeof(DCtlEntry));
	       up = (umacdriverptr) NewPtr(sizeof(umacdriver));
	       if (!(HxX(h, dCtlDriver) = RM(up)))
		 err = MemError();
	       else {
		 up->udrvrOpen   = (ProcPtr) RM(dip->open);
		 up->udrvrPrime  = (ProcPtr) RM(dip->prime);
		 up->udrvrCtl    = (ProcPtr) RM(dip->ctl);
		 up->udrvrStatus = (ProcPtr) RM(dip->status);
		 up->udrvrClose  = (ProcPtr) RM(dip->close);
		 str255assign(up->udrvrName, dip->name);
		 err = noErr;
	       }
	     }
	   }
	   if (err == noErr) {
	     pbp->cntrlParam.ioCRefNum = CW(dip->refnum);
	     err = ROMlib_dispatch(pbp, a, Open, 0);
	   }
	 } else
	   err = dInstErr;
       }
     });
  fs_err_hook (err);
  return err;
}

A2(PUBLIC, OSErr, OpenDriver, StringPtr, name, INTEGER *, rnp)	/* IMII-178 */
{
    ParamBlockRec pb;
    OSErr retval;

    pb.ioParam.ioRefNum = CWC (0); /* so we can't get garbage for ioRefNum. */
    pb.ioParam.ioPermssn = fsCurPerm;
    pb.ioParam.ioNamePtr = RM(name);
    retval = ROMlib_driveropen(&pb, FALSE);
    *rnp = pb.ioParam.ioRefNum;
    fs_err_hook (retval);
    return retval;
}

A1(PUBLIC, OSErr, CloseDriver, INTEGER, rn)   /* IMII-178 */
{
    ParamBlockRec pb;
    OSErr err;

    pb.cntrlParam.ioCRefNum = CW(rn);
    err = PBClose(&pb, FALSE);
    fs_err_hook (err);
    return err;
}

