/*
 * 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"
#include "util.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
 */

  /* Set menu options */
  enum {
    SET_DEVTYPE,
    SET_ACCEPT,
    SET_DENY,
    SET_ENABLE,
    SET_DISABLE,
    SET_YES,
    SET_NO,
  };

/*
 * INTERNAL FUNCTIONS
 */

  static void	PhysOpenTimeout(void *arg);
  static void	PhysMsg(int type, void *arg);
  static int	PhysSetCommand(Context ctx, int ac, char *av[], void *arg);
  static PhysInfo	PhysFind(char *name);

/*
 * GLOBAL VARIABLES
 */

  const struct cmdtab PhysSetCmds[] = {
    { "type type",			"Device type",
	PhysSetCommand, NULL, (void *) SET_DEVTYPE },
    { NULL },
  };


  const PhysType gPhysTypes[] = {
#define _WANT_DEVICE_TYPES
#include "devices.h"
    NULL,
  };

  const char *gPhysStateNames[] = {
    "DOWN",
    "CONNECTING",
    "READY",
    "UP",
  };

/*
 * PhysInit()
 *
 * Initialize physical layer state. Note that
 * the device type remains unspecified at this point.
 */

PhysInfo
PhysInit(char *name, Link l, Rep r)
{
    PhysInfo		p;
    struct context	ctx;
    int			k;

    /* See if bundle name already taken */
    if ((p = PhysFind(name)) != NULL) {
	Log(LG_ERR, ("phys \"%s\" already exists", name));
	return NULL;
    }

    p = Malloc(MB_PHYS, sizeof(*p));
  
    strlcpy(p->name, name, sizeof(p->name));
    p->state = PHYS_STATE_DOWN;
    p->msgs = MsgRegister(PhysMsg);
    p->link = l;
    p->rep = r;

    /* Find a free link pointer */
    for (k = 0; k < gNumPhyses && gPhyses[k] != NULL; k++);
    if (k == gNumPhyses)			/* add a new link pointer */
	LengthenArray(&gPhyses, sizeof(*gPhyses), &gNumPhyses, MB_PHYS);

    p->id = k;
    gPhyses[k] = p;

    memset(&ctx, 0, sizeof(ctx));
    ctx.lnk = l;
    ctx.phys = p;
    ctx.rep = NULL;
    ctx.bund = NULL;

    /* Read special configuration for link, if any */
    (void) ReadFile(LINKS_FILE, name, DoCommand, &ctx);

    return(p);
}

/*
 * PhysOpenCmd()
 */

void
PhysOpenCmd(Context ctx)
{
    PhysOpen(ctx->phys);
}

/*
 * PhysOpen()
 */

void
PhysOpen(PhysInfo p)
{
    MsgSend(p->msgs, MSG_OPEN, p);
}

/*
 * PhysCloseCmd()
 */

void
PhysCloseCmd(Context ctx)
{
    PhysClose(ctx->phys);
}

/*
 * PhysClose()
 */

void
PhysClose(PhysInfo p)
{
    MsgSend(p->msgs, MSG_CLOSE, p);
}

/*
 * PhysUp()
 */

void
PhysUp(PhysInfo p)
{
    Log(LG_PHYS2, ("[%s] device: UP event", p->name));
    if (p->link) {
	LinkUp(p->link);
    } else if (p->rep) {
	RepUp(p);
    }
}

/*
 * PhysDown()
 */

void
PhysDown(PhysInfo p, const char *reason, const char *details, ...)
{
    Log(LG_PHYS2, ("[%s] device: DOWN event", p->name));
    p->lastClose = time(NULL);
    if (p->link) {
	if (details) {
	    va_list	args;
	    char	buf[256];
	    
	    va_start(args, details);
	    vsnprintf(buf, sizeof(buf), details, args);
	    va_end(args);
	    RecordLinkUpDownReason(NULL, p->link, 0, reason, buf);
	} else {
	    RecordLinkUpDownReason(NULL, p->link, 0, reason, NULL);
	}
	p->link->upReasonValid=0;
	LinkDown(p->link);
    } else if (p->rep) {
	RepDown(p);
    }
}

/*
 * PhysIncoming()
 */

void
PhysIncoming(PhysInfo p)
{
    if (p->link) {
	RecordLinkUpDownReason(NULL, p->link, 1, STR_INCOMING_CALL, NULL);
	BundOpenLink(p->link);
    } else if (p->rep) {
        RepIncoming(p);
    }
}

/*
 * PhysSetAccm()
 */

