debugknl.cc

//*************************************************************************
//  MODULE : Kernel - The RCOS message switching centre.		  *
//  AUTHOR : Ron Chernich                                                 *
//  PURPOSE: Supply a single point which receives and dispatches messages *
//	     and runs applications when it has nothing else to do..	  *
//  HISTORY:                                                              *
//   31-MAR-93	First (MSC/C++ 7.00) version				  *
//   04-APR-93	Port concept with pointers to function members introduced *
//   20-APR-94  Memory leak in Run and Destructor plugged (10 byte MSG)   *
//*************************************************************************

#include "kernel.hh"
#include "exec.hh"

#define DEBUG

#ifdef DEBUG
# include 
  static FILE *flog;
#endif


//////////////////////////////////////////////////////////////////////////
// Constructor for Port class must set its ID and register with
// the Kernel..
//
port::port (UINT16 id, UINT16 cls, Knl *pK)
{
  uID = id;
  pTx = pK;
  MSG Msg(uID, KM_Register, cls, (void*)(this));
  pTx->SendMsg(ID_Kernel, &Msg);
}

///////////////////////
// Port routine to simplify sending error messages to its manager.
//  is a device specific code (positive) which can be used by
// "support" to trace reason for failure.   is a flag for
// degree - set if Fatal.
//
void port::DevError (INT16 nErr, BOOL bFatal)
{
  MSG Msg(uID, KM_DevError, ((bFatal) ? (0x8000 | nErr) : nErr));
  pTx->SendMsg(ID_Kernel, &Msg);
}

//////////////////////////////////////////////////////////////////////////
// Since the class exports its  member, all of Kernel is considered
// critical and must be protected in the time honored way.
//
Knl::Knl (void)
{
  inCrit = 0;
  pTask = new Exec(this);
# ifdef DEBUG
  flog = fopen("debug.log", "wt");
# endif
}

////////////
// Close down the operating system - free up any memory occupied by
// structures in the linked lists that the list class won't be able
// to release, and close down the Exec Instance.
//
Knl::~Knl (void)
{
  PQMSG pM;
  while (pM = (PQMSG)Msg.DblGetHead()) {
    if (pM->pMsg->pBody)
      delete [] pM->pMsg->pBody;
    if (pM->pMsg)
      delete pM->pMsg;
    Msg.DblDelete();
  }
  delete pTask;
# ifdef DEBUG
  fclose(flog);
# endif
}

//////////////////
// Scan the list of ports known to us for one matching the passed ID
// RETURNS: pointer to structure, or NULL if not in list
//
PDEVLST Knl::GetPort (UINT16 uPortID)
{
  PDEVLST pD = (PDEVLST)Dev.DblGetHead();

  while (pD)
    if (pD->uID == uPortID)
      break;
    else
      pD = (PDEVLST)Dev.DblGetNext();
  return pD;
}

/////////////////////
// Dispatch the message at the head of the queue: if it's a KERNEL service
// request, dispatch direct; if it's for a device which does not exist,
// disgard it; if the destination is legal and its status is "Ready", invoke
// the port's receive member. If the port is Inhibited or Busy, call our own
// "Post" member, effectively putting the message to the back of the queue.
// If the destination is a process which is sleeping on this message, place
// the message in the process PCB and remove the block on the process.
// If there are no messages, see if we have a ready process and execute its
// next p-code.	Failing all the above, just exit .. something will happen
// eventually.
//
// Late breaking thought: on the above logic, if we have a message for a port
// which is continually busy,  will spend ALL its time re-posting the
// message and no p-codes will ever get executed! So, in this event we must
// call the dispatcher as well as sending the message to the back of the Q.
//							 arcy .. 20-APR-94
//
// RETURNS: TRUE  .. Single-step procedure (if active) can continue
//	    FALSE .. A p-code has been executed; deactivte s-step.
//
BOOL Knl::Run (void)
{
  BOOL bStep = TRUE;

  if (inCrit == 0) {
    --inCrit;
    PQMSG pM = (PQMSG)Msg.DblGetHead();
    if (pM == NULL)
      bStep = pTask->Dispatch();
    else {
      if (pM->wDest == ID_Kernel)
	Service(pM->pMsg);
      else {
	PDEVLST pD = GetPort(pM->wDest);
	if (pD) {
#ifdef DEBUG
  fprintf(flog, "message at %04X:%04X\n", _FP_SEG(pM->pMsg), _FP_OFF(pM->pMsg));
  fprintf(flog, "   %04X, %04X, %04X,", pM->pMsg->wSender, pM->pMsg->wMsgType, pM->pMsg->wParam);
  fprintf(flog, " %04X:%04X\n\n", _FP_SEG(pM->pMsg->pBody), _FP_OFF(pM->pMsg->pBody));
  fflush(flog);
#endif
	  if (pD->uStatus == STAT_Ready)
	    pD->pP->RxPort(pM->pMsg);
	  else {
	    PostMsg(pD->uID, pM->pMsg);
	    bStep = pTask->Dispatch();
	  }
	}
      }
      pM = (PQMSG)Msg.DblGetHead();
      if (pM->pMsg->pBody)
	delete [] pM->pMsg->pBody;
      if (pM->pMsg)
        delete pM->pMsg;
      Msg.DblDelete();
    }
    ++inCrit;
  }
  return bStep;
}

