#ifdef UNIX
/*
 * File stolen from WAMPES 921229, modified for compatibility with JNOS and my
 * tastes, and left to sink or swim.  Blub!  ++bsa
 *
 * The actual structure is much closer to that of JNOS than to WAMPES.  The
 * reason is that WAMPES uses these weirdball I/O hooks... We will use the
 * "classic" interface, modified by the use of register_fd().  (Actually, the
 * weirdball I/O hooks are just WAMPES's version of register_fd().  The API
 * for WAMPES is a heck of a lot hairier, though.)
 */

#include "global.h"
#include "commands.h"

#include <fcntl.h>
#include <termios.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <signal.h>

#ifdef UNIX_SOCKETS
/* to prevent calling in "socket.h", "tcp.h", and "sockaddr.h" */
#define	_SOCKET_H
#define _TCP_H
#define _SOCKADDR_H
/*lint -save -e508 -e631 -e532 -e27 -e43 */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
/*lint -restore */
#endif

#include "mbuf.h"
#include "proc.h"
/*lint -save -e114 */
#include "iface.h"
/*lint -restore */
#include "asy.h"
#include "unixasy.h"
/*lint -esym(762,select) */
#include "hardware.h"
#include "devparam.h"
#include "kisspoll.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: unixasy.c,v 1.34 1997/09/07 00:31:16 root Exp $";
#endif

#ifndef EWOULDBLOCK
#define EWOULDBLOCK EAGAIN
#endif

#ifndef TNOS_CRTSCTS
#define CRTSCTS      0x80000000UL
#endif

char const *LOCKDIR = NULLCHAR;

static int find_speed (long speed);
static void asy_tx (int, void *, void *);
static void asy_input (int, void *, void *);
static int asy_vmin (int dev, long pktsize);
static int asy_rts (int dev, unsigned long onoff);
void pasy (struct asy *asyp);

struct asy *Asy;	/* allocated in main.c */

/*---------------------------------------------------------------------------*/

static int dorxqueue (int argc, char **argv, void *p);
static int dotxqueue (int argc, char **argv, void *p);
static int dobufsize (int argc, char **argv, void *p);
static int doasy2stat (int argc, char **argv, void *p);

static struct cmds AsyPcmds[] =
{
	{ "rxqueue",	dorxqueue,	0, 0, NULLCHAR },
	{ "txqueue",	dotxqueue,	0, 0, NULLCHAR },
	{ "bufsize",	dobufsize,	0, 0, NULLCHAR },
	{ "status",	doasy2stat,	0, 0, NULLCHAR },
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR }
};

/*---------------------------------------------------------------------------*/

static struct {
	long speed;
	speed_t flags;
} speed_table[] = {
#ifdef B50
	{ 50, B50 },
#endif
#ifdef B75
	{ 75, B75 },
#endif
#ifdef B110
	{ 110, B110 },
#endif
#ifdef B134
	{ 134, B134 },
#endif
#ifdef B150
	{ 150, B150 },
#endif
#ifdef B200
	{ 200, B200 },
#endif
#ifdef B300
	{ 300, B300 },
#endif
#ifdef B600
	{ 600, B600 },
#endif
#ifdef B900
	{ 900, B900 },
#endif
#ifdef B1200
	{ 1200, B1200 },
#endif
#ifdef B1800
	{ 1800, B1800 },
#endif
#ifdef B2400
	{ 2400, B2400 },
#endif
#ifdef B3600
	{ 3600, B3600 },
#endif
#ifdef B4800
	{ 4800, B4800 },
#endif
#ifdef B7200
	{ 7200, B7200 },
#endif
#ifdef B9600
	{ 9600, B9600 },
#endif
#ifdef B19200
	{ 19200, B19200 },
#endif
#ifdef B38400
	{ 38400, B38400 },
#endif
#ifdef B57600
	{ 57600, B57600 },
#endif
#ifdef B115200
	{ 115200, B115200 },
#endif
#ifdef B230400
	{ 230400, B230400 },
#endif
#ifdef B460800
	{ 460800, B460800 },
#endif
	{ -1, 0 }
};


/*---------------------------------------------------------------------------*/

static int
find_speed (long speed)
{
int i;

	i = 0;
	while (speed_table[i].speed < speed && speed_table[i + 1].speed > 0)
		i++;
	return i;
}


/*---------------------------------------------------------------------------*/