int
PhysSetAccm(PhysInfo p, uint32_t xmit, u_int32_t recv)
{
    if (p->type && p->type->setaccm)
	return (*p->type->setaccm)(p, xmit, recv);
    else 
	return (0);
}

/*
 * PhysGetUpperHook()
 */

int
PhysGetUpperHook(PhysInfo p, char *path, char *hook)
{
    if (p->link && p->link->bund) {
	snprintf(path, NG_PATHLEN, "[%lx]:", (u_long)p->link->bund->nodeID);
	snprintf(hook, NG_HOOKLEN, "%s%d",
	    NG_PPP_HOOK_LINK_PREFIX, p->link->bundleIndex);
	return 1;
    } else if (p->rep) {
	return RepGetHook(p, path, hook);
    }
    return 0;
}

/*
 * PhysGetOriginate()
 *
 * This returns one of LINK_ORIGINATE_{UNKNOWN, LOCAL, REMOTE}
 */

int
PhysGetOriginate(PhysInfo p)
{
  PhysType	const pt = p->type;

  return((pt && pt->originate) ? (*pt->originate)(p) : LINK_ORIGINATE_UNKNOWN);
}

/*
 * PhysIsSync()
 *
 * This returns 1 if link is synchronous
 */

int
PhysIsSync(PhysInfo p)
{
  PhysType	const pt = p->type;

  return((pt && pt->issync) ? (*pt->issync)(p) : 0);
}

/*
 * PhysSetCalledNum()
 */

int
PhysSetCallingNum(PhysInfo p, char *buf)
{
    PhysType	const pt = p->type;

    if (pt && pt->setcallingnum)
	return ((*pt->setcallingnum)(p, buf));
    else
	return (0);
}

/*
 * PhysSetCalledNum()
 */

int
PhysSetCalledNum(PhysInfo p, char *buf)
{
    PhysType	const pt = p->type;

    if (pt && pt->setcallednum)
	return ((*pt->setcallednum)(p, buf));
    else
	return (0);
}

/*
 * PhysGetPeerAddr()
 */

int
PhysGetPeerAddr(PhysInfo p, char *buf, int buf_len)
{
    PhysType	const pt = p->type;

    buf[0] = 0;

    if (pt && pt->peeraddr)
	return ((*pt->peeraddr)(p, buf, buf_len));
    else
	return (0);
}

/*
 * PhysGetPeerPort()
 */

int
PhysGetPeerPort(PhysInfo p, char *buf, int buf_len)
{
    PhysType	const pt = p->type;

    buf[0] = 0;

    if (pt && pt->peerport)
	return ((*pt->peerport)(p, buf, buf_len));
    else
	return (0);
}

/*
 * PhysGetCalledNum()
 */

int
PhysGetCallingNum(PhysInfo p, char *buf, int buf_len)
{
    PhysType	const pt = p->type;

    buf[0] = 0;

    if (pt && pt->callingnum)
	return ((*pt->callingnum)(p, buf, buf_len));
    else
	return (0);
}

/*
 * PhysGetCalledNum()
 */

int
PhysGetCalledNum(PhysInfo p, char *buf, int buf_len)
{
    PhysType	const pt = p->type;

    buf[0] = 0;

    if (pt && pt->callednum)
	return ((*pt->callednum)(p, buf, buf_len));
    else
	return (0);
}

/*
 * PhysShutdown()
 */

void
PhysShutdown(PhysInfo p)
{
    PhysType	const pt = p->type;
    int		k;

    if (pt && pt->shutdown)
	(*pt->shutdown)(p);

    for (k = 0; 
	k < gNumPhyses && gPhyses[k] != p;
	k++);
    if (k < gNumPhyses)
	gPhyses[k] = NULL;

    Freee(MB_PHYS, p);
}

/*
 * PhysSetDeviceType()
 */