////////////////
// This function acts like a direct call to the destination device,
// effectively allowing any device to make a synchronous call to
// another driver.  If the destination don't exist, nothing happens.
//
void Knl::SendMsg (UINT16 dest, PMSG pMsg)
{
  if (dest == ID_Kernel)
    Service(pMsg);
  else {
    PDEVLST pD = GetPort(dest);
    if (pD) {
      pMsg->wMsgType |= MM_Sync;
      pD->pP->RxPort(pMsg);
    }
  }
}

///////////////
// This function effects an asynchronous call to the destination device or
// process.  If the destination is a process, we ask Exec to attach it to
// the message input queue for the process (causing it to become un-blocked
// if it had previously blocked on a PostMsg).	All other messages are added
// to our message FIFO queue.  Some checking for destination validity must
// be done somewhere, but not here - we can leave that until this message
// reaches the head and is about to be dispatched.  Finally, if the sender
// is a process, we get Exec to block the process until a reply for this
// message returns.
//
void Knl::PostMsg (UINT16 uDest, PMSG pMsg)
{
  if (uDest < MAX_PROC)
    pTask->PostReply(uDest, pMsg);
  else {
    QMSG temp;
    temp.wDest = uDest;
    temp.pMsg = new MSG(*pMsg);
    Msg.DblAppend((void*)&temp, sizeof(QMSG));
    if (pMsg->wSender < MAX_PROC)
      pTask->Block();
  }
}

//////////////////
// allows a port to "peek" at it's next message without actually
// retrieving it from the queue..
//
void Knl::PeekMsg (UINT16 uDest, PMSG *p)
{
  PQMSG pM = (PQMSG)Msg.DblGetHead();

  while (pM && (pM->wDest != uDest))
    pM = (PQMSG)Msg.DblGetNext();
  *p = (pM) ? pM->pMsg : NULL;
}

///////////////////
// Process a Kernel services message -
//   Adds new ports to the list provide that the ID is unique
//   Allows a port to change it's status
//   Displays Device errors/failures on console (unless it's failed!)
//
void Knl::Service (PMSG pM)
{
  PDEVLST pD = GetPort(pM->wSender);
  static char *szEmsg = "\33[s\33[x;yH\33[K  Device 0x%X %s: 0x%x\33[u\a";

  switch (pM->wMsgType) {
    case KM_SetStatus:
      if (pD)
	pD->uStatus = pM->wParam;
      break;
    case KM_DevError:
      if (pD) {
	if ((INT16)pM->wParam < 0)
	  pD->uStatus = STAT_Inhibit;
	if (pD->uID != ID_TTY0) {
	  char *pstr = (char*)malloc(strlen(szEmsg)+16);
	  if (pstr) {
	    sprintf(pstr, szEmsg, pD->uID,
	     (((INT16)pM->wParam < 0) ? "FAILED":"ERROR"), pM->wParam&0x7fff);
	    MSG Msg(ID_Kernel, KM_WriteBlk, strlen(pstr), (void*)pstr);
	    *(pstr + 5) = *(pstr + 7) = '\0';
	    SendMsg(ID_TTY0, &Msg);
	  }
	}
      }
      break;
    case KM_Register:
      if (pD == NULL) {
	pD = new DEVLST;
	if (pD) {
	  pD->uAssign = NO_PROC;
	  pD->uID = pM->wSender;
	  pD->uClass = pM->wParam;
	  pD->uStatus = STAT_Ready;
	  pD->pP = (port*)(pM->pBody);
	  Dev.DblAppend((void*)pD, sizeof(DEVLST));
	  delete pD;
	}
      }
      break;
    case KM_CheckOut:
      if (pD)
	Dev.DblDelete();
      break;
    case KM_Open: {
	PDEVLST pDC = (PDEVLST)Dev.DblGetHead();
	while (pDC) {
	  if ((pDC->uClass == pM->wParam) && (pDC->uAssign == NO_PROC)) {
	    SendMsg(pDC->uID, pM);
	    pDC->uAssign = pM->wSender;
	    pM->wParam = pDC->uID;
	    return;
	  }
	  pDC = (PDEVLST)Dev.DblGetNext();
	}
	pM->wParam = ID_NULL;
      }
      break;
    case KM_Close: {
	PDEVLST pDC = (PDEVLST)Dev.DblGetHead();
	while (pDC) {
	  if (pDC->uID == pM->wParam) {
	    SendMsg(pDC->uID, pM);
	    pDC->uAssign = NO_PROC;
	    pDC->uStatus = STAT_Ready;
	    pM->wParam = ID_NULL;
	    return;
	  }
	  pDC = (PDEVLST)Dev.DblGetNext();
	}
	pM->wParam = 0;
      }
      break;
    case ANI_GET_QUS:
      pTask->GetQcom(pM->wParam, (PMSG_ANIQ)pM->pBody);
      break;
    case ANI_GET_PCB:
      pTask->GetPcom((PMSG_ANIP)pM->pBody);
      break;
  }
}


////////////////////////////////// EOF /////////////////////////////////////