int
Unixlockfile (char const *name, int dev, char const *basename, int silent)
{
int k;
int fd;
int (*pf) (const char *fmt,...);
#ifdef HAVE_BINARY_LOCKFILES
int pid;
#else
char filename[80];
#endif

	pf = (dev) ? tprintf : printf;
	for (k = 0; k < 30; k++) {
		if ((fd = open (name, O_WRONLY | O_CREAT | O_EXCL, 0644)) != -1 || errno != EEXIST)
			break;
		/* read pid, unlink and retry if proc no longer exists */
		if ((fd = open (name, O_RDONLY)) == -1) {
			(void) sleep (1);
			continue;	/* timing is everything */
		}
#ifdef HAVE_BINARY_LOCKFILES
		read (fd, &pid, sizeof (pid));
		close (fd);
		fd = pid;
#else
		filename[read (fd, filename, 10)] = '\0';
		close (fd);
		sscanf (filename, "%d", &fd);
#endif
		if (kill (fd, 0) == -1 && errno == ESRCH) {
			if (!silent)
				(void) (*pf) ("Removing stale lockfile for %s\n", basename);
			unlink (name);
			continue;
		}
		(void) (*pf) ("%s%s is locked by process %d\n", (dev) ? "/dev/" : "", basename, fd);
		return -1;
	}
	if (fd == -1) {
		(void) (*pf) ("Can't lock %s%s: %s\n", (dev) ? "/dev/" : "", basename, strerror (errno));
		return -1;
	}
	(void) chmod (name, 0644);	/* beware of overly restrictive umask */
#ifdef HAVE_BINARY_LOCKFILES
	pid = getpid ();
	write (fd, &pid, sizeof (pid));
#else
	sprintf (filename, "%10d\n", (int)getpid ());
	write (fd, filename, 11);
#endif
	close (fd);

	return 0;
}



/*---------------------------------------------------------------------------*/

static char const *lockdir_paths[] = {
	"/usr/spool/uucp",
	"/var/spool/lock",
	"/var/spool/uucp",
	"/var/lock",
	NULLCHAR
};


void
findUnixLockdir (char *where)
{
int k;
struct stat sb;

	if (where && !stat (where, &sb) && S_ISDIR(sb.st_mode))	{
		LOCKDIR = strdup (where);
		return;
	}

	for (k = 0; lockdir_paths[k] != NULLCHAR; k++)	{
		if (stat (lockdir_paths[k], &sb))
			continue;
		if (S_ISDIR(sb.st_mode))	{
			LOCKDIR = lockdir_paths[k];
			return;
		}
	}

	/* error! can't find lockdir! */
	printf ("\007!! Cannot start TNOS!\nCannot locate a directory for the Lockfiles!\nThe following were searched:\n");
	for (k = 0; lockdir_paths[k] != NULLCHAR; k++)
		printf ("\t%s\n", lockdir_paths[k]);

	(void) fflush (stdout);
	(void) sleep (5);
	exit (1);
}



/*---------------------------------------------------------------------------*/

