
/*
 *  SERIALPORT.C
 *
 *  (C) Copyright 1989-1990 by Matthew Dillon,  All Rights Reserved.
 *
 *  Serial Port locking to prevent collisions between, say, a Getty
 *  accepting a login sequence and a uucico calling up another machine.
 *
 *  The existance of the public port indicates a lock.	People waiting
 *  for the lock PutMsg() EXEC messages to the port.  Upon unlocking,
 *  the unlocker will deallocate the port only if no messages are
 *  pending, else it will ReplyMsg() the first message in the queue
 *  and NOT deallocate the port.
 *
 *  On receiving a message back you own the port and its memory
 */

#include <proto/all.h>
#include <exec/memory.h>

typedef struct MsgPort	PORT;
typedef struct Message	MSG;

struct SMsgPort {
    PORT    Port;
    short   NameLen;
    short   Reserved;
};

typedef struct SMsgPort SPORT;

static SPORT	*SPLock;

int IAmGetty = 0;

void
LockSerialPort(name, unit)
char *name;
long unit;
{
    short namelen = strlen(name) + 32;
    char *portname;
    SPORT *sport;
    PORT  *rport = NULL;
    MSG   msg;

    if (SPLock)
	return;

    portname = AllocMem(namelen, MEMF_PUBLIC | MEMF_CLEAR);

    sprintf(portname, "SPLock-%d-%s", unit, name);

    Forbid();
    if (sport = (SPORT *)FindPort(portname)) {
	rport = CreatePort(NULL, 0);
	msg.mn_ReplyPort = rport;
	msg.mn_Length = 0;
	if (IAmGetty)
	    AddHead(&sport->Port.mp_MsgList, &msg);
	else
	    AddTail(&sport->Port.mp_MsgList, &msg);
	FreeMem(portname, namelen);
    } else {
	sport = AllocMem(sizeof(SPORT), MEMF_PUBLIC | MEMF_CLEAR);
	sport->Port.mp_Node.ln_Name = portname;
	sport->Port.mp_Node.ln_Type = NT_MSGPORT;
	sport->Port.mp_Flags = PA_IGNORE;
	sport->NameLen = namelen;
	AddPort(&sport->Port);
    }
    Permit();
    SPLock = sport;
    if (rport) {            /*  wait for message to be returned */
	WaitPort(rport);
	DeletePort(rport);
    }
}

/*
 *  Unlock the serial port.  If I am NOT the Getty then delay before
 *  unlocking to give the Getty a chance to take the next lock (it takes
 *  about a second for the Getty to realize the serial.device has been
 *  closed and try to take the lock.
 */

void
UnLockSerialPort(name, unit)
char *name;
long unit;
{
    MSG *msg;

    if (SPLock) {
	if (IAmGetty == 0)
	    sleep(2);
	Forbid();
	if (msg = GetMsg(&SPLock->Port)) {  /*  somebody else wants it */
	    ReplyMsg(msg);
	} else {			/*  nobody else wants it   */
	    RemPort(&SPLock->Port);
	    FreeMem(SPLock->Port.mp_Node.ln_Name, SPLock->NameLen);
	    FreeMem(SPLock, sizeof(SPORT));
	}
	Permit();
	SPLock = NULL;
    }
}

