/*
* phys.c
*
* Written by Archie Cobbs <archie@freebsd.org>
* Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
* See ``COPYRIGHT.whistle''
*/
#include "ppp.h"
#include "msg.h"
#include "link.h"
#include "devices.h"
/*
* The physical layer has four states: DOWN, OPENING, CLOSING, and UP.
* Each device type must implement this set of standard methods:
*
* init Called once for each device to initialize it.
* open Called in the DOWN state to initiate connection.
* Device should eventually call PhysUp() or PhysDown().
* close Called in the OPENING or UP states.
* Device should eventually call PhysDown().
* update Called when LCP reaches the UP state. Device should
* update its configuration based on LCP negotiated
* settings, if necessary.
* showstat Display device statistics.
*
* The device should generate UP and DOWN events in response to OPEN
* and CLOSE events. If the device goes down suddenly after being OPEN,
* the close method will not be explicitly called to clean up.
*
* All device types must support MRU's of at least 1500.
*
* Each device is responsible for connecting the appropriate netgraph
* node to the PPP node when the link comes up, and disconnecting it
* when the link goes down (or is closed). The device should NOT send
* any NGM_PPP_SET_CONFIG messsages to the ppp node.
*/
/*
* DEFINITIONS
*/
struct downmsg {
const char *reason;
char buf[256];
};
/*
* GLOBAL VARIABLES
*/
const PhysType gPhysTypes[] = {
#define _WANT_DEVICE_TYPES
#include "devices.h"
NULL,
};
/*
* INTERNAL FUNCTIONS
*/
static void PhysOpenTimeout(void *arg);
static void PhysMsg(int type, void *arg);
/*
* PhysInit()
*
* Initialize physical layer state. Note that
* the device type remains unspecified at this point.
*/
PhysInfo
PhysInit(void)
{
PhysInfo p;
p = Malloc(MB_PHYS, sizeof(*p));
p->state = PHYS_DOWN;
p->msgs = MsgRegister(PhysMsg, PHYS_PRIO);
return(p);
}
/*
* PhysOpen()
*/
void
PhysOpen(void)
{
MsgSend(lnk->phys->msgs, MSG_OPEN, NULL);
}
/*
* PhysClose()
*/
void
PhysClose(void)
{
MsgSend(lnk->phys->msgs, MSG_CLOSE, NULL);
}
/*
* PhysUp()
*/
void
PhysUp(void)
{
MsgSend(lnk->phys->msgs, MSG_UP, NULL);
}
/*
* PhysDown()
*/
void
PhysDown(const char *reason, const char *details, ...)
{
struct downmsg *dm = Malloc(MB_UTIL, sizeof(*dm));
va_list args;
dm->reason = reason;
if (details) {
va_start(args, details);
vsnprintf(dm->buf, sizeof(dm->buf), details, args);
va_end(args);
}
MsgSend(lnk->phys->msgs, MSG_DOWN, dm);
}
/*
* PhysUpdate()
*/
void
PhysUpdate(void)
{
const PhysInfo p = lnk->phys;
if (p->type->update != NULL)
(*p->type->update)(p);
}
/*
* PhysGetOriginate()
*
* This returns one of LINK_ORIGINATE_{UNKNOWN, LOCAL, REMOTE}
*/
int
PhysGetOriginate(void)
{
PhysInfo const p = lnk->phys;
PhysType const pt = p->type;
return((pt && pt->originate) ? (*pt->originate)(p) : LINK_ORIGINATE_UNKNOWN);
}
/*
* PhysSetDeviceType()
*/
void
PhysSetDeviceType(char *typename)
{
PhysInfo const p = lnk->phys;
PhysType pt;
int k;
/* Make sure device type not already set */
if (p->type) {
Log(LG_ERR, ("[%s] device type already set to %s",
lnk->name, p->type->name));
return;
}
/* Locate type */
for (k = 0; (pt = gPhysTypes[k]); k++) {
if (!strcmp(pt->name, typename))
break;
}
if (pt == NULL) {
Log(LG_ERR, ("[%s] device type \"%s\" unknown", lnk->name, typename));
return;
}
p->type = pt;
/* Initialize type specific stuff */
if ((p->type->init)(p) < 0) {
Log(LG_ERR, ("[%s] type \"%s\" initialization failed",
lnk->name, p->type->name));
p->type = NULL;
return;
}
}
/*
* PhysMsg()
*/
static void
PhysMsg(int type, void *arg)
{
PhysInfo const p = lnk->phys;
time_t const now = time(NULL);
Log(LG_PHYS, ("[%s] device: %s event in state %s",
lnk->name, MsgName(type), PhysState(p)));
if (!p->type) {
Log(LG_ERR, ("[%s] this link has no type set", lnk->name));
goto done;
}
switch (type) {
case MSG_OPEN:
p->want_open = TRUE;
if (now - p->lastClose < p->type->minReopenDelay) {
if (TimerRemain(&p->openTimer) < 0) {
int delay = p->type->minReopenDelay - (now - p->lastClose);
if ((random() ^ getpid() ^ time(NULL)) & 1)
delay++;
else if (delay > 1)
delay--;
Log(LG_PHYS, ("[%s] pausing %d seconds before open",
lnk->name, delay));
TimerStop(&p->openTimer);
TimerInit(&p->openTimer, "PhysOpen",
delay * SECONDS, PhysOpenTimeout, NULL);
TimerStart(&p->openTimer);
}
break;
}
switch (p->state) {
case PHYS_DOWN:
if (TimerRemain(&p->openTimer) >= 0)
goto done;
(*p->type->open)(p);
p->state = PHYS_OPENING;
break;
case PHYS_CLOSING:
case PHYS_OPENING:
case PHYS_UP:
break;
}
break;
case MSG_CLOSE:
p->want_open = FALSE;
TimerStop(&p->openTimer);
switch (p->state) {
case PHYS_DOWN:
case PHYS_CLOSING:
break;
case PHYS_OPENING:
case PHYS_UP:
(*p->type->close)(p);
p->state = PHYS_CLOSING;
break;
}
break;
case MSG_DOWN:
{
struct downmsg *const dm = (struct downmsg *) arg;
p->lastClose = now;
switch (p->state) {
case PHYS_CLOSING:
RecordLinkUpDown(-1);
/* fall through */
case PHYS_DOWN:
if (p->want_open)
PhysOpen();
break;
case PHYS_OPENING:
if (*dm->buf) {
SetStatus(ADLG_WAN_CONNECT_FAILURE, STR_COPY, dm->buf);
RecordLinkUpDownReason(lnk, 0, dm->reason, dm->buf);
} else {
SetStatus(ADLG_WAN_CONNECT_FAILURE, STR_CON_FAILED0);
RecordLinkUpDownReason(lnk, 0, dm->reason, NULL);
}
RecordLinkUpDown(0);
#if 0
SetStatus(ADLG_WAN_WAIT_FOR_DEMAND, STR_COPY, dm->buf);
#endif
RecordLinkUpDownReason(lnk, 1, STR_REDIAL, NULL);
break;
case PHYS_UP:
if (dm->reason)
RecordLinkUpDownReason(lnk, 0, dm->reason, dm->buf);
RecordLinkUpDown(-1);
SetStatus(ADLG_WAN_WAIT_FOR_DEMAND, STR_COPY, dm->buf);
RecordLinkUpDownReason(lnk, 1, STR_REDIAL, NULL);
break;
}
p->state = PHYS_DOWN;
LinkDown(lnk);
Freee(dm);
}
break;
case MSG_UP:
switch (p->state)
{
case PHYS_DOWN:
case PHYS_CLOSING:
Log(LG_ERR, ("[%s] weird event in this state", lnk->name));
break;
case PHYS_OPENING:
/*
Log(LG_PHYS, ("[%s] connection successful", lnk->name));
*/
RecordLinkUpDown(1);
LinkUp(lnk);
break;
case PHYS_UP:
break;
}
p->state = PHYS_UP;
break;
}
done:
Log(LG_PHYS, ("[%s] device is now in state %s",
lnk->name, PhysState(p)));
}
/*
* PhysOpenTimeout()
*/
static void
PhysOpenTimeout(void *arg)
{
PhysInfo const p = lnk->phys;
TimerStop(&p->openTimer);
assert(p->want_open);
PhysOpen();
}
/*
* PhysStat()
*/
void
PhysStat(int ac, char *av[], void *arg)
{
PhysInfo const p = lnk->phys;
printf("\tType : %s\n", p->type->name);
printf("\tState : %s\n", PhysState(p));
if (p->type->showstat)
(*p->type->showstat)(p);
}
/*
* PhysState()
*/
const char *
PhysState(PhysInfo p)
{
switch (p->state) {
case PHYS_DOWN: return("DOWN");
case PHYS_CLOSING: return("CLOSING");
case PHYS_OPENING: return("OPENING");
case PHYS_UP: return("UP");
}
return("???");
}
syntax highlighted by Code2HTML, v. 0.9.1