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 /////////////////////////////////////