void
PhysSetDeviceType(PhysInfo p, char *typename)
{
  PhysType	pt;
  int		k;

  /* Make sure device type not already set */
  if (p->type) {
    Log(LG_ERR, ("[%s] device type already set to %s",
      p->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", p->name, typename));
    return;
  }
  p->type = pt;

  /* Initialize type specific stuff */
  if ((p->type->init)(p) < 0) {
    Log(LG_ERR, ("[%s] type \"%s\" initialization failed",
      p->name, p->type->name));
    p->type = NULL;
    return;
  }
}

/*
 * PhysMsg()
 */

static void
PhysMsg(int type, void *arg)
{
  PhysInfo	const p = (PhysInfo)arg;
  time_t	const now = time(NULL);

  Log(LG_PHYS2, ("[%s] device: %s event",
    p->name, MsgName(type)));
  if (!p->type) {
    Log(LG_ERR, ("[%s] this link has no type set", p->name));
    return;
  }
  switch (type) {
    case MSG_OPEN:
        if (p->link)
    	    p->link->downReasonValid=0;
        if (now - p->lastClose < p->type->minReopenDelay) {
	    if (TimerRemain(&p->openTimer) < 0) {
		int	delay = p->type->minReopenDelay - (now - p->lastClose);

		if ((random() ^ gPid ^ time(NULL)) & 1)
		    delay++;
		Log(LG_PHYS, ("[%s] pausing %d seconds before open",
		    p->name, delay));
		TimerStop(&p->openTimer);
		TimerInit(&p->openTimer, "PhysOpen",
		    delay * SECONDS, PhysOpenTimeout, p);
		TimerStart(&p->openTimer);
	    }
	    break;
        }
        TimerStop(&p->openTimer);
        (*p->type->open)(p);
        break;
    case MSG_CLOSE:
        TimerStop(&p->openTimer);
        (*p->type->close)(p);
        break;
    default:
        assert(FALSE);
  }
}

/*
 * PhysOpenTimeout()
 */

static void
PhysOpenTimeout(void *arg)
{
  PhysInfo	const p = (PhysInfo)arg;

  TimerStop(&p->openTimer);
  PhysOpen(p);
}

/*
 * PhysCommand()
 */

int
PhysCommand(Context ctx, int ac, char *av[], void *arg)
{
  int		k;
  PhysInfo	p;

  switch (ac) {
    case 0:

        Printf("Defined phys items:\r\n");

        for (k = 0; k < gNumPhyses; k++) {
	    if ((p = gPhyses[k]) != NULL) {
		if (p->link && p->link->bund)
		    Printf("\t\"%s\" -> link \"%s\" -> bundle \"%s\"\r\n", 
			p->name, p->link->name, p->link->bund->name);
		else if (p->rep)
		    Printf("\t\"%s\" -> repeater \"%s\"\r\n", p->name, p->rep->name);
		else
		    Printf("\t\"%s\" -> unknown\r\n", p->name);
	    }
	}
      break;

    case 1:

	if ((p = PhysFind(av[0])) == NULL) {
	    Printf("Phys \"%s\" is not defined\r\n", av[0]);
	    return(0);
	}

	/* Change default link and bundle */
        ctx->phys = p;
        ctx->lnk = p->link;
        if (p->link) {
    	    ctx->bund = ctx->lnk->bund;
	} else {
	    ctx->bund = NULL;
	}
	ctx->rep = p->rep;
	break;

    default:
      return(-1);
  }	
  return(0);
}

/*
 * PhysFind()
 *
 * Find a phys structure
 */

static PhysInfo
PhysFind(char *name)
{
  int	k;

  for (k = 0;
    k < gNumPhyses && strcmp(gPhyses[k]->name, name);
    k++);
  return((k < gNumPhyses) ? gPhyses[k] : NULL);
}


/*
 * PhysStat()
 */

int
PhysStat(Context ctx, int ac, char *av[], void *arg)
{
  PhysInfo	const p = ctx->phys;

  Printf("\tType  : %s\r\n", p->type->name);
  if (p->type->showstat)
    (*p->type->showstat)(ctx);
  return 0;
}

/*
 * PhysSetCommand()
 */

static int
PhysSetCommand(Context ctx, int ac, char *av[], void *arg)
{
  if (ac == 0)
    return(-1);

  switch ((intptr_t)arg) {
    case SET_DEVTYPE:
      PhysSetDeviceType(ctx->phys, *av);
      break;
/*
    case SET_ACCEPT:
      AcceptCommand(ac, av, &phys->options, gConfList);
      break;

    case SET_DENY:
      DenyCommand(ac, av, &phys->options, gConfList);
      break;

    case SET_ENABLE:
      EnableCommand(ac, av, &phys->options, gConfList);
      break;

    case SET_DISABLE:
      DisableCommand(ac, av, &phys->options, gConfList);
      break;

    case SET_YES:
      YesCommand(ac, av, &phys->options, gConfList);
      break;

    case SET_NO:
      NoCommand(ac, av, &phys->options, gConfList);
      break;
*/
    default:
      assert(0);
  }

  return(0);
}



syntax highlighted by Code2HTML, v. 0.9.1