/* Initialize asynch port "dev" */
int
asy_init (dev, ifp, arg1, arg2, bufsize, trigchar, monitor, speed, force, triglevel, polled)
int dev;
struct iface *ifp;
char *arg1;		/* Attach args for address and vector */
char *arg2 OPTIONAL;
int16 bufsize;
int trigchar OPTIONAL;
char monitor OPTIONAL;
long speed;
int force OPTIONAL;
int triglevel;
int polled OPTIONAL;
{
register struct asy *ap;
char filename[80];
char *ifn;
int sp, fd = 0;
struct termios termios;
#ifdef POLLEDKISS
long interval;
#endif

	ap = &Asy[dev];

	filename[0] = 0;
#ifdef UNIX_SOCKETS
	if (atoi (arg2)) {
		struct sockaddr_in addr;
		struct hostent *hp;
		u_long hostaddr;

		if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
			goto Fail;

#if 1
		hostaddr = inet_addr (arg1);
		if (hostaddr != (u_long) - 1) {
			addr.sin_addr.s_addr = hostaddr;
#else
		if (isaddr (arg1)) {
			*((int32 *) & addr.sin_addr) = aton (arg1);
#endif
			addr.sin_family = AF_INET;
		} else {
			if ((hp = gethostbyname (arg1)) != NULL) {
				addr.sin_family = (short) hp->h_addrtype;
#if defined(h_addr)
				memcpy ((caddr_t) & addr.sin_addr, hp->h_addr_list[0], (unsigned) hp->h_length);
#else
				memcpy ((caddr_t) & addr.sin_addr, hp->h_addr, (unsigned) hp->h_length);
#endif
			} else {
				tprintf ("unknown host: %s\n", arg1);
				goto Fail;
			}
		}

		addr.sin_port = htons ((int16)atoi (arg2));

		if (connect (fd, (struct sockaddr *) &addr, sizeof (addr)) < 0)
			goto Fail;

		ap->uulock[0] = '\0';
		ap->fd = fd;

	} else {
#endif
		/* just in case the user forgot to create the lock dir */
		(void) mkdir (LOCKDIR, 0777);
		/* UUCP locking with ASCII pid */
		sprintf (ap->uulock, "%s/LCK..%s", LOCKDIR, arg1);
		if (Unixlockfile (ap->uulock, 1, arg1, 0) == -1)
			goto Fail;
		strcpy (filename, "/dev/");
		strcat (filename, arg1);
		if ((fd = open (filename, O_RDWR | O_NONBLOCK | O_NOCTTY, 0644)) == -1) {
			tprintf ("Can't open port: %s\n", strerror (errno));
			goto Fail;
		}
#ifdef UNIX_SOCKETS
	}
#endif
	ap->flags = 0;
	ap->iface = ifp;
	sp = find_speed (speed);
	ap->speed = speed_table[sp].speed;
#ifdef UNIX_SOCKETS
	if (!atoi (arg2)) {	/* it's not efficient to do this twice...bfd */
#endif
		memset ((char *) &termios, 0, sizeof (termios));
		termios.c_iflag = IGNBRK | IGNPAR;
		termios.c_cflag = CS8 | CREAD | CLOCAL | speed_table[sp].flags;
		termios.c_cc[VTIME] = 2;
		ap->pktsize = triglevel & 255;
		if (ap->pktsize)
			termios.c_cc[VMIN] = triglevel & 255;
		if (cfsetispeed (&termios, speed_table[sp].flags) == -1) {
			tprintf ("Can't set speed: %s\n", strerror (errno));
			goto Fail;
		}
		if (cfsetospeed (&termios, speed_table[sp].flags) == -1) {
			tprintf ("Can't set speed: %s\n", strerror (errno));
			goto Fail;
		}
		if (tcsetattr (fd, TCSANOW, &termios) == -1) {
			tprintf ("warning: Can't configure port: %s\n", strerror (errno));
			/* goto Fail; *//* made this non-terminal for z8530 kernel driver */
		}
		/* security: port won't work until re-opened */
		if (filename[0])	{
			if ((ap->fd = open (filename, O_RDWR | O_NONBLOCK | O_NOCTTY, 0644)) == -1) {
				tprintf ("Can't reopen port: %s\n", strerror (errno));
				goto Fail;
			}
			close (fd);
		}

#ifdef UNIX_SOCKETS
	}
#endif
	/* new asy parameters, defaulted for now */
	ap->rxq = 1;		/* disable queueing */
	ap->txq = 1;
	ap->rxbuf = bufsize;
#ifdef POLLEDKISS
	ap->poller = NULL;
#endif

	/* clear statistics */
	ap->rxints = 0;
	ap->txints = 0;
	ap->rxchar = 0;
	ap->txchar = 0;
	ap->rxput = 0;
	ap->rxovq = 0;
	ap->rxblock = 0;
	ap->txget = 0;
	ap->txovq = 0;
	ap->txblock = 0;

	ifp->txproc = newproc (ifn = if_name (ifp, " tx"),
			       256, asy_tx, dev, ifp, NULL, 0);
	free (ifn);
	ap->rxproc = newproc (ifn = if_name (ifp, " hw"),
			      256, asy_input, dev, ifp, NULL, 0);
	free (ifn);

	ifp->txproc->ptype = ap->rxproc->ptype = PTYPE_IO;
	register_io (ap->fd, &ap->fd);

#ifdef POLLEDKISS
	if (polled) {
		/* Calculate the poll interval: some processing time +
		 * the packet duration for a mtu size packet.
		 */
		interval = (((long) ifp->mtu * 10000L) / speed);
		ap->poller = newproc (ifn = if_name (ifp, " poller"),
		   384, kiss_poller, ifp->xdev, (void *) interval, NULL, 0);
		ap->poller->ptype = PTYPE_IO;
		free (ifn);
	}
#endif

	return 0;

Fail:
	rflush ();		/* make sure the message gets out */
	if (fd != -1)
		close (fd);
	/* Unlock port */
	if (ap->uulock[0])
		unlink (ap->uulock);
	ap->uulock[0] = '\0';
	ap->iface = NULLIF;
	return -1;
}


/*---------------------------------------------------------------------------*/

int
asy_stop (struct iface *ifp)
{
register struct asy *ap;

	ap = &Asy[ifp->dev];

	if (ap->iface == NULLIF)
		return -1;	/* Not allocated */

	unregister_io (ap->fd);

#ifdef POLLEDKISS
	if (ap->poller)
		killproc (ap->poller);
#endif
	if (ifp->txproc)
		killproc (ifp->txproc);
	ifp->txproc = 0;

	if (ap->rxproc)
		killproc (ap->rxproc);
	ap->rxproc = 0;

	ap->iface = NULLIF;

	free_q (&ap->sndq);
	close (ap->fd);

	free_q (&ap->rcvq);

	if (ap->uulock[0])
		unlink (ap->uulock);
	ap->uulock[0] = '\0';

	return 0;
}


void
detach_all_asy ()
{
register struct asy *ap;

	for (ap = Asy; ap != Asy + ASY_MAX; ap++) {
		if (ap->iface == NULLIF)
			break;
		unregister_io (ap->fd);
		if (ap->iface->txproc)
			killproc (ap->iface->txproc);
		ap->iface->txproc = 0;
		if (ap->rxproc)
			killproc (ap->rxproc);
		ap->rxproc = 0;
		ap->iface = NULLIF;
		free_q (&ap->sndq);
		free_q (&ap->rcvq);
		close (ap->fd);
		if (ap->uulock[0])
			unlink (ap->uulock);
		ap->uulock[0] = '\0';
	}
}


/*---------------------------------------------------------------------------*/

/* Set asynch line speed */
int
asy_speed (int dev, long bps)
{
struct asy *asyp;
int sp;
struct termios termios;

	if (bps <= 0 || dev >= ASY_MAX)
		return -1;
	asyp = &Asy[dev];
	if (asyp->iface == NULLIF)
		return -1;
	if (bps == 0)
		return -1;
	sp = find_speed (bps);
	if (tcgetattr (asyp->fd, &termios))
		return -1;
	if (cfsetispeed (&termios, speed_table[sp].flags))
		return -1;
	if (cfsetospeed (&termios, speed_table[sp].flags))
		return -1;
#if 0	/* was !defined(linux) && !defined(BSD) && !defined(__FreeBSD__)	This is obsolete stuff! */
	termios.c_cflag &= ~CBAUD;
	termios.c_cflag |= speed_table[sp].flags;
#endif
	if (tcsetattr (asyp->fd, TCSANOW, &termios))
		return -1;
	asyp->speed = speed_table[sp].speed;
	return 0;
}


/* Set termios VMIN (packet size) */
static int
asy_vmin (int dev, long pktsize)
{
struct termios termios;
struct asy *asyp;

	if (pktsize < 0 || pktsize > 255 || dev >= ASY_MAX)
		return -1;
	if ((asyp = &Asy[dev])->iface == NULLIF)
		return -1;
	if (tcgetattr (asyp->fd, &termios))
		return -1;
	termios.c_cc[VMIN] = asyp->pktsize = pktsize & 255;
	if (tcsetattr (asyp->fd, TCSANOW, &termios))
		return -1;
	return 0;
}


/* Set or clear RTS/CTS flow control */
static int
asy_rts (int dev, unsigned long onoff)
{
struct termios termios;
struct asy *asyp;

	if ((asyp = &Asy[dev])->iface == NULLIF)
		return -1;
	if (tcgetattr (asyp->fd, &termios))
		return -1;
	/*
	 * note sense is reversed as per DOS version, except that we default to
	 * RTS/CTS being *off* for compatibility with earlier ALPHA versions
	 */
	if (onoff) {
		asyp->flags &= ~ASY_RTSCTS;
		termios.c_cflag &= ~((tcflag_t) TNOS_CRTSCTS);	/*lint !e40 */
	} else {
		asyp->flags |= ASY_RTSCTS;
		termios.c_cflag |= ((tcflag_t) TNOS_CRTSCTS);	/*lint !e40 */
	}
	if (tcsetattr (asyp->fd, TCSANOW, &termios))
		return -1;
	return 0;
}


/*---------------------------------------------------------------------------*/

/* Asynchronous line I/O control */
int32
asy_ioctl (struct iface * ifp, int cmd, int set, int32 val)
{
struct asy *ap = &Asy[ifp->dev];

	switch (cmd) {
		case PARAM_SPEED:
			if (set)
				(void) asy_speed (ifp->dev, val);
			return ap->speed;
		case PARAM_MIN:
			if (set)
				(void) asy_vmin (ifp->dev, val);
			return ap->pktsize;
		case PARAM_RTS:
			if (set)
				(void) asy_rts (ifp->dev, (unsigned long) val);
			return (ap->flags & ASY_RTSCTS) == 0;
		default:
			break;
	}
	return -1;
}


/*---------------------------------------------------------------------------*/

static void
asy_input (int dev, void *arg1 OPTIONAL, void *arg2 OPTIONAL)
{
struct timeval tv;
struct asy *ap;
fd_set fds;
char *buf;
int i, c;

	server_disconnect_io ();
	ap = &Asy[dev];
	for (;;) {
		if (kwait (&ap->fd) != 0)
			return;
		ap->rxints++;
		buf = mallocw (ap->rxbuf);

		if (ap->pktsize)
			(void) fcntl (ap->fd, F_SETFL, fcntl (ap->fd, F_GETFL, 0) & ~O_NONBLOCK);
		if ((i = read (ap->fd, buf, ap->rxbuf)) == 0 || (!ap->pktsize && i == -1 && errno == EWOULDBLOCK)) {
			if (ap->pktsize)
				(void) fcntl (ap->fd, F_SETFL, fcntl (ap->fd, F_GETFL, 0) | O_NONBLOCK);
			ap->rxblock++;
			free (buf);
			continue;
		}

		if (i == -1) {
			if (errno == EINTR || errno == ECHILD) {
#if 0
				tprintf ("asy_input(%d): read interrupted, retrying.\n", dev);
#endif
				continue;
			}
			tprintf ("asy_input(%d): read error %d, shutting down\n", dev,
				 errno);
			if (ap->pktsize)
				(void) fcntl (ap->fd, F_SETFL, fcntl (ap->fd, F_GETFL, 0) | O_NONBLOCK);
			free (buf);
			return;
		}
		if ((c = ap->rxq) <= 0)
			c = 1;
		while (i > 0 && c > 0) {
			ap->rxchar += (uint32) i;
			ap->rxput++;
			enqueue (&ap->rcvq, qdata ((unsigned char *) buf, (int16) i));
			c--;
			if (ap->pktsize) {
				/* can't just read to check for data, since it might block */
#ifndef _lint
				FD_ZERO (&fds);
				FD_SET (ap->fd, &fds);
#endif
				tv.tv_sec = tv.tv_usec = 0;
				if (select (FD_SETSIZE, &fds, 0, 0, &tv) == 0) {
					i = -1;
					errno = EWOULDBLOCK;
					break;
				}
			}
			i = read (ap->fd, buf, ap->rxbuf);
		}
		free (buf);
		if (i == -1 && (errno == EINTR || errno == ECHILD)) {
#if 0
			tprintf ("asy_input(%d): read interrupted, retrying.\n", dev);
#endif
			continue;
		}
		if (i == -1 && errno != EWOULDBLOCK) {
			tprintf ("asy_input(%d): read error %d, shutting down\n", dev,
				 errno);
			return;
		}
		if (ap->pktsize)
			(void) fcntl (ap->fd, F_SETFL, fcntl (ap->fd, F_GETFL, 0) | O_NONBLOCK);
		if (c < 1 && ap->rxq > 1)
			ap->rxovq++;
	}
}


int
get_asy (int dev)
{
#if 0
struct asy *ap;

	ap = &Asy[dev];
	if (ap->iface == NULLIF)
		return -1;
	while (!ap->rcvq) {
		if (kwait (&ap->rcvq) != 0)
			return -1;	/* may not be dead, e.g. alarm in dialer */
	}
	ap = &Asy[dev];
	return PULLCHAR (&ap->rcvq);
#else
	if (Asy[dev].iface == NULLIF)
		return -1;
	if (!Asy[dev].rcvq)	{
		do	{
			if (kwait (&Asy[dev].rcvq) != 0)
				return -1;	/* may not be dead, e.g. alarm in dialer */
		
		} while (!Asy[dev].rcvq);
	}
	return PULLCHAR (&Asy[dev].rcvq);

#endif

}


/*---------------------------------------------------------------------------*/

void
pasy (struct asy *asyp)
{
	tprintf ("%s: %lu bps, ", asyp->iface->name, asyp->speed);
	if (asyp->pktsize)
		tprintf ("packet size %d", asyp->pktsize);
	else
		tputs ("non-blocking");
	tprintf (", RTS/CTS %sabled", (asyp->flags & ASY_RTSCTS ? "en" : "dis"));
#if 0
	tprintf (", carrier %sabled", (asyp->flags & ASY_CARR ? "en" : "dis"));
#endif
	tprintf ("\n  RX: ints %lu chars %lu puts %lu buf %d rxqueue %d qlen %d ovq "
		 "%ld block %ld\n",
	    asyp->rxints, asyp->rxchar, asyp->rxput, asyp->rxbuf, asyp->rxq,
		 len_q (asyp->rcvq), asyp->rxovq, asyp->rxblock);
	tprintf ("  TX: ints %lu gets %lu chars %lu txqueue %d qlen %d ovq %ld "
		 "block %ld\n",
		 asyp->txints, asyp->txget, asyp->txchar, asyp->txq,
		 len_q (asyp->sndq), asyp->txovq, asyp->txblock);
}


/*---------------------------------------------------------------------------*/

/* Serial transmit process, common to all protocols */

/*
 * Yes, this badly needs to be rewritten.
 */

static void
asy_tx (int dev, void *p1 OPTIONAL, void *p2 OPTIONAL)
{
register struct mbuf *bp;
struct asy *asyp;
int c, l, off;

	server_disconnect_io ();
	asyp = &Asy[dev];
	if ((c = asyp->txq) <= 0)
		c = 1;
	for (;;) {
		while (asyp->sndq == NULLBUF) {
			c = asyp->txq;
			if (!c)
				c = 1;
			if (kwait (&asyp->sndq) != 0)
				return;
			asyp->txints++;
		}
		bp = dequeue (&asyp->sndq);
		asyp->txget++;
		off = 0;
		while (bp != NULLBUF) {
			l = write (asyp->fd, bp->data + off, (size_t) bp->cnt - (int16) off);
			if (l == -1 && (errno == EINTR || errno == ECHILD)) {
#if 0
				tprintf ("asy_tx(%d): write interrupted, retrying.\n", dev);
#endif
				continue;
			}
			if (l == -1 && errno != EWOULDBLOCK) {
				tprintf ("asy_tx(%d): write error %d, shutting down\n", dev,
					 errno);
				return;
			}
			if (l == -1)
				l = 0;
			asyp->txchar += (uint32) l;
			if (l == bp->cnt - off) {
				bp = free_mbuf (bp);
				off = 0;
			} else {
				asyp->txblock++;
				kwait (NULL);
				off += l;
			}
		}
		if (--c < 1 && asyp->txq > 1) {
			asyp->txovq++;
			kwait (NULL);
		}
	}
}


/*---------------------------------------------------------------------------*/

/* Send a message on the specified serial line */
int
asy_send (int dev, struct mbuf *bp)
{
struct asy *asyp;

	if (dev < 0 || dev >= ASY_MAX) {
		free_p (bp);
		return -1;
	}
	asyp = &Asy[dev];

	if (asyp->iface == NULLIF)
		free_p (bp);
	else
		enqueue (&asyp->sndq, bp);
	return 0;
}


/* stub, CD not enabled at present */

int
carrier_detect (int dev OPTIONAL)
{
	return 1;		/* assume always on, with CLOCAL it is! */
}


/*---------------------------------------------------------------------------*/

int
doasyconfig (int ac, char **av, void *p OPTIONAL)
{
struct iface *ip;
struct asy *ap;
int k;

	if (ac < 3)
		return 1;
	ip = if_lookup (av[1]);
	if (!ip) {
		tprintf ("Interface %s unknown\n", av[1]);
		return 1;
	}
	for (k = 0, ap = Asy; k < ASY_MAX; ap++) {
		if (ap->iface == ip)
			break;
	}
	if (k == ASY_MAX || !ap) {
		tprintf ("Interface %s not asy\n", av[1]);
		return 1;
	}
	return subcmd (AsyPcmds, ac - 1, av + 1, ap);
}


static int
doasy2stat (int ac OPTIONAL, char **av OPTIONAL, void *d)
{
	pasy ((struct asy *) d);
	return 0;
}


static int
dorxqueue (int ac, char **av, void *d)
{
	return setint (&((struct asy *) d)->rxq, "Receive queue size", ac, av);
}


static int
dotxqueue (int ac, char **av, void *d)
{
	return setint (&((struct asy *) d)->txq, "Transmit queue size", ac, av);
}


static int
dobufsize (int ac, char **av, void *d)
{
	return setuns (&((struct asy *) d)->rxbuf, "Receive buffer size", ac, av);
}

#endif		/* UNIX */
