OpenSS7
SS7 for the
Common Man
© Copyright 1997-2007 OpenSS7 Corporation All Rights Reserved.
Last modified: Sun, 03 Aug 2008 22:23:24 GMT
Home TopIndex FirstPrev Next LastMore Download Info FAQ Mail  Home -> Resources -> Browse Source -> /code/strxnet/src/lib/xnet.c
Quick Links

Download

SCTP

SIGTRAN

SS7

Hardware

STREAMS

Asterisk

Related

Package

Manual

FAQ

Browse Source

Applications

SS7 Stack

ISDN Stack

SIGTRAN Stack

VoIP Stack

MG Stack

SS7/ISDN Devices

IP Transport

Embedded Systems

Operating System

Resources

Packages

Sys Req

Download

Mailing Lists

Browse Source

CVS Archive

Bug Reports

Library

Hardware

Vendor Links

Home

Overview

Status

Documentation

Resources

About

News

Description: Code

File /code/strxnet/src/lib/xnet.c



#ident "@(#) xnet.c,v openss7-0_9_2_F(0.9.2.28) 2007/05/22 02:10:38"

static char const ident[] =
    "xnet.c,v openss7-0_9_2_F(0.9.2.28) 2007/05/22 02:10:38";

#define _XOPEN_SOURCE 600
#define _REENTRANT
#define _THREAD_SAFE

#if 0
#define __USE_UNIX98
#define __USE_XOPEN2K
#define __USE_GNU
#endif

#define _SC_T_DEFAULT_ADDRLEN	2
#define _SC_T_DEFAULT_CONNLEN	3
#define _SC_T_DEFAULT_DISCLEN	4
#define _SC_T_DEFAULT_OPTLEN	5
#define _SC_T_DEFAULT_DATALEN	6

#define _T_DEFAULT_ADDRLEN	128
#define _T_DEFAULT_CONNLEN	256
#define _T_DEFAULT_DISCLEN	256
#define _T_DEFAULT_OPTLEN	256
#define _T_DEFAULT_DATALEN	16384
#define _T_TIMEOUT		-1
#define _T_IOV_MAX		16

#define NEED_T_USCALAR_T 1

#include <stdlib.h>

#include "gettext.h"
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#include <sys/stropts.h>
#include <sys/poll.h>
#include <fcntl.h>

#if 0
#pragma weak getpmsg
#pragma weak putpmsg
#pragma weak getmsg
#pragma weak putmsg
#pragma weak isastream
#endif

#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#else
# ifdef HAVE_STDINT_H
#  include <stdint.h>
# endif
#endif

#ifndef __EXCEPTIONS
#define __EXCEPTIONS 1
#endif

#include <unistd.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <stropts.h>
#include <pthread.h>
#include <linux/limits.h>
#include <values.h>

#ifndef __P
#define __P(__prototype) __prototype
#endif

#include <xti.h>
#include <tihdr.h>
#include <timod.h>

#if defined __i386__ || defined __x86_64__ || defined __k8__
#define fastcall __attribute__((__regparm__(3)))
#else
#define fastcall
#endif

#define __hot __attribute__((section(".text.hot")))
#define __unlikely __attribute__((section(".text.unlikely")))

#if __GNUC__ < 3
#define inline inline fastcall __hot
#define noinline extern fastcall __unlikely
#else
#define inline inline __attribute__((always_inline)) fastcall __hot
#define noinline static __attribute__((noinline)) fastcall __unlikely
#endif

#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

#undef min
#define min(a, b) (a < b ? a : b)

struct __xnet_tsd {
	int terrno;
};

static pthread_once_t __xnet_tsd_once = PTHREAD_ONCE_INIT;

static pthread_key_t __xnet_tsd_key = 0;

static void
__xnet_tsd_free(void *buf)
{
	pthread_setspecific(__xnet_tsd_key, NULL);
	free(buf);
}

static void
__xnet_tsd_alloc(void)
{
	int ret;
	void *buf;

	ret = pthread_key_create(&__xnet_tsd_key, __xnet_tsd_free);
	buf = malloc(sizeof(struct __xnet_tsd));
	bzero(buf, sizeof(*buf));
	ret = pthread_setspecific(__xnet_tsd_key, buf);
	return;
}

static struct __xnet_tsd *
__xnet_get_tsd(void)
{
	pthread_once(&__xnet_tsd_once, __xnet_tsd_alloc);
	return (struct __xnet_tsd *) pthread_getspecific(__xnet_tsd_key);
};

int *
__xnet__t_errno(void)
{
	return &(__xnet_get_tsd()->terrno);
}

__asm__(".symver __xnet__t_errno,_t_errno@@XNET_1.0");

struct _t_user {
	pthread_rwlock_t lock;
	int refs;
	int event;
	int flags;
	int fflags;
	int gflags;
	int state;
	int statef;
	int prim;
	int qlen;
	int ocnt;
	u_int8_t moredat;
	u_int8_t moresdu;
	u_int8_t moreexp;
	u_int8_t moreedu;
	u_int8_t moremsg;
	int ctlmax;
	char *ctlbuf;
	int datmax;
	char *datbuf;
	uint token;
	struct strbuf ctrl;
	struct strbuf data;
	struct t_info info;
};

#define TUF_FLOW_NORM		01
#define TUF_FLOW_EXP		02
#define TUF_SYNC_REQUIRED	04
#define TUF_WACK_INFO		010
#define TUF_WACK_OPTMGMT	020
#define TUF_WACK_ADDR		040
#define TUF_WACK_CAPABILITY	0100
#define TUF_WACK_GETADDR	0200
#define TUF_WACK_OK		0400
#define TUF_WACK_BIND		01000
#define TUF_MORE_DATA		02000

#ifndef T_ACK
#define T_ACK (-2)
#endif

static struct _t_user *_t_fds[OPEN_MAX] = { NULL, };

#define TSF_UNBND	(1 << TS_UNBND		)
#define TSF_WACK_BREQ	(1 << TS_WACK_BREQ	)
#define TSF_WACK_UREQ	(1 << TS_WACK_UREQ	)
#define TSF_IDLE	(1 << TS_IDLE		)
#define TSF_WACK_OPTREQ	(1 << TS_WACK_OPTREQ	)
#define TSF_WACK_CREQ	(1 << TS_WACK_CREQ	)
#define TSF_WCON_CREQ	(1 << TS_WCON_CREQ	)
#define TSF_WRES_CIND	(1 << TS_WRES_CIND	)
#define TSF_WACK_CRES	(1 << TS_WACK_CRES	)
#define TSF_DATA_XFER	(1 << TS_DATA_XFER	)
#define TSF_WIND_ORDREL	(1 << TS_WIND_ORDREL	)
#define TSF_WREQ_ORDREL	(1 << TS_WREQ_ORDREL	)
#define TSF_WACK_DREQ6	(1 << TS_WACK_DREQ6	)
#define TSF_WACK_DREQ7	(1 << TS_WACK_DREQ7	)
#define TSF_WACK_DREQ9	(1 << TS_WACK_DREQ9	)
#define TSF_WACK_DREQ10	(1 << TS_WACK_DREQ10	)
#define TSF_WACK_DREQ11	(1 << TS_WACK_DREQ11	)

static inline void
__xnet_u_setstate_const(struct _t_user *user, const int state)
{
	user->statef = (1 << state);
	switch (state) {
	case TS_UNBND:
	case TS_WACK_BREQ:
		user->state = T_UNBND;
		break;
	case TS_WACK_UREQ:
	case TS_IDLE:
	case TS_WACK_CREQ:
		user->state = T_IDLE;
		break;
	case TS_WCON_CREQ:
	case TS_WACK_DREQ6:
		user->state = T_OUTCON;
		break;
	case TS_WRES_CIND:
	case TS_WACK_CRES:
	case TS_WACK_DREQ7:
		user->state = T_INCON;
		break;
	case TS_DATA_XFER:
	case TS_WACK_DREQ9:
		user->state = T_DATAXFER;
		break;
	case TS_WIND_ORDREL:
	case TS_WACK_DREQ10:
		user->state = T_OUTREL;
		break;
	case TS_WREQ_ORDREL:
	case TS_WACK_DREQ11:
		user->state = T_INREL;
		break;
	default:
		user->state = T_UNINIT;
	}
}

static void
__xnet_u_setstate(struct _t_user *user, int state)
{
	user->statef = (1 << state);
	switch (state) {
	case TS_UNBND:
	case TS_WACK_BREQ:
		user->state = T_UNBND;
		break;
	case TS_WACK_UREQ:
	case TS_IDLE:
	case TS_WACK_CREQ:
		user->state = T_IDLE;
		break;
	case TS_WCON_CREQ:
	case TS_WACK_DREQ6:
		user->state = T_OUTCON;
		break;
	case TS_WRES_CIND:
	case TS_WACK_CRES:
	case TS_WACK_DREQ7:
		user->state = T_INCON;
		break;
	case TS_DATA_XFER:
	case TS_WACK_DREQ9:
		user->state = T_DATAXFER;
		break;
	case TS_WIND_ORDREL:
	case TS_WACK_DREQ10:
		user->state = T_OUTREL;
		break;
	case TS_WREQ_ORDREL:
	case TS_WACK_DREQ11:
		user->state = T_INREL;
		break;
	default:
		user->state = T_UNINIT;
	}
}

static inline int
__xnet_u_setevent(struct _t_user *user, int prim, int flags)
{
	switch ((user->prim = prim)) {
	case -1:
		return (user->event = T_DATA);
	case T_CONN_IND:
		return (user->event = T_LISTEN);
	case T_CONN_CON:
		return (user->event = T_CONNECT);
	case T_DISCON_IND:
		return (user->event = T_DISCONNECT);
	case T_DATA_IND:
		return (user->event = T_DATA);
	case T_EXDATA_IND:
		return (user->event = T_EXDATA);
	case T_UNITDATA_IND:
		return (user->event = T_DATA);
	case T_UDERROR_IND:
		return (user->event = T_UDERR);
	case T_ORDREL_IND:
		return (user->event = T_ORDREL);
	case T_OPTDATA_IND:
		return (user->event = (flags & T_ODF_EX) ? T_EXDATA : T_DATA);
	default:
		return (user->event = 0);
	}
}

static void
__xnet_u_reset_event(struct _t_user *user)
{
	if (user->event != T_EXDATA || (!user->moresdu && !user->moredat)) {
		user->prim = 0;
		user->event = 0;
		user->ctrl.maxlen = user->ctlmax;
		user->ctrl.len = 0;
		user->ctrl.buf = user->ctlbuf;
		user->data.maxlen = user->datmax;
		user->data.len = 0;
		user->data.buf = user->datbuf;
		user->moredat = 0;
		user->moresdu = 0;
		user->moreexp = 0;
		user->moreedu = 0;
		user->moremsg = 0;
	} else {

		user->prim = 0;
		user->event = T_DATA;
		user->ctrl.maxlen = user->ctlmax;
		user->ctrl.len = 0;
		user->ctrl.buf = user->ctlbuf;
		user->data.maxlen = user->datmax;
		user->data.len = 0;
		user->data.buf = user->datbuf;
		user->moreexp = 0;
		user->moreedu = 0;
		user->moremsg = 0;
	}
}

static inline int
__xnet_u_max_addr(struct _t_user *user)
{
	return (user->info.addr == T_INFINITE
		? MAXINT : (user->info.addr >= 0 ? user->info.addr : 0));
}
static inline int
__xnet_u_max_options(struct _t_user *user)
{
	return (user->info.options == T_INFINITE
		? MAXINT : (user->info.options >= 0 ? user->info.options : 0));
}
static inline int
__xnet_u_max_tsdu(struct _t_user *user)
{
	return ((user->info.tsdu == T_INFINITE || user->info.tsdu == 0)
		? MAXINT : (user->info.tsdu >= 0 ? user->info.tsdu : 0));
}
static inline int
__xnet_u_max_etsdu(struct _t_user *user)
{
	return ((user->info.etsdu == T_INFINITE || user->info.etsdu == 0)
		? MAXINT : (user->info.etsdu >= 0 ? user->info.etsdu : 0));
}
static inline int
__xnet_u_max_connect(struct _t_user *user)
{
	return (user->info.connect == T_INFINITE
		? MAXINT : (user->info.connect >= 0 ? user->info.connect : 0));
}
static inline int
__xnet_u_max_discon(struct _t_user *user)
{
	return (user->info.discon == T_INFINITE
		? MAXINT : (user->info.discon >= 0 ? user->info.discon : 0));
}
static inline int
__xnet_u_max_tidu(struct _t_user *user)
{
	return (user->info.tidu == T_INFINITE
		? MAXINT : (user->info.tidu >= 0 ? user->info.tidu : 0));
}

static int __xnet_t_getmsg(int fd, struct strbuf *ctrl, struct strbuf *data, int *flagsp);
static int __xnet_t_putmsg(int fd, struct strbuf *ctrl, struct strbuf *data, int flags);
static int __xnet_t_putpmsg(int fd, struct strbuf *ctrl, struct strbuf *data, int band, int flags);
static int __xnet_t_ioctl(int fd, int cmd, void *arg);
static int __xnet_t_strioctl(int fd, int cmd, void *arg, size_t arglen);

#if 0
int __xnet_t_accept(int fd, int resfd, const struct t_call *call);
int __xnet_t_addleaf(int fd, int leafid, struct netbuf *addr);
char *__xnet_t_alloc(int fd, int type, int fields);
int __xnet_t_bind(int fd, const struct t_bind *req, struct t_bind *ret);
int __xnet_t_close(int fd);
int __xnet_t_connect(int fd, const struct t_call *sndcall, struct t_call *rcvcall);
int __xnet_t_free(void *ptr, int type);
int __xnet_t_getinfo(int fd, struct t_info *info);
int __xnet_t_getstate(int fd);
int __xnet_t_listen(int fd, struct t_call *call);
int __xnet_t_look(int fd);
int __xnet_t_open(const char *path, int oflag, struct t_info *info);
int __xnet_t_optmgmt(int fd, const struct t_optmgmt *req, struct t_optmgmt *ret);
int __xnet_t_rcv(int fd, char *buf, unsigned int nbytes, int *flags);
int __xnet_t_rcvconnect(int fd, struct t_call *call);
int __xnet_t_rcvdis(int fd, struct t_discon *discon);
int __xnet_t_rcvleafchange(int fd, struct t_leaf_status *change);
int __xnet_t_rcvrel(int fd);
int __xnet_t_rcvreldata(int fd, struct t_discon *discon);
int __xnet_t_rcvopt(int fd, struct t_unitdata *optdata, int *flags);
int __xnet_t_rcvudata(int fd, struct t_unitdata *unitdata, int *flags);
int __xnet_t_rcvv(int fd, struct t_iovec *iov, unsigned int iovcount, int *flags);
int __xnet_t_rcvvudata(int fd, struct t_unitdata *unitdata, struct t_iovec *iov,
		       unsigned int iovcount, int *flags);
int __xnet_t_removeleaf(int fd, int leafid, int reason);
int __xnet_t_snd(int fd, char *buf, unsigned int nbytes, int flags);
int __xnet_t_snddis(int fd, const struct t_call *call);
int __xnet_t_sndrel(int fd);
int __xnet_t_sndreldata(int fd, struct t_discon *discon);
int __xnet_t_sndopt(int fd, const struct t_unitdata *optdata, int flags);
int __xnet_t_sndvopt(int fd, const struct t_unitdata *optdata, const struct t_iovec *iov,
		     unsigned int iovcount, int flags);
int __xnet_t_sndudata(int fd, const struct t_unitdata *unitdata);
int __xnet_t_sndv(int fd, const struct t_iovec *iov, unsigned int iovcount, int flags);
int __xnet_t_sndvudata(int fd, struct t_unitdata *unitdata, struct t_iovec *iov,
		       unsigned int iovcount);
int __xnet_t_sysconf(int name);
int __xnet_t_unbind(int fd);
const char *__xnet_t_strerror(int errnum);
#endif

#if 0
int __xnet_t_accept_r(int fd, int resfd, const struct t_call *call);
int __xnet_t_addleaf_r(int fd, int leafid, struct netbuf *addr);
char *__xnet_t_alloc_r(int fd, int type, int fields);
int __xnet_t_bind_r(int fd, const struct t_bind *req, struct t_bind *ret);
int __xnet_t_close_r(int fd);
int __xnet_t_connect_r(int fd, const struct t_call *sndcall, struct t_call *rcvcall);
int __xnet_t_getinfo_r(int fd, struct t_info *info);
int __xnet_t_getstate_r(int fd);
int __xnet_t_listen_r(int fd, struct t_call *call);
int __xnet_t_look_r(int fd);
int __xnet_t_open_r(const char *path, int oflag, struct t_info *info);
int __xnet_t_optmgmt_r(int fd, const struct t_optmgmt *req, struct t_optmgmt *ret);
int __xnet_t_rcv_r(int fd, char *buf, unsigned int nbytes, int *flags);
int __xnet_t_rcvconnect_r(int fd, struct t_call *call);
int __xnet_t_rcvdis_r(int fd, struct t_discon *discon);
int __xnet_t_rcvleafchange_r(int fd, struct t_leaf_status *change);
int __xnet_t_rcvrel_r(int fd);
int __xnet_t_rcvreldata_r(int fd, struct t_discon *discon);
int __xnet_t_rcvopt_r(int fd, struct t_unitdata *optdata, int *flags);
int __xnet_t_rcvudata_r(int fd, struct t_unitdata *unitdata, int *flags);
int __xnet_t_rcvv_r(int fd, struct t_iovec *iov, unsigned int iovcount, int *flags);
int __xnet_t_rcvvudata_r(int fd, struct t_unitdata *unitdata, struct t_iovec *iov,
			 unsigned int iovcount, int *flags);
int __xnet_t_removeleaf_r(int fd, int leafid, int reason);
int __xnet_t_snd_r(int fd, char *buf, unsigned int nbytes, int flags);
int __xnet_t_snddis_r(int fd, const struct t_call *call);
int __xnet_t_sndrel_r(int fd);
int __xnet_t_sndreldata_r(int fd, struct t_discon *discon);
int __xnet_t_sndopt_r(int fd, const struct t_unitdata *optdata, int flags);
int __xnet_t_sndvopt_r(int fd, const struct t_unitdata *optdata, const struct t_iovec *iov,
		       unsigned int iovcount, int flags);
int __xnet_t_sndudata_r(int fd, const struct t_unitdata *unitdata);
int __xnet_t_sndv_r(int fd, const struct t_iovec *iov, unsigned int iovcount, int flags);
int __xnet_t_sndvudata_r(int fd, struct t_unitdata *unitdata, struct t_iovec *iov,
			 unsigned int iovcount);
int __xnet_t_unbind_r(int fd);
#endif

static int
__xnet_t_getmsg(int fd, struct strbuf *ctrl, struct strbuf *data, int *flagsp)
{
	int ret;

	if ((ret = getmsg(fd, ctrl, data, flagsp)) >= 0)
		return (ret);
	t_errno = TSYSERR;
	switch (errno) {
	case EISDIR:
	case EBADF:
		t_errno = TBADF;
		break;
	case EFAULT:
	case ENODEV:
	case ENOSTR:
	case EIO:
	case EINVAL:
		break;
	case EAGAIN:
		t_errno = TNODATA;
		return (0);
	case EINTR:
	case ENOSR:
	case EBADMSG:
		break;
	}
	return (-1);
}

static __hot int
__xnet_t_putmsg(int fd, struct strbuf *ctrl, struct strbuf *data, int flags)
{
	int ret;

	if ((ret = putmsg(fd, ctrl, data, flags)) >= 0)
		return (ret);
	t_errno = TSYSERR;
	switch (errno) {
	case EAGAIN:
		t_errno = TFLOW;
		break;
	case EISDIR:
	case EBADF:
		t_errno = TBADF;
		break;
	case EFAULT:
	case EINTR:
	case EINVAL:
	case EIO:
	case ENODEV:
	case ENOSR:
	case ENOSTR:
	case ENXIO:
	case ERANGE:
		break;
	}
	return (-1);
}

static int
__xnet_t_putpmsg(int fd, struct strbuf *ctrl, struct strbuf *data, int band, int flags)
{
	int ret;

	if ((ret = putpmsg(fd, ctrl, data, band, flags)) != -1)
		return (ret);
	t_errno = TSYSERR;
	switch (errno) {
	case EAGAIN:
		t_errno = TFLOW;
		break;
	case EISDIR:
	case EBADF:
		t_errno = TBADF;
		break;
	case EFAULT:
	case EINTR:
	case EINVAL:
	case EIO:
	case ENODEV:
	case ENOSR:
	case ENOSTR:
	case ENXIO:
	case ERANGE:
		break;
	}
	return (-1);
}

static __hot int
__xnet_t_getdata(int fd, struct strbuf *udata, int expect)
{
	struct _t_user *user = _t_fds[fd];
	int ret, flag = 0;
	union T_primitives *p = (typeof(p)) user->ctlbuf;

	if (likely(user->event == T_DATA) || likely(user->event == T_EXDATA)) {
		if (unlikely(!(user->event & expect)))
			goto done;
		if (unlikely(user->data.len < 1))
			goto getmoredata;
		udata->len = min(udata->maxlen, user->data.len);
		memcpy(udata->buf, user->data.buf, udata->len);
		user->data.buf += udata->len;
		user->data.len -= udata->len;
	} else if (user->event == 0) {
		__xnet_u_reset_event(user);
	      getmoredata:
		user->data.maxlen = min(udata->maxlen, user->datmax);
		user->data.len = 0;
		user->data.buf = udata->buf;
		if ((ret = __xnet_t_getmsg(fd, &user->ctrl, &user->data, &flag)) < 0)
			goto error;
		if (user->ctrl.len < 0)
			user->ctrl.len = 0;
		if (user->data.len < 0)
			user->data.len = 0;
		udata->len = user->data.len;
		if (ret & MORECTL)
			goto cleanup;
		if (flag != 0 || flag == RS_HIPRI)
			goto tsync;
		if (user->ctrl.len || user->data.len) {
			if (user->event == 0 || user->ctrl.len > 0)
				__xnet_u_setevent(user, user->ctrl.len ? p->type : -1, 0);

			switch (user->prim) {
			case -1:
				user->moredat = ((ret & MOREDATA) != 0);
				break;
			case T_DATA_IND:
				user->moresdu = (p->data_ind.MORE_flag != 0);
				user->moredat = ((ret & MOREDATA) != 0);
				break;
			case T_EXDATA_IND:
				user->moreedu = (p->exdata_ind.MORE_flag != 0);
				user->moreexp = ((ret & MOREDATA) != 0);
				break;
			case T_OPTDATA_IND:
				if (p->optdata_ind.DATA_flag & T_ODF_EX) {
					user->moreedu =
					    ((p->optdata_ind.DATA_flag & T_ODF_MORE) != 0);
					user->moreexp = ((ret & MOREDATA) != 0);
				} else {
					user->moresdu =
					    ((p->optdata_ind.DATA_flag & T_ODF_MORE) != 0);
					user->moredat = ((ret & MOREDATA) != 0);
				}
				break;
			case T_UNITDATA_IND:
				user->moresdu = 0;
				user->moredat = ((ret & MOREDATA) != 0);
				user->moreedu = 0;
				user->moreexp = 0;
				break;
			case T_CONN_IND:
			case T_CONN_CON:
			case T_DISCON_IND:
			case T_ORDREL_IND:
			case T_UDERROR_IND:
				user->moremsg = ((ret & MOREDATA) != 0);
				break;
			}
			if ((user->event & expect))
				user->data.len = 0;
			else {

				if (user->data.len > 0)
					memcpy(user->datbuf, user->data.buf, user->data.len);
				user->data.buf = user->datbuf;
				udata->len = 0;
			}
		}
	} else
		goto tlook;
      done:
	return (user->event);
      cleanup:
	while (ret != -1 && (ret & (MORECTL | MOREDATA)))
		ret = __xnet_t_getmsg(fd, &user->ctrl, &user->data, &flag);
	goto tsync;
      tlook:
	t_errno = TLOOK;
	goto error;
      tsync:
	__xnet_u_reset_event(user);
	user->flags |= TUF_SYNC_REQUIRED;
	goto tproto;
      tproto:
	t_errno = TPROTO;
	goto error;
      error:
	return (-1);
}

static int
__xnet_t_getevent(int fd)
{
	struct _t_user *user = _t_fds[fd];
	int ret, flag = 0;
	union T_primitives *p = (typeof(p)) user->ctlbuf;

	switch (user->event) {
	case 0:
		__xnet_u_reset_event(user);
		if ((ret = __xnet_t_getmsg(fd, &user->ctrl, &user->data, &flag)) < 0)
			goto error;
		if (ret & MORECTL)
			goto cleanup;
		if (flag != 0 || flag == RS_HIPRI)
			goto tsync;
		if (user->ctrl.len || user->data.len) {
			__xnet_u_setevent(user, user->ctrl.len ? p->type : -1, 0);

			switch (user->prim) {
			case -1:
				user->moredat = ((ret & MOREDATA) != 0);
				break;
			case T_DATA_IND:
				user->moresdu = (p->data_ind.MORE_flag != 0);
				user->moredat = ((ret & MOREDATA) != 0);
				break;
			case T_EXDATA_IND:
				user->moreedu = (p->exdata_ind.MORE_flag != 0);
				user->moreexp = ((ret & MOREDATA) != 0);
				break;
			case T_OPTDATA_IND:
				if (p->optdata_ind.DATA_flag & T_ODF_EX) {
					user->moreedu =
					    ((p->optdata_ind.DATA_flag & T_ODF_MORE) != 0);
					user->moreexp = ((ret & MOREDATA) != 0);
				} else {
					user->moresdu =
					    ((p->optdata_ind.DATA_flag & T_ODF_MORE) != 0);
					user->moredat = ((ret & MOREDATA) != 0);
				}
				break;
			case T_UNITDATA_IND:
				user->moresdu = 0;
				user->moredat = ((ret & MOREDATA) != 0);
				user->moreedu = 0;
				user->moreexp = 0;
				break;
			case T_CONN_IND:
			case T_CONN_CON:
			case T_DISCON_IND:
			case T_ORDREL_IND:
			case T_UDERROR_IND:
				user->moremsg = ((ret & MOREDATA) != 0);
				break;
			}
		}
	}
	return (user->event);
      cleanup:
	while (ret != -1 && (ret & (MORECTL | MOREDATA)))
		ret = __xnet_t_getmsg(fd, &user->ctrl, &user->data, &flag);
	goto tsync;
      tsync:
	__xnet_u_reset_event(user);
	user->flags |= TUF_SYNC_REQUIRED;
	goto tproto;
      tproto:
	t_errno = TPROTO;
	goto error;
      error:
	return (-1);
}

static pthread_rwlock_t __xnet_fd_lock = PTHREAD_RWLOCK_INITIALIZER;

static inline int
__xnet_lock_rdlock(pthread_rwlock_t * rwlock)
{
	return pthread_rwlock_rdlock(rwlock);
}
static inline int
__xnet_lock_wrlock(pthread_rwlock_t * rwlock)
{
	return pthread_rwlock_wrlock(rwlock);
}
static inline void
__xnet_lock_unlock(void *rwlock)
{
	pthread_rwlock_unlock(rwlock);
}

static inline int
__xnet_list_rdlock(void)
{
	return __xnet_lock_rdlock(&__xnet_fd_lock);
}
static int
__xnet_list_wrlock(void)
{
	return __xnet_lock_wrlock(&__xnet_fd_lock);
}
static void
__xnet_list_unlock(void *ignore)
{
	return __xnet_lock_unlock(&__xnet_fd_lock);
}

#if 0
static int
__xnet_user_rdlock(struct _t_user *user)
{
	return __xnet_lock_rdlock(&user->lock);
}
#endif
static inline int
__xnet_user_wrlock(struct _t_user *user)
{
	return __xnet_lock_wrlock(&user->lock);
}
static inline void
__xnet_user_unlock(struct _t_user *user)
{
	return __xnet_lock_unlock(&user->lock);
}

static void
__xnet_t_putuser(void *arg)
{
	int fd = *(int *) arg;
	struct _t_user *user = _t_fds[fd];

	__xnet_user_unlock(user);
	__xnet_list_unlock(NULL);
	return;
}

static __hot struct _t_user *
__xnet_t_getuser(int fd)
{
	struct _t_user *user;
	int err;

	if (unlikely((err = __xnet_list_rdlock())))
		goto list_lock_error;
	if (unlikely(0 > fd) || unlikely(fd >= OPEN_MAX))
		goto tbadf;
	if (unlikely(!(user = _t_fds[fd])))
		goto tbadf;
	if (unlikely((err = __xnet_user_wrlock(user))))
		goto user_lock_error;
	return (user);
      user_lock_error:
	t_errno = TSYSERR;
	errno = err;
	__xnet_list_unlock(NULL);
	goto error;
      tbadf:
	t_errno = TBADF;
	goto error;
      list_lock_error:
	t_errno = TSYSERR;
	errno = err;
	goto error;
      error:
	return (NULL);
}

static __hot struct _t_user *
__xnet_t_tstuser(int fd, const int expect, const int servtype, const int states)
{
	struct _t_user *user;

	if (unlikely(0 > fd) || unlikely(fd >= OPEN_MAX))
		goto tbadf;
	if (unlikely(!(user = _t_fds[fd])))
		goto tbadf;
	if (unlikely(user->flags & TUF_SYNC_REQUIRED))
		goto tproto;
	if (unlikely(user->event && user->event != expect && expect != -1))
		goto tlook;
#ifndef CONFIG_XTI_IS_TYPELESS
	if (unlikely(!((1 << user->info.servtype) & servtype)))
		goto tnotsupport;
#endif
#ifndef CONFIG_XTI_IS_STATELESS
	if (unlikely(!(user->statef & states)))
		goto toutstate;
#endif
	return (user);
#ifndef CONFIG_XTI_IS_STATELESS
      toutstate:
	t_errno = TOUTSTATE;
	goto error;
#endif
#ifndef CONFIG_XTI_IS_TYPELESS
      tnotsupport:
	t_errno = TNOTSUPPORT;
	goto error;
#endif
      tlook:
	t_errno = TLOOK;
	goto error;
      tproto:
	t_errno = TPROTO;
	goto error;
      tbadf:
	t_errno = TBADF;
	goto error;
      error:
	return (NULL);
}

static int
__xnet_t_ioctl(int fd, int cmd, void *arg)
{
	int ret;

	switch ((ret = ioctl(fd, cmd, arg))) {
	case 0:
		return (0);
	case -1:
		switch (errno) {
		case EBADF:
		case ENOTTY:
		case EINVAL:
		case EPERM:
			t_errno = TBADF;
			break;
		default:
			t_errno = TSYSERR;
			break;
		}
		return (-1);
	default:
		if ((t_errno = ret & 0x00ff) == TSYSERR)
			if (!(errno = (ret >> 8) & 0x00ff))
				errno = EINVAL;
		return (-1);
	}
}

static int
__xnet_t_strioctl(int fd, int cmd, void *arg, size_t arglen)
{
	struct strioctl ioc;

	ioc.ic_cmd = cmd;
	ioc.ic_timout = _T_TIMEOUT;
	ioc.ic_len = arglen;
	ioc.ic_dp = arg;
	return __xnet_t_ioctl(fd, I_STR, &ioc);
}

int
__xnet_t_accept(int fd, int resfd, const struct t_call *call)
{
	struct _t_user *user, *resuser;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS) | (1 << T_COTS_ORD), TSF_WRES_CIND)))
		goto error;
	if (fd == resfd) {
		resuser = user;
		if (user->ocnt > 1)
			goto tindout;
		if (user->ocnt < 1)
			goto tproto;
	} else
	    if (!(resuser = __xnet_t_tstuser(resfd, 0, (1 << T_COTS) | (1 << T_COTS_ORD),
					     TSF_UNBND | TSF_IDLE)))
		goto error;
	if (__xnet_t_look(fd) > 0)
		goto tlook;
	if (__xnet_t_look(resfd) > 0)
		goto tlook;
#ifdef DEBUG
	if (!call)
		goto einval;
	if (call->addr.len < 0 || (call->addr.len > 0 && !call->addr.buf))
		goto einval;
	if (call->opt.len < 0 || (call->opt.len > 0 && !call->opt.buf))
		goto einval;
	if (call->udata.len < 0 || (call->udata.len > 0 && !call->udata.buf))
		goto einval;
#endif
	if (call && call->addr.len > __xnet_u_max_addr(user))
		goto tbadaddr;
	if (call && call->opt.len > __xnet_u_max_options(user))
		goto tbadopt;
	if (call && call->udata.len > __xnet_u_max_connect(user))
		goto tbaddata;

	if (resuser->statef & TSF_UNBND) {
		size_t add_len = (call && call->addr.len > 0) ? call->addr.len : 0;
		struct {
			struct T_bind_req prim;
			unsigned char addr[add_len];
		} req;

		req.prim.PRIM_type = T_BIND_REQ;
		req.prim.ADDR_length = add_len;
		req.prim.ADDR_offset = add_len ? sizeof(req.prim) : 0;
		req.prim.CONIND_number = 0;
		if (add_len)
			memcpy(req.addr, call->addr.buf, add_len);
		if (__xnet_t_strioctl(resfd, TI_BIND, &req, sizeof(req)) != 0)
			goto error;
		__xnet_u_setstate_const(resuser, TS_IDLE);
	}

	{
		size_t opt_len = (call && call->opt.len > 0) ? call->opt.len : 0;
		size_t dat_len = (call && call->udata.len > 0) ? call->udata.len : 0;
		struct {
			struct T_conn_res prim;
			unsigned char opt[opt_len];
			unsigned char udata[dat_len];
		} req;

		req.prim.PRIM_type = T_CONN_RES;
		req.prim.ACCEPTOR_id = resuser->token;
		req.prim.OPT_length = opt_len;
		req.prim.OPT_offset = opt_len ? sizeof(req.prim) : 0;
		req.prim.SEQ_number = call ? call->sequence : 0;
		if (opt_len)
			memcpy(req.opt, call->opt.buf, opt_len);
		if (dat_len > 0)
			memcpy(req.opt + opt_len, call->udata.buf, dat_len);
		if (__xnet_t_strioctl(fd, TI_SETMYNAME, &req, sizeof(req)) != 0)
			goto error;
		__xnet_u_setstate_const(resuser, TS_DATA_XFER);
		if (user->ocnt && !--user->ocnt)
			if (user != resuser)
				__xnet_u_setstate_const(user, TS_IDLE);
	}
	return (0);
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tbadaddr:
	t_errno = TBADADDR;
	goto error;
      tbadopt:
	t_errno = TBADOPT;
	goto error;
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      tproto:
	t_errno = TPROTO;
	goto error;
      tlook:
	t_errno = TLOOK;
	goto error;
      tindout:
	t_errno = TINDOUT;
	goto error;
      error:
	if (t_errno != TLOOK && (__xnet_t_look(fd) > 0 || __xnet_t_look(resfd) > 0))
		goto tlook;
	return (-1);
}

int
__xnet_t_accept_r(int fd, int resfd, const struct t_call *call)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_accept(fd, resfd, call);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_accept_r,t_accept@@XNET_1.0");

int
__xnet_t_addleaf(int fd, int leafid, struct netbuf *addr)
{
#if defined HAVE_XTI_ATM_H
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS) | (1 << T_COTS_ORD), TSF_DATA_XFER)))
		goto error;
#ifdef DEBUG
	if (!addr)
		goto einval;
	if (addr->len < 0 || (addr->len > 0 && !addr->buf))
		goto einval;
#endif
	if (!addr || addr->len < sizeof(struct t_atm_addr))
		goto tbadaddr;
	{
		struct {
			struct t_opthdr hdr;
			struct t_atm_add_leaf leaf;
		} opts;
		struct t_optmgmt req, ret;

		req.opt.maxlen = 0;
		req.opt.len = sizeof(opts);
		req.opt.buf = (char *) &opts;
		req.flags = T_CURRENT;
		ret.opt.maxlen = sizeof(opts);
		ret.opt.len = 0;
		ret.opt.buf = (char *) &opts;
		ret.flags = 0;
		opts.hdr.len = sizeof(opts);
		opts.hdr.level = T_ATM_SIGNALLING;
		opts.hdr.name = T_ATM_ADD_LEAF;
		opts.hdr.status = 0;
		opts.leaf.leaf_ID = leafid;
		memcpy(&opt.leaf.leaf_address, addr->buf, addr->len);
		if (__xnet_t_optmgmt(fd, &req, &ret) == -1)
			goto error;
		if (ret.opt.flags == T_FAILURE)
			goto tproto;
		if (ret.opt.len < sizeof(opts) || opts.hdr.len < sizeof(opts))
			goto tproto;
		if (opts.hdr.status != T_SUCCESS)
			goto tproto;
		if (opts.hdr.level != T_ATM_SIGNALLING || opts.hdr.name != T_ATM_ADD_LEAF)
			goto tproto;

		goto tnodata;
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tnodata:
	t_errno = TNODATA;
	goto error;
      tbadaddr:
	t_errno = TBADADDR;
	goto error;
      error:
	return (-1);
#else
	t_errno = TNOTSUPPORT;
	return (-1);
#endif
}

#pragma weak __xnet_t_addleaf_r

int
__xnet_t_addleaf_r(int fd, int leafid, struct netbuf *addr)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_addleaf(fd, leafid, addr);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_addleaf_r,t_addleaf@@XNET_1.0");

char *
__xnet_t_alloc(int fd, int type, int fields)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, -1, -1)))
		goto error;
	switch (type) {
	case T_BIND:
	{
		struct t_bind *bind;

		if (!(bind = (struct t_bind *) malloc(sizeof(*bind))))
			goto badalloc;
		memset(bind, 0, sizeof(*bind));
		if (fields & T_ADDR) {
			int len;

			switch ((len = user->info.addr)) {
			case T_INFINITE:
				len = _T_DEFAULT_ADDRLEN;
			default:
				if (!(bind->addr.buf = (char *) malloc(len))) {
					free(bind);
					goto badalloc;
				}
				bind->addr.maxlen = len;
			case 0:
				break;
			case T_INVALID:
				if (fields != T_ALL) {
					free(bind);
					goto einval;
				}
				break;
			}
		}
		return ((char *) bind);
	}
	case T_OPTMGMT:
	{
		struct t_optmgmt *opts;

		if (!(opts = (struct t_optmgmt *) malloc(sizeof(*opts))))
			goto badalloc;
		memset(opts, 0, sizeof(*opts));
		if (fields & T_OPT) {
			int len;

			switch ((len = user->info.options)) {
			case T_INFINITE:
				len = _T_DEFAULT_OPTLEN;
			default:
				if (!(opts->opt.buf = (char *) malloc(len))) {
					free(opts);
					goto badalloc;
				}
				opts->opt.maxlen = len;
			case 0:
				break;
			case T_INVALID:
				if (fields != T_ALL) {
					free(opts);
					goto einval;
				}
				break;
			}
		}
		return ((char *) opts);
	}
	case T_CALL:
	{
		struct t_call *call;

#ifndef CONFIG_XTI_IS_TYPELESS
		if (user->info.servtype == T_CLTS)
			goto tnostructype;
#endif
		if (!(call = (struct t_call *) malloc(sizeof(*call))))
			goto badalloc;
		memset(call, 0, sizeof(*call));
		if (fields & T_ADDR) {
			int len;

			switch ((len = user->info.addr)) {
			case T_INFINITE:
				len = _T_DEFAULT_ADDRLEN;
			default:
				if (!(call->addr.buf = (char *) malloc(len))) {
					free(call);
					goto badalloc;
				}
				call->addr.maxlen = len;
			case 0:
				break;
			case T_INVALID:
				if (fields != T_ALL) {
					free(call);
					goto einval;
				}
				break;
			}
		}
		if (fields & T_OPT) {
			int len;

			switch ((len = user->info.options)) {
			case T_INFINITE:
				len = _T_DEFAULT_OPTLEN;
			default:
				if (!(call->opt.buf = (char *) malloc(len))) {
					if (call->addr.buf)
						free(call->addr.buf);
					free(call);
					goto badalloc;
				}
				call->opt.maxlen = len;
			case 0:
				break;
			case T_INVALID:
				if (fields != T_ALL) {
					if (call->addr.buf)
						free(call->addr.buf);
					free(call);
					goto einval;
				}
				break;
			}
		}
		if (fields & T_UDATA) {
			int len;

			switch ((len = user->info.connect)) {
			case T_INFINITE:
				len = _T_DEFAULT_CONNLEN;
			default:
				if (!(call->udata.buf = (char *) malloc(len))) {
					if (call->addr.buf)
						free(call->addr.buf);
					if (call->opt.buf)
						free(call->opt.buf);
					free(call);
					goto badalloc;
				}
				call->udata.maxlen = len;
			case 0:
				break;
			case T_INVALID:
				if (fields != T_ALL) {
					if (call->addr.buf)
						free(call->addr.buf);
					if (call->opt.buf)
						free(call->opt.buf);
					free(call);
					goto einval;
				}
				break;
			}
		}
		return ((char *) call);
	}
	case T_DIS:
	{
		struct t_discon *discon;

#ifndef CONFIG_XTI_IS_TYPELESS
		if (user->info.servtype == T_CLTS)
			goto tnostructype;
#endif
		if (!(discon = (struct t_discon *) malloc(sizeof(*discon))))
			goto badalloc;
		memset(discon, 0, sizeof(*discon));
		if (fields & T_UDATA) {
			int len;

			switch ((len = user->info.discon)) {
			case T_INFINITE:
				len = _T_DEFAULT_DISCLEN;;
			default:
				if (!(discon->udata.buf = (char *) malloc(len))) {
					free(discon);
					goto badalloc;
				}
				discon->udata.maxlen = len;
			case 0:
				break;
			case T_INVALID:
				if (fields != T_ALL) {
					free(discon);
					goto einval;
				}
				break;
			}
		}
		return ((char *) discon);
	}
	case T_UNITDATA:
	{
		struct t_unitdata *udata;

#ifndef CONFIG_XTI_IS_TYPELESS
		if (user->info.servtype != T_CLTS)
			goto tnostructype;
#endif
		if (!(udata = (struct t_unitdata *) malloc(sizeof(*udata))))
			goto badalloc;
		memset(udata, 0, sizeof(*udata));
		if (fields & T_ADDR) {
			int len;

			switch ((len = user->info.addr)) {
			case T_INFINITE:
				len = _T_DEFAULT_ADDRLEN;
			default:
				if (!(udata->addr.buf = (char *) malloc(len))) {
					free(udata);
					goto badalloc;
				}
				udata->addr.maxlen = len;
			case 0:
				break;
			case T_INVALID:
				if (fields != T_ALL) {
					free(udata);
					goto einval;
				}
				break;
			}
		}
		if (fields & T_OPT) {
			int len;

			switch ((len = user->info.options)) {
			case T_INFINITE:
				len = _T_DEFAULT_OPTLEN;
			default:
				if (!(udata->opt.buf = (char *) malloc(len))) {
					if (udata->addr.buf)
						free(udata->addr.buf);
					free(udata);
					goto badalloc;
				}
				udata->opt.maxlen = len;
			case 0:
				break;
			case T_INVALID:
				if (fields != T_ALL) {
					if (udata->addr.buf)
						free(udata->addr.buf);
					free(udata);
					goto einval;
				}
				break;
			}
		}
		if (fields & T_UDATA) {
			int len;

			switch ((len = user->info.tsdu)) {
			case T_INFINITE:
			case 0:
				len = _T_DEFAULT_DATALEN;
			default:
				if (!(udata->udata.buf = (char *) malloc(len))) {
					if (udata->addr.buf)
						free(udata->addr.buf);
					if (udata->opt.buf)
						free(udata->opt.buf);
					free(udata);
					goto badalloc;
				}
				udata->udata.maxlen = len;
				break;
			case T_INVALID:
				if (fields != T_ALL) {
					if (udata->addr.buf)
						free(udata->addr.buf);
					if (udata->opt.buf)
						free(udata->opt.buf);
					free(udata);
					goto einval;
				}
				break;
			}
		}
		return ((char *) udata);
	}
	case T_UDERROR:
	{
		struct t_uderr *uderr;

#ifndef CONFIG_XTI_IS_TYPELESS
		if (user->info.servtype != T_CLTS)
			goto tnostructype;
#endif
		if (!(uderr = (struct t_uderr *) malloc(sizeof(*uderr))))
			goto badalloc;
		memset(uderr, 0, sizeof(*uderr));
		if (fields & T_ADDR) {
			int len;

			switch ((len = user->info.addr)) {
			case T_INFINITE:
				len = _T_DEFAULT_ADDRLEN;
			default:
				if (!(uderr->addr.buf = (char *) malloc(len))) {
					free(uderr);
					goto badalloc;
				}
				uderr->addr.maxlen = len;
			case 0:
				break;
			case T_INVALID:
				if (fields != T_ALL) {
					free(uderr);
					goto einval;
				}
				break;
			}
		}
		if (fields & T_OPT) {
			int len;

			switch ((len = user->info.options)) {
			case T_INFINITE:
				len = _T_DEFAULT_OPTLEN;
			default:
				if (!(uderr->opt.buf = (char *) malloc(len))) {
					if (uderr->addr.buf)
						free(uderr->addr.buf);
					free(uderr);
					goto badalloc;
				}
				uderr->opt.maxlen = len;
			case 0:
				break;
			case T_INVALID:
				if (fields != T_ALL) {
					if (uderr->addr.buf)
						free(uderr->addr.buf);
					free(uderr);
					goto einval;
				}
				break;
			}
		}
		return ((char *) uderr);
	}
	case T_INFO:
	{
		struct t_info *inf;

		if (!(inf = (struct t_info *) malloc(sizeof(*inf))))
			goto badalloc;
		memset(inf, 0, sizeof(*inf));
		return ((char *) inf);
	}
	default:
		goto tnostructype;
	}
      tnostructype:
	t_errno = TNOSTRUCTYPE;
	goto error;
      einval:
	t_errno = TSYSERR;
	errno = EINVAL;
	goto error;
      badalloc:
	t_errno = TSYSERR;
	goto error;
      error:
	return ((char *) NULL);
}

char *
__xnet_t_alloc_r(int fd, int type, int fields)
{
	char *ret = NULL;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_alloc(fd, type, fields);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_alloc_r,t_alloc@@XNET_1.0");

int
__xnet_t_bind(int fd, const struct t_bind *req, struct t_bind *ret)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, -1, TSF_UNBND)))
		goto error;
#ifdef DEBUG
	if (req && (req->addr.len < 0 || (req->addr.len > 0 && !req->addr.buf)))
		goto einval;
	if (ret && (req->addr.maxlen < 0 || (ret->addr.maxlen > 0 && !ret->addr.buf)))
		goto einval;
#endif
	if (req && req->addr.len > __xnet_u_max_addr(user))
		goto tbadaddr;
	{
		size_t add_len = (req && req->addr.len > 0) ? req->addr.len : 0;
		size_t add_max = min(__xnet_u_max_addr(user), _T_DEFAULT_ADDRLEN);
		size_t qlen = (req && req->qlen > 0) ? req->qlen : 0;
		union {
			struct {
				struct T_bind_req prim;
				unsigned char addr[add_len];
			} req;
			struct {
				struct T_bind_ack prim;
				unsigned char addr[add_max];
			} ack;
		} buf;

		buf.req.prim.PRIM_type = T_BIND_REQ;
		buf.req.prim.ADDR_length = add_len;
		buf.req.prim.ADDR_offset = add_len ? sizeof(buf.req.prim) : 0;
		buf.req.prim.CONIND_number = qlen;
		if (add_len)
			memcpy(buf.req.addr, req->addr.buf, add_len);
		if (__xnet_t_strioctl(fd, TI_BIND, &buf, sizeof(buf)) != 0)
			goto error;
		__xnet_u_setstate_const(user, TS_IDLE);
		user->qlen = buf.ack.prim.CONIND_number;
		if (ret) {
			if (ret->addr.maxlen > 0) {
				if (ret->addr.maxlen < buf.ack.prim.ADDR_length)
					goto tbufovflw;
				if ((ret->addr.len = buf.ack.prim.ADDR_length))
					memcpy(ret->addr.buf,
					       ((char *) &buf) + buf.ack.prim.ADDR_offset,
					       ret->addr.len);
			}
			ret->qlen = buf.ack.prim.CONIND_number;
		}
		return (0);
	}
      tbufovflw:
	t_errno = TBUFOVFLW;
	goto error;
      tbadaddr:
	t_errno = TBADADDR;
	goto error;
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto tsyserr;
#endif
      error:
	return (-1);
}

int
__xnet_t_bind_r(int fd, const struct t_bind *req, struct t_bind *ret)
{
	int rtv = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		rtv = __xnet_t_bind(fd, req, ret);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (rtv);
}

__asm__(".symver __xnet_t_bind_r,t_bind@@XNET_1.0");

int
__xnet_t_close(int fd)
{
	struct _t_user *user;
	int ret;

	if (!(user = __xnet_t_tstuser(fd, -1, -1, -1)))
		goto error;
	if ((ret = close(fd)) == 0 || errno != EINTR) {
		if (--user->refs == 0) {
			_t_fds[fd] = NULL;
			pthread_rwlock_destroy(&user->lock);
			if (user->ctlbuf)
				free(user->ctlbuf);
			if (user->datbuf)
				free(user->datbuf);
			free(user);
		}
	}
	if (ret == 0)
		return (0);
	if (errno == EINTR)
		t_errno = TSYSERR;
      error:
	return (-1);
}

int
__xnet_t_close_r(int fd)
{
	int err, ret = -1;

	pthread_cleanup_push_defer_np(__xnet_list_unlock, NULL);
	if ((err = __xnet_list_wrlock()) == 0) {
		ret = __xnet_t_close(fd);
		__xnet_list_unlock(NULL);
	} else {
		t_errno = TSYSERR;
		errno = err;
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_close_r,t_close@@XNET_1.0");

int
__xnet_t_connect(int fd, const struct t_call *sndcall, struct t_call *rcvcall)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS) | (1 << T_COTS_ORD), TSF_IDLE)))
		goto error;
	if (__xnet_t_look(fd) > 0)
		goto tlook;
#ifdef DEBUG
	if (!sndcall)
		goto einval;
	if (sndcall->addr.len < 0 || (sndcall->addr.len > 0 && !sndcall->addr.buf))
		goto einval;
	if (sndcall->opt.len < 0 || (sndcall->opt.len > 0 && !sndcall->opt.buf))
		goto einval;
	if (sndcall->udata.len < 0 || (sndcall->udata.len > 0 && !sndcall->udata.buf))
		goto einval;
	if (rcvcall
	    && (rcvcall->addr.maxlen < 0 || (rcvcall->addr.maxlen > 0 && !rcvcall->addr.buf)))
		goto einval;
	if (rcvcall && (rcvcall->opt.maxlen < 0 || (rcvcall->opt.maxlen > 0 && !rcvcall->opt.buf)))
		goto einval;
	if (rcvcall
	    && (rcvcall->udata.maxlen < 0 || (rcvcall->udata.maxlen > 0 && !rcvcall->udata.buf)))
		goto einval;
#endif
	if (sndcall && sndcall->addr.len > __xnet_u_max_addr(user))
		goto tbadaddr;
	if (sndcall && sndcall->opt.len > __xnet_u_max_options(user))
		goto tbadopt;
	if (sndcall && sndcall->udata.len > __xnet_u_max_connect(user))
		goto tbaddata;
	{
		size_t add_len = (sndcall && sndcall->addr.len > 0) ? sndcall->addr.len : 0;
		size_t opt_len = (sndcall && sndcall->opt.len > 0) ? sndcall->opt.len : 0;
		size_t dat_len = (sndcall && sndcall->udata.len > 0) ? sndcall->udata.len : 0;
		struct {
			struct T_conn_req prim;
			unsigned char addr[add_len];
			unsigned char opt[opt_len];
			unsigned char udata[dat_len];
		} req;

		req.prim.PRIM_type = T_CONN_REQ;
		req.prim.DEST_length = add_len;
		req.prim.DEST_offset = add_len ? sizeof(req.prim) : 0;
		req.prim.OPT_length = opt_len;
		req.prim.OPT_offset = opt_len ? sizeof(req.prim) + add_len : 0;
		if (add_len)
			memcpy(req.addr, sndcall->addr.buf, add_len);
		if (opt_len)
			memcpy(req.addr + add_len, sndcall->opt.buf, opt_len);
		if (dat_len)
			memcpy(req.addr + add_len + opt_len, sndcall->udata.buf, dat_len);
		if (__xnet_t_strioctl(fd, TI_SETPEERNAME, &req, sizeof(req)))
			goto error;
		__xnet_u_setstate_const(user, TS_WCON_CREQ);
	}
	return __xnet_t_rcvconnect(fd, rcvcall);
      tlook:
	t_errno = TLOOK;
	goto error;
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tbadaddr:
	t_errno = TBADADDR;
	goto error;
      tbadopt:
	t_errno = TBADOPT;
	goto error;
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      error:
	if (t_errno != TLOOK && __xnet_t_look(fd) > 0)
		goto tlook;
	return (-1);
}

int
__xnet_t_connect_r(int fd, const struct t_call *sndcall, struct t_call *rcvcall)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_connect(fd, sndcall, rcvcall)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_connect_r,t_connect@@XNET_1.0");

int
__xnet_t_error(const char *errmsg)
{
	fprintf(stderr, "%s: %s\n", errmsg, __xnet_t_strerror(t_errno));
	return (0);
}

int
__xnet_t_error_r(const char *errmsg)
{
	int oldtype, ret;

	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
	ret = __xnet_t_error(errmsg);
	pthread_setcanceltype(oldtype, NULL);
	return (ret);
}

__asm__(".symver __xnet_t_error_r,t_error@@XNET_1.0");

int
__xnet_t_free(void *ptr, int type)
{
	if (!ptr)
		goto einval;
	switch (type) {
	case T_BIND:
	{
		struct t_bind *bind = (struct t_bind *) ptr;

		if (bind->addr.buf)
			free(bind->addr.buf);
		free(bind);
		return (0);
	}
	case T_OPTMGMT:
	{
		struct t_optmgmt *opts = (struct t_optmgmt *) ptr;

		if (opts->opt.buf)
			free(opts->opt.buf);
		free(opts);
		return (0);
	}
	case T_CALL:
	{
		struct t_call *call = (struct t_call *) ptr;

		if (call->addr.buf)
			free(call->addr.buf);
		if (call->opt.buf)
			free(call->opt.buf);
		if (call->udata.buf)
			free(call->udata.buf);
		free(call);
		return (0);
	}
	case T_DIS:
	{
		struct t_discon *discon = (struct t_discon *) ptr;

		if (discon->udata.buf)
			free(discon->udata.buf);
		free(discon);
		return (0);
	}
	case T_UNITDATA:
	{
		struct t_unitdata *unitdata = (struct t_unitdata *) ptr;

		if (unitdata->addr.buf)
			free(unitdata->addr.buf);
		if (unitdata->opt.buf)
			free(unitdata->opt.buf);
		if (unitdata->udata.buf)
			free(unitdata->udata.buf);
		return (0);
	}
	case T_UDERROR:
	{
		struct t_uderr *uderr = (struct t_uderr *) ptr;

		if (uderr->addr.buf)
			free(uderr->addr.buf);
		if (uderr->opt.buf)
			free(uderr->opt.buf);
		free(uderr);
		return (0);
	}
	case T_INFO:
	{
		struct t_info *info = (struct t_info *) ptr;

		free(info);
		return (0);
	}
	default:
		goto tnostructype;
	}
      tnostructype:
	t_errno = TNOSTRUCTYPE;
	goto error;
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
      error:
	return (-1);
}

__asm__(".symver __xnet_t_free,t_free@@XNET_1.0");

int
__xnet_t_getinfo(int fd, struct t_info *info)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, -1, -1)))
		goto error;
#ifdef DEBUG
	if (!info)
		goto einval;
#endif
	{
		union {
			struct T_info_req req;
			struct T_info_ack ack;
		} buf;

		buf.req.PRIM_type = T_INFO_REQ;
		if (__xnet_t_strioctl(fd, TI_GETINFO, &buf, sizeof(buf)) != 0)
			goto error;
		user->info.addr = buf.ack.ADDR_size;
		user->info.options = buf.ack.OPT_size;
		user->info.tsdu = buf.ack.TSDU_size;
		user->info.etsdu = buf.ack.ETSDU_size;
		user->info.connect = buf.ack.CDATA_size;
		user->info.discon = buf.ack.DDATA_size;
		user->info.servtype = buf.ack.SERV_type;
		user->info.flags = buf.ack.PROVIDER_flag;

		user->info.tidu = buf.ack.TIDU_size;

		if (info)
			*info = user->info;
		return (0);
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      error:
	return (-1);
}

int
__xnet_t_getinfo_r(int fd, struct t_info *info)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_getinfo(fd, info);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_getinfo_r,t_getinfo@@XNET_1.0");

int
__xnet_t_getprotaddr(int fd, struct t_bind *loc, struct t_bind *rem)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, -1, -1)))
		goto error;
	{
		size_t add_max = min(__xnet_u_max_addr(user), _T_DEFAULT_ADDRLEN);
		union {
			struct {
				struct T_addr_req prim;
			} req;
			struct {
				struct T_addr_ack prim;
				unsigned char loc[add_max];
				unsigned char rem[add_max];
			} ack;
		} buf;

		buf.req.prim.PRIM_type = T_ADDR_REQ;
		if (__xnet_t_strioctl(fd, TI_GETADDRS, &buf, sizeof(buf)) != 0)
			goto error;
		if (loc && loc->addr.maxlen < buf.ack.prim.LOCADDR_length)
			goto tbufovflw;
		if (rem && rem->addr.maxlen < buf.ack.prim.REMADDR_length)
			goto tbufovflw;
		if (loc && (loc->addr.len = buf.ack.prim.LOCADDR_length))
			memcpy(loc->addr.buf, (char *) &buf + buf.ack.prim.LOCADDR_offset,
			       loc->addr.len);
		if (rem && (rem->addr.len = buf.ack.prim.REMADDR_length))
			memcpy(rem->addr.buf, (char *) &buf + buf.ack.prim.REMADDR_offset,
			       rem->addr.len);
		return (0);
	}
      tbufovflw:
	t_errno = TBUFOVFLW;
	goto error;
      error:
	return (-1);
}

int
__xnet_t_getprotaddr_r(int fd, struct t_bind *loc, struct t_bind *rem)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_getprotaddr(fd, loc, rem);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_getprotaddr_r,t_getprotaddr@@XNET_1.0");

int
__xnet_t_getstate(int fd)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, -1, -1, -1)))
		goto error;
	if (user->statef & (TSF_UNBND | TSF_IDLE | TSF_WCON_CREQ | TSF_WRES_CIND | TSF_DATA_XFER |
			    TSF_WREQ_ORDREL | TSF_WIND_ORDREL)) {
		return (user->state);
	}
	if (user->statef & (TSF_WACK_DREQ6 | TSF_WACK_DREQ7 | TSF_WACK_DREQ9 | TSF_WACK_DREQ10 |
			    TSF_WACK_DREQ11 | TSF_WACK_BREQ | TSF_WACK_UREQ | TSF_WACK_CREQ |
			    TSF_WACK_CRES))
		goto tstatechng;
	goto tproto;
      tstatechng:
	t_errno = TSTATECHNG;
	goto error;
      tproto:
	user->flags |= TUF_SYNC_REQUIRED;
	t_errno = TPROTO;
	goto error;
      error:
	return (-1);
}

int
__xnet_t_getstate_r(int fd)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_getstate(fd);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_getstate_r,t_getstate@@XNET_1.0");

int
__xnet_t_listen(int fd, struct t_call *call)
{
	struct _t_user *user;

	if (!
	    (user =
	     __xnet_t_tstuser(fd, T_LISTEN, (1 << T_COTS) | (1 << T_COTS_ORD),
			      TSF_WRES_CIND | TSF_IDLE)))
		goto error;
	if ((user->statef & TSF_IDLE) && user->qlen <= 0)
		goto tbadqlen;
	if (user->ocnt >= user->qlen)
		goto tqfull;
	switch (__xnet_t_getevent(fd)) {
	case T_LISTEN:
	{
		union T_primitives *p = (typeof(p)) user->ctrl.buf;

		__xnet_u_setstate_const(user, TS_WRES_CIND);
		user->ocnt++;
		if (call) {
			call->sequence = p->conn_ind.SEQ_number;
			if (call->addr.maxlen && call->addr.maxlen < p->conn_ind.SRC_length)
				goto tbufovflw;
			if (call->opt.maxlen && call->opt.maxlen < p->conn_ind.OPT_length)
				goto tbufovflw;
			if (call->udata.maxlen && call->udata.maxlen < user->data.len)
				goto tbufovflw;
			if (call->addr.maxlen && (call->addr.len = p->conn_ind.SRC_length))
				memcpy(call->addr.buf, (char *) p + p->conn_ind.SRC_offset,
				       call->addr.len);
			if (call->opt.maxlen && (call->opt.len = p->conn_ind.OPT_length))
				memcpy(call->opt.buf, (char *) p + p->conn_ind.OPT_offset,
				       call->opt.len);
			if (call->udata.maxlen && (call->udata.len = user->data.len))
				memcpy(call->udata.buf, user->data.buf, call->udata.len);
		}
		__xnet_u_reset_event(user);
		return (0);
	}
	case 0:
		goto tnodata;
	case -1:
		goto error;
	default:
		goto tlook;
	}
      tlook:
	t_errno = TLOOK;
	goto error;
      tnodata:
	t_errno = TNODATA;
	goto error;
      tbufovflw:
	__xnet_u_reset_event(user);
	t_errno = TBUFOVFLW;
	goto error;
      tqfull:
	t_errno = TQFULL;
	goto error;
      tbadqlen:
	t_errno = TBADQLEN;
	goto error;
      error:
	if (t_errno != TLOOK && __xnet_t_look(fd) > 0)
		goto tlook;
	return (-1);
}

int
__xnet_t_listen_r(int fd, struct t_call *call)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_listen(fd, call)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_listen_r,t_listen@@XNET_1.0");

int
__xnet_t_look(int fd)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, -1, -1, -1)))
		goto error;
	if (user->event == 0) {
		struct pollfd pfd;

		pfd.fd = fd;
		pfd.events =
		    (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND | POLLMSG | POLLERR | POLLHUP);
		pfd.revents = 0;
		switch (poll(&pfd, 1, 0)) {
		case 1:
			if (pfd.revents & (POLLMSG | POLLERR | POLLHUP))
				break;
			if (pfd.revents & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND))
				break;
		case 0:
			return (0);
		case -1:
			goto tsyserr;
		default:
			goto tproto;
		}
	}
	if (user->event == 0) {

		int ret, flag = 0;
		union T_primitives *p = (typeof(p)) user->ctlbuf;

		user->ctrl.maxlen = user->ctlmax;
		user->ctrl.len = 0;
		user->ctrl.buf = user->ctlbuf;
		user->data.maxlen = user->datmax;
		user->data.len = 0;
		user->data.buf = user->datbuf;
#ifndef CONFIG_XTI_IS_TYPELESS
		switch (user->info.servtype) {
		case T_CLTS:
			if (user->state == T_IDLE)
				user->data.maxlen = 0;
			break;
		case T_COTS_ORD:
			if (user->state == T_OUTREL)
				user->data.maxlen = 0;
		case T_COTS:
			if (user->state == T_DATAXFER)
				user->data.maxlen = 0;
			break;
		default:
			goto tsync;
		}
#else
		if ((user->state == T_IDLE && user->qlen == 0) || (user->state == T_OUTREL)
		    || (user->state == T_DATAXFER))
			user->data.maxlen = 0;
#endif
		if ((ret = __xnet_t_getmsg(fd, &user->ctrl, &user->data, &flag)) < 0)
			goto error;
		if ((ret & MORECTL) || ((ret & MOREDATA) && user->data.maxlen > 0))
			goto cleanup;
		if (flag != 0 || flag == RS_HIPRI)
			goto tsync;
		switch (user->state) {
		case T_UNINIT:

			goto tsync;
		case T_UNBND:

			goto tsync;
		case T_IDLE:
#ifndef CONFIG_XTI_IS_TYPELESS
			switch (user->info.servtype) {
			case T_COTS:
			case T_COTS_ORD:
				if (user->qlen > 0)
					goto tincon;

				goto tsync;
			case T_CLTS:

				if (user->ctrl.len == 0) {
					if ((user->gflags = (ret & MOREDATA))) {
						user->event = T_DATA;
						return (T_DATA);
					}
					return (0);
				}
				switch ((user->prim = p->type)) {
				case T_UNITDATA_IND:
					user->event = T_DATA;
					break;
				case T_UDERROR_IND:
					user->event = T_UDERR;
					break;
				default:
					goto cleanup;
				}
				user->gflags = (ret & MOREDATA);
				return (user->event);
			default:
				goto tsync;
			}
#else
			if (user->qlen > 0)
				goto tincon;
			if (user->ctrl.len == 0) {
				if ((user->gflags = (ret & MOREDATA))) {
					user->event = T_DATA;
					return (T_DATA);
				}
				return (0);
			}
			switch ((user->prim = p->type)) {
			case T_UNITDATA_IND:
				user->event = T_DATA;
				break;
			case T_UDERROR_IND:
				user->event = T_UDERR;
				break;
			default:
				goto cleanup;
			}
			user->gflags = (ret & MOREDATA);
			return (user->event);
#endif
		case T_OUTCON:

			switch ((user->prim = p->type)) {
			case T_CONN_CON:
				user->event = T_CONNECT;
				break;
			case T_DISCON_IND:
				user->event = T_DISCONNECT;
				break;
			default:
				goto cleanup;
			}
			user->gflags = 0;
			return (user->event);
		case T_INCON:

		      tincon:
			switch ((user->prim = p->type)) {
			case T_CONN_IND:
				user->event = T_LISTEN;
				break;
			case T_DISCON_IND:
				user->event = T_DISCONNECT;
				break;
			default:
				goto cleanup;
			}
			user->gflags = 0;
			return (user->event);
		case T_DATAXFER:

		case T_OUTREL:

			if (user->ctrl.len == 0) {
				if ((user->gflags = (ret & MOREDATA))) {
					user->event = T_DATA;
					return (T_DATA);
				}
				return (0);
			}
			switch ((user->prim = p->type)) {
			case T_DATA_IND:
				user->event = T_DATA;
				break;
			case T_EXDATA_IND:
				user->event = T_EXDATA;
				break;
			case T_OPTDATA_IND:
				user->event =
				    (p->optdata_ind.DATA_flag & T_ODF_EX) ? T_EXDATA : T_DATA;
				break;
			case T_ORDREL_IND:
				if (ret & MOREDATA) {
					user->data.maxlen = user->datmax;
					user->data.len = 0;
					user->data.buf = user->datbuf;
					ret = __xnet_t_getmsg(fd, NULL, &user->data, &flag);
					if (ret < 0)
						goto error;
					if (ret != 0)
						goto cleanup;
					if (flag != 0)
						goto tsync;
				}
				user->event = (ret & MOREDATA) ? T_ORDRELDATA : T_ORDREL;
				break;
			case T_DISCON_IND:
				if (ret & MOREDATA) {
					user->data.maxlen = user->datmax;
					user->data.len = 0;
					user->data.buf = user->datbuf;
					ret = __xnet_t_getmsg(fd, NULL, &user->data, &flag);
					if (ret < 0)
						goto error;
					if (ret != 0)
						goto cleanup;
					if (flag != 0)
						goto tsync;
				}
				user->event = T_DISCONNECT;
				break;
			default:
				goto cleanup;
			}
			user->gflags = (ret & MOREDATA);
			return (user->event);
		case T_INREL:

			switch ((user->prim = p->type)) {
			case T_DISCON_IND:
				user->event = T_DISCONNECT;
				break;
			default:
				goto cleanup;
			}
			user->gflags = 0;
			return (user->event);
		default:
			goto tsync;
		}
	      cleanup:
		user->ctrl.maxlen = user->ctlmax;
		user->data.maxlen = user->datmax;
		while (ret & (MORECTL | MOREDATA))
			if ((ret = __xnet_t_getmsg(fd, &user->ctrl, &user->data, &flag)) < 0)
				goto error;
		goto tsync;
	}
	return (user->event);
      tsync:
	user->flags |= TUF_SYNC_REQUIRED;
	goto error;
      tproto:
	t_errno = TPROTO;
	goto error;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
      error:
	return (-1);
}

int
__xnet_t_look_r(int fd)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_look(fd);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_look_r,t_look@@XNET_1.0");

int
__xnet_t_open(const char *path, int oflag, struct t_info *info)
{
	struct _t_user *user;
	int err, fd;

	t_errno = TSYSERR;
	if (!(user = (struct _t_user *) malloc(sizeof(*user))))
		goto enomem;
	memset(user, 0, sizeof(*user));
	user->ctlmax =
	    sizeof(union T_primitives) + _T_DEFAULT_ADDRLEN + _T_DEFAULT_ADDRLEN +
	    _T_DEFAULT_OPTLEN;
	user->datmax = _T_DEFAULT_DATALEN;
	if (!(user->ctlbuf = (char *) malloc(user->ctlmax)))
		goto enobufs;
	if (!(user->datbuf = (char *) malloc(user->datmax)))
		goto enobufs;
	if ((fd = open(path, oflag)) == -1)
		goto badopen;
	if (ioctl(fd, I_PUSH, "timod") != 0)
		goto badioctl;

	{
		int i;
		union {
			struct T_capability_req req;
			struct T_capability_ack ack;
		} buf;

		buf.req.PRIM_type = T_CAPABILITY_REQ;
		buf.req.CAP_bits1 = TC1_INFO | TC1_ACCEPTOR_ID;
		if (__xnet_t_strioctl(fd, TI_CAPABILITY, &buf, sizeof(buf)) != 0)
			goto badioctl;

		if ((buf.ack.CAP_bits1 & (TC1_INFO | TC1_ACCEPTOR_ID)) !=
		    (TC1_INFO | TC1_ACCEPTOR_ID))
			goto badbits;

		user->flags |= TUF_SYNC_REQUIRED;
		for (i = 0; i < OPEN_MAX; i++)
			if (_t_fds[i] && _t_fds[i]->token == buf.ack.ACCEPTOR_id) {
				free(user);
				user = _t_fds[i];
				pthread_rwlock_destroy(&user->lock);
				break;
			}
		user->fflags = oflag;
		user->info.addr = buf.ack.INFO_ack.ADDR_size;
		user->info.options = buf.ack.INFO_ack.OPT_size;
		user->info.tsdu = buf.ack.INFO_ack.TSDU_size;
		user->info.etsdu = buf.ack.INFO_ack.ETSDU_size;
		user->info.connect = buf.ack.INFO_ack.CDATA_size;
		user->info.discon = buf.ack.INFO_ack.DDATA_size;
		user->info.servtype = buf.ack.INFO_ack.SERV_type;
		user->info.flags = buf.ack.INFO_ack.PROVIDER_flag;
		user->info.tidu = buf.ack.INFO_ack.TIDU_size;
		user->token = buf.ack.ACCEPTOR_id;
		user->refs++;
		if (user->flags & TUF_SYNC_REQUIRED)
			__xnet_u_setstate(user, buf.ack.INFO_ack.CURRENT_state);
		pthread_rwlock_init(&user->lock, NULL);
		_t_fds[fd] = user;
		user->flags &= ~TUF_SYNC_REQUIRED;
		if (info)
			*info = user->info;
	}
	return (fd);
      badbits:
	t_errno = TPROTO;
      badioctl:
	err = errno;
	close(fd);
	free(user);
	errno = err;
	goto error;
      badopen:
	err = errno;
	free(user);
	errno = err;
	goto error;
      enobufs:
	errno = ENOBUFS;
	if (user->ctlbuf)
		free(user->ctlbuf);
	if (user->datbuf)
		free(user->datbuf);
	free(user);
	goto error;
      enomem:
	errno = ENOMEM;
	goto error;
      error:
	return (-1);
}

int
__xnet_t_open_r(const char *path, int oflag, struct t_info *info)
{
	int err, ret = -1;

	pthread_cleanup_push_defer_np(__xnet_list_unlock, NULL);
	if ((err = __xnet_list_wrlock()) == 0) {
		ret = __xnet_t_open(path, oflag, info);
		__xnet_list_unlock(NULL);
	} else {
		t_errno = TSYSERR;
		errno = err;
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_open_r,t_open@@XNET_1.0");

int
__xnet_t_optmgmt(int fd, const struct t_optmgmt *req, struct t_optmgmt *ret)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, -1, -1)))
		goto error;
#ifdef DEBUG
	if (!req)
		goto einval;
	if (req && (req->opt.len < 0 || (req->opt.len > 0 && !req->opt.buf)))
		goto einval;
	if (ret && (ret->opt.maxlen < 0 || (ret->opt.maxlen > 0 && !ret->opt.buf)))
		goto einval;
#endif
	if (req && req->opt.len > __xnet_u_max_options(user))
		goto tbadopt;
	{
		size_t opt_len = (req && req->opt.len > 0) ? req->opt.len : 0;
		size_t opt_max = min(__xnet_u_max_options(user), _T_DEFAULT_OPTLEN);
		union {
			struct {
				struct T_optmgmt_req prim;
				unsigned char opts[opt_len];
			} req;
			struct {
				struct T_optmgmt_ack prim;
				unsigned char opts[opt_max];
			} ack;
		} buf;

		buf.req.prim.PRIM_type = T_OPTMGMT_REQ;
		buf.req.prim.OPT_length = opt_len;
		buf.req.prim.OPT_offset = opt_len ? sizeof(buf.req.prim) : 0;
		buf.req.prim.MGMT_flags = req ? req->flags : 0;
		if (opt_len)
			memcpy(buf.req.opts, req->opt.buf, opt_len);
		if (__xnet_t_strioctl(fd, TI_OPTMGMT, &buf, sizeof(buf)) != 0)
			goto error;
		if (ret) {
			ret->flags = buf.ack.prim.MGMT_flags;
			if (ret->opt.maxlen < buf.ack.prim.OPT_length)
				goto tbufovflw;
			if ((ret->opt.len = buf.ack.prim.OPT_length))
				memcpy(ret->opt.buf, (char *) &buf + buf.ack.prim.OPT_offset,
				       ret->opt.len);
		}
		return (0);
	}
      tbadopt:
	t_errno = TBADOPT;
	goto error;
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tbufovflw:
	t_errno = TBUFOVFLW;
	goto error;
      error:
	return (-1);
}

int
__xnet_t_optmgmt_r(int fd, const struct t_optmgmt *req, struct t_optmgmt *ret)
{
	int rtv = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		rtv = __xnet_t_optmgmt(fd, req, ret);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (rtv);
}

__asm__(".symver __xnet_t_optmgmt_r,t_optmgmt@@XNET_1.0");

int
__xnet_t_rcv(int fd, char *buf, unsigned int nbytes, int *flags)
{
	struct _t_user *user;
	int copied = 0, event = 0, flag = 0, result;

	if (!
	    (user =
	     __xnet_t_tstuser(fd, -1, (1 << T_COTS) | (1 << T_COTS_ORD),
			      TSF_DATA_XFER | TSF_WIND_ORDREL)))
		goto error;
	if (nbytes == 0)
		return (0);
#ifdef DEBUG
	if (!flags || !buf)
		goto einval;
#endif
	{
		struct strbuf data;

		data.maxlen = nbytes;
		data.len = 0;
		data.buf = buf ? buf : NULL;
		if ((result =
		     __xnet_t_getdata(fd, &data, (T_DATA | T_EXDATA))) & (T_DATA | T_EXDATA))
			event = result;
		for (;;) {
			switch (result) {
			case T_DATA:
				if (event != T_DATA)
					goto tlook;
				flag = ((user->moresdu || user->moredat) ? T_MORE : 0);
				break;
			case T_EXDATA:
				if (event != T_EXDATA)
					goto tlook;
				flag =
				    ((user->moreedu || user->moreexp) ? T_MORE : 0) | T_EXPEDITED;
				break;
			case 0:
				goto tnodata;
			case -1:
				goto error;
			default:
				goto tlook;
			}
			if ((copied += data.len) >= nbytes)
				break;
			if (!(flag & T_MORE)) {
				__xnet_u_reset_event(user);
				break;
			}
			data.maxlen -= data.len;
			data.buf += data.len;
			data.len = 0;
			result = __xnet_t_getdata(fd, &data, event);
		}
	}
	if (flags)
		*flags = flag;
	return (copied);
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tlook:
	t_errno = TLOOK;
	goto error;
      tnodata:
	t_errno = TNODATA;
	goto error;
      error:
	if (copied) {
		if (flags)
			*flags = flag;
		return (copied);
	}
	return (-1);
}

int
__xnet_t_rcv_r(int fd, char *buf, unsigned int nbytes, int *flags)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_rcv(fd, buf, nbytes, flags)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_rcv_r,t_rcv@@XNET_1.0");

int
__xnet_t_rcvconnect(int fd, struct t_call *call)
{
	struct _t_user *user;
	union T_primitives *p;

	if (!
	    (user =
	     __xnet_t_tstuser(fd, T_CONNECT, (1 << T_COTS) | (1 << T_COTS_ORD), TSF_WCON_CREQ)))
		goto error;
#ifdef DEBUG
	if (call && (call->addr.maxlen < 0 || (call->addr.maxlen > 0 && !call->addr.buf)))
		goto einval;
	if (call && (call->opt.maxlen < 0 || (call->opt.maxlen > 0 && !call->opt.buf)))
		goto einval;
	if (call && (call->udata.maxlen < 0 || (call->udata.maxlen > 0 && !call->udata.buf)))
		goto einval;
#endif
	p = (typeof(p)) user->ctlbuf;
	switch (__xnet_t_getevent(fd)) {
	case T_CONNECT:
	{
		__xnet_u_setstate_const(user, TS_DATA_XFER);
		if (call) {
			if (call->addr.maxlen && call->addr.maxlen < p->conn_con.RES_length)
				goto tbufovflw;
			if (call->opt.maxlen && call->opt.maxlen < p->conn_con.OPT_length)
				goto tbufovflw;
			if (call->udata.maxlen && call->udata.maxlen < user->data.len)
				goto tbufovflw;
			if (call->addr.maxlen && (call->addr.len = p->conn_con.RES_length))
				memcpy(call->addr.buf, (char *) p + p->conn_con.RES_offset,
				       call->addr.len);
			if (call->opt.maxlen && (call->opt.len = p->conn_con.OPT_length))
				memcpy(call->opt.buf, (char *) p + p->conn_con.OPT_offset,
				       call->opt.len);
			if (call->udata.maxlen && (call->udata.len = user->data.len))
				memcpy(call->udata.buf, user->data.buf, call->udata.len);
		}
		__xnet_u_reset_event(user);
		return (0);
	}
	case 0:
		goto tnodata;
	case -1:
		goto error;
	default:
		goto tlook;
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tlook:
	t_errno = TLOOK;
	goto error;
      tnodata:
	t_errno = TNODATA;
	goto error;
      tbufovflw:
	__xnet_u_reset_event(user);
	t_errno = TBUFOVFLW;
	goto error;
      error:
	return (-1);
}

int
__xnet_t_rcvconnect_r(int fd, struct t_call *call)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_rcvconnect(fd, call)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_rcvconnect_r,t_rcvconnect@@XNET_1.0");

int
__xnet_t_rcvdis(int fd, struct t_discon *discon)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, T_DISCONNECT, (1 << T_COTS) | (1 << T_COTS_ORD),
				      TSF_DATA_XFER | TSF_WCON_CREQ | TSF_WIND_ORDREL |
				      TSF_WREQ_ORDREL | TSF_WRES_CIND)))
		goto error;
	if (user->state == T_INCON && user->ocnt < 1)
		goto toutstate;
#ifdef DEBUG
	if (discon
	    && (discon->udata.maxlen < 0 || (discon->udata.maxlen > 0 && !discon->udata.buf)))
		goto einval;
#endif
	switch (__xnet_t_getevent(fd)) {
	case T_DISCONNECT:
	{
		union T_primitives *p = (typeof(p)) user->ctrl.buf;

		switch (user->state) {
		case T_DATAXFER:
		case T_OUTCON:
		case T_OUTREL:
		case T_INREL:
			if (user->ocnt)
				__xnet_u_setstate_const(user, TS_WRES_CIND);
			else
				__xnet_u_setstate_const(user, TS_IDLE);
			break;
		case T_INCON:
			if (user->ocnt && --user->ocnt)
				__xnet_u_setstate_const(user, TS_WRES_CIND);
			else
				__xnet_u_setstate_const(user, TS_IDLE);
			break;
		default:
			goto toutstate;
		}
		if (discon) {
			discon->reason = p->discon_ind.DISCON_reason;
			discon->sequence = p->discon_ind.SEQ_number;
			if (discon->udata.maxlen && discon->udata.maxlen < user->data.len)
				goto tbufovflw;
			if (discon->udata.maxlen && (discon->udata.len = user->data.len))
				memcpy(discon->udata.buf, user->data.buf, discon->udata.len);
		}
		__xnet_u_reset_event(user);
		return (0);
	}
	case 0:
		goto tnodis;
	case -1:
		goto error;
	default:
		goto tlook;
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tlook:
	t_errno = TLOOK;
	goto error;
      tnodis:
	t_errno = TNODIS;
	goto error;
      tbufovflw:
	__xnet_u_reset_event(user);
	t_errno = TBUFOVFLW;
	goto error;
      toutstate:
	t_errno = TOUTSTATE;
	goto error;
      error:
	return (-1);
}

int
__xnet_t_rcvdis_r(int fd, struct t_discon *discon)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_rcvdis(fd, discon);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_rcvdis_r,t_rcvdis@@XNET_1.0");

int
__xnet_t_rcvleafchange(int fd, struct t_leaf_status *change)
{
#if defined HAVE_XTI_ATM_H
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS) | (1 << T_COTS_ORD), TSF_DATA_XFER)))
		goto error;
#ifdef DEBUG
	if (!change)
		goto einval;
#endif
	{
		struct {
			struct t_opthdr hdr;
			struct t_atm_leaf_ind leaf;
		} opts;
		struct t_optmgmt req, ret;

		req.opt.maxlen = 0;
		req.opt.len = sizeof(opts);
		req.opt.buf = (char *) &opts;
		req.flags = T_NEGOTIATE;
		ret.opt.maxlen = sizeof(opts);
		req.opt.len = 0;
		req.opt.buf = (char *) &opts;
		req.flags = 0;
		opts.hdr.len = sizeof(opts);
		opts.hdr.level = T_ATM_SIGNALLING;
		opts.hdr.name = T_ATM_LEAF_IND;
		opts.hdr.status = 0;
		if (__xnet_t_optmgmt(fd, &req, &ret) == -1)
			goto error;
		if (ret.opt.flags == T_FAILURE)
			goto tproto;
		if (ret.opt.len < sizeof(opts) || opts.hdr.len < sizeof(opts))
			goto tproto;
		if (opts.hdr.status != T_SUCCESS)
			goto tproto;
		if (opts.hdr.level != T_ATM_SIGNALLING || opts.hdr.name != T_ATM_LEAF_IND)
			goto tproto;
		if (opts.leaf.status == T_LEAF_NOCHANGE)
			goto tnodata;
		if (change) {
			change->leafid = opts.leaf.leaf_ID;
			change->status = opts.leaf.status;
			change->reason = opts.leaf.reason;
		}
		return (0);
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tproto:
	t_errno = TPROTO;
	goto error;
      tnodata:
	t_errno = TNODATA;
	goto error;
      error:
	return (-1);
#else
	t_errno = TNOTSUPPORT;
	return (-1);
#endif
}

#pragma weak __xnet_t_rcvleafchange_r

int
__xnet_t_rcvleafchange_r(int fd, struct t_leaf_status *change)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_rcvleafchange(fd, change);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_rcvleafchange_r,t_rcvleafchange@@XNET_1.0");

int
__xnet_t_rcvrel(int fd)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, T_ORDREL, (1 << T_COTS_ORD),
				      TSF_DATA_XFER | TSF_WIND_ORDREL)))
		goto error;
	switch (__xnet_t_getevent(fd)) {
	case T_ORDREL:
	{
		user->event = 0;
		if (user->state == T_DATAXFER)
			__xnet_u_setstate_const(user, TS_WREQ_ORDREL);
		if (user->state == T_OUTREL) {

			if (user->ocnt)
				__xnet_u_setstate_const(user, TS_WRES_CIND);
			else
				__xnet_u_setstate_const(user, TS_IDLE);
		}
		return (0);
	}
	case 0:
		goto tnorel;
	default:
		goto tlook;
	case -1:
		goto error;
	}
      tlook:
	t_errno = TLOOK;
	goto error;
      tnorel:
	t_errno = TNOREL;
	goto error;
      error:
	return (-1);
}

int
__xnet_t_rcvrel_r(int fd)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_rcvrel(fd)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_rcvrel_r,t_rcvrel@@XNET_1.0");

int
__xnet_t_rcvreldata(int fd, struct t_discon *discon)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, T_ORDREL, (1 << T_COTS_ORD),
				      TSF_DATA_XFER | TSF_WIND_ORDREL)))
		goto error;
#ifdef DEBUG
	if (discon
	    && (discon->udata.maxlen < 0 || (discon->udata.maxlen > 0 && !discon->udata.buf)))
		goto einval;
#endif
	switch (__xnet_t_getevent(fd)) {
	case T_ORDREL:
	{
		union T_primitives *p = (typeof(p)) user->ctrl.buf;

		if (user->state == T_DATAXFER)
			__xnet_u_setstate_const(user, TS_WREQ_ORDREL);
		if (user->state == T_OUTREL) {

			if (user->ocnt)
				__xnet_u_setstate_const(user, TS_WRES_CIND);
			else
				__xnet_u_setstate_const(user, TS_IDLE);
		}
		if (discon) {
			discon->reason = 0;
			discon->sequence = 0;
			if (discon->udata.maxlen && discon->udata.maxlen < user->data.len)
				goto tbufovflw;
			if (discon->udata.maxlen && (discon->udata.len = user->data.len))
				memcpy(discon->udata.buf, user->data.buf, discon->udata.len);
		}
		__xnet_u_reset_event(user);
		return (0);
	}
	case 0:
		goto tnorel;
	case -1:
		goto error;
	default:
		goto tlook;
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tlook:
	t_errno = TLOOK;
	goto error;
      tnorel:
	t_errno = TNOREL;
	goto error;
      tbufovflw:
	__xnet_u_reset_event(user);
	t_errno = TBUFOVFLW;
	goto error;
      error:
	return (-1);
}

int
__xnet_t_rcvreldata_r(int fd, struct t_discon *discon)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_rcvreldata(fd, discon)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_rcvreldata_r,t_rcvreldata@@XNET_1.0");

int
__xnet_t_rcvopt(int fd, struct t_unitdata *optdata, int *flags)
{
	struct _t_user *user;
	int copied = 0;

	if (!(user = __xnet_t_tstuser(fd, -1, (1 << T_COTS) | (1 << T_COTS_ORD),
				      TSF_DATA_XFER | TSF_WIND_ORDREL)))
		goto error;
#ifdef DEBUG
	if (!optdata)
		goto einval;
	if (!flags)
		goto einval;
#endif
	if (!optdata)
		return (0);
	switch (__xnet_t_getevent(fd)) {
	case T_DATA:
	case T_EXDATA:
	{
		union T_primitives *p = (typeof(p)) user->ctrl.buf;

		switch (user->prim) {
		case T_DATA_IND:
			if (optdata->addr.maxlen >= 0)
				optdata->addr.len = 0;
			if (optdata->opt.maxlen >= 0)
				optdata->opt.len = 0;
			if (optdata->udata.maxlen
			    && (optdata->udata.maxlen =
				min(optdata->udata.maxlen, user->data.len))) {
				memcpy(optdata->udata.buf, user->data.buf, optdata->udata.len);
				user->data.len -= optdata->udata.len;
				user->data.buf += optdata->udata.len;
			}
			if (flags)
				*flags |= (user->moresdu || user->moredat
					   || user->data.len > 0) ? T_MORE : 0;
			break;
		case T_EXDATA_IND:
			if (optdata->addr.maxlen >= 0)
				optdata->addr.len = 0;
			if (optdata->opt.maxlen >= 0)
				optdata->opt.len = 0;
			if (optdata->udata.maxlen
			    && (optdata->udata.len = min(optdata->udata.maxlen, user->data.len))) {
				memcpy(optdata->udata.buf, user->data.buf, optdata->udata.len);
				user->data.len -= optdata->udata.len;
				user->data.buf += optdata->udata.len;
			}
			if (flags)
				*flags |= (user->moreedu || user->moreexp
					   || user->data.len >
					   0) ? T_MORE | T_EXPEDITED : T_EXPEDITED;
			break;
		case T_OPTDATA_IND:
			if (optdata->addr.maxlen >= 0)
				optdata->addr.len = 0;
			if (optdata->opt.maxlen && optdata->opt.maxlen < p->optdata_ind.OPT_length)
				goto tbufovflw;
			if (optdata->opt.maxlen && (optdata->opt.len = p->optdata_ind.OPT_length))
				memcpy(optdata->opt.buf, (char *) p + p->optdata_ind.OPT_offset,
				       optdata->opt.len);
			if (optdata->udata.maxlen
			    && (optdata->udata.len = min(optdata->udata.maxlen, user->data.len))) {
				memcpy(optdata->udata.buf, user->data.buf, optdata->udata.len);
				user->data.len -= optdata->udata.len;
				user->data.buf += optdata->udata.len;
			}
			if (p->optdata_ind.DATA_flag & T_ODF_EX) {
				if (flags)
					*flags |= (user->moreedu || user->moreexp
						   || user->data.len >
						   0) ? T_MORE | T_EXPEDITED : T_EXPEDITED;
			} else {
				if (flags)
					*flags |= (user->moresdu || user->moredat
						   || user->data.len > 0) ? T_MORE : 0;
			}
			break;
		default:
			goto tlook;
		}
		__xnet_u_reset_event(user);
		return (0);
	}
	case 0:
		goto tnodata;
	case -1:
		goto error;
	default:
		goto tlook;
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tlook:
	t_errno = TLOOK;
	goto error;
      tnodata:
	t_errno = TNODATA;
	goto error;
      tbufovflw:
	__xnet_u_reset_event(user);
	t_errno = TBUFOVFLW;
	goto error;
      error:
	if (!copied)
		return (-1);
	return (copied);
}

int
__xnet_t_rcvopt_r(int fd, struct t_unitdata *optdata, int *flags)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_rcvopt(fd, optdata, flags)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_rcvopt_r,t_rcvopt@@XNET_1.0");

__hot int
__xnet_t_rcvudata(int fd, struct t_unitdata *unitdata, int *flags)
{
	struct _t_user *user;
	int copied = 0, flag = 0, result;

	if (unlikely(!(user = __xnet_t_tstuser(fd, T_DATA, (1 << T_CLTS), TSF_IDLE))))
		goto error;
#ifdef DEBUG
	if (!unitdata)
		goto einval;
	if (!flags)
		goto einval;
#endif
	if (unlikely(!unitdata))
		goto done;
	{
		struct strbuf data;

		data.maxlen = likely(unitdata->udata.maxlen > 0)
		    ? unitdata->udata.maxlen : 0;
		data.len = 0;
		data.buf = unitdata->udata.buf;
		if (likely((result = __xnet_t_getdata(fd, &data, T_DATA)) == T_DATA)) {
			union T_primitives *p = (typeof(p)) user->ctrl.buf;

			if (likely(user->prim == T_UNITDATA_IND)) {
				if (unlikely(unitdata->addr.maxlen)
				    && unlikely(unitdata->addr.maxlen < p->unitdata_ind.SRC_length))
					goto tbufovflw;
				if (unlikely(unitdata->opt.maxlen)
				    && unlikely(unitdata->opt.maxlen < p->unitdata_ind.OPT_length))
					goto tbufovflw;
				if (unlikely(unitdata->addr.maxlen)
				    && (unitdata->addr.len = p->unitdata_ind.SRC_length))
					memcpy(unitdata->addr.buf,
					       (char *) p + p->unitdata_ind.SRC_offset,
					       unitdata->addr.len);
				if (unlikely(unitdata->opt.maxlen)
				    && (unitdata->opt.len = p->unitdata_ind.OPT_length))
					memcpy(unitdata->opt.buf,
					       (char *) p + p->unitdata_ind.OPT_offset,
					       unitdata->opt.len);
			} else {
				unitdata->addr.len = 0;
				unitdata->opt.len = 0;
			}
		}
		for (;;) {
			if (likely(result == T_DATA))
				flag = (user->moresdu || user->moredat) ? T_MORE : 0;
			else if (result == 0)
				goto tnodata;
			else if (result == -1)
				goto error;
			else
				goto tlook;

			if (likely((copied += data.len) >= unitdata->udata.maxlen))
				break;
			if (likely(!(flag & T_MORE))) {
				__xnet_u_reset_event(user);
				break;
			}
			data.maxlen -= data.len;
			data.len = 0;
			data.buf += data.len;
			result = __xnet_t_getdata(fd, &data, T_DATA);
		}
	}
	if (flags)
		*flags = flag;
	unitdata->udata.len = copied;
      done:
	return (0);
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tlook:
	t_errno = TLOOK;
	goto error;
      tnodata:
	t_errno = TNODATA;
	goto error;
      tbufovflw:
	__xnet_u_reset_event(user);
	t_errno = TBUFOVFLW;
	goto error;
      error:
	if (flags)
		*flags = flag;
	unitdata->udata.len = copied;
	return (-1);
}

__hot int
__xnet_t_rcvudata_r(int fd, struct t_unitdata *unitdata, int *flags)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_rcvudata(fd, unitdata, flags)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_rcvudata_r,t_rcvudata@@XNET_1.0");

int
__xnet_t_rcvuderr(int fd, struct t_uderr *uderr)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, T_UDERR, (1 << T_CLTS), TSF_IDLE)))
		goto error;
#ifdef DEBUG
	if (uderr && (uderr->addr.maxlen < 0 || (uderr->addr.maxlen > 0 && !uderr->addr.buf)))
		goto einval;
	if (uderr && (uderr->opt.maxlen < 0 || (uderr->opt.maxlen > 0 && !uderr->opt.buf)))
		goto einval;
#endif
	switch (__xnet_t_getevent(fd)) {
	case T_UDERR:
	{
		union T_primitives *p = (typeof(p)) user->ctrl.buf;

		if (uderr) {
			uderr->error = p->uderror_ind.ERROR_type;
			if (uderr->addr.maxlen && uderr->addr.maxlen < p->uderror_ind.DEST_length)
				goto tbufovflw;
			if (uderr->opt.maxlen && uderr->opt.maxlen < p->uderror_ind.OPT_length)
				goto tbufovflw;
			if (uderr->addr.maxlen && (uderr->addr.len = p->uderror_ind.DEST_length))
				memcpy(uderr->addr.buf, (char *) p + p->uderror_ind.DEST_offset,
				       uderr->addr.len);
			if (uderr->opt.maxlen && (uderr->opt.len = p->uderror_ind.OPT_length))
				memcpy(uderr->opt.buf, (char *) p + p->uderror_ind.OPT_offset,
				       uderr->opt.len);
		}
		__xnet_u_reset_event(user);
		return (0);
	}
	case 0:
		goto tnouderr;
	case -1:
		goto error;
	default:
		goto tlook;
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tbufovflw:
	__xnet_u_reset_event(user);
	t_errno = TBUFOVFLW;
	goto error;
      tnouderr:
	t_errno = TNOUDERR;
	goto error;
      tlook:
	t_errno = TLOOK;
	goto error;
      error:
	return (-1);
}

int
__xnet_t_rcvuderr_r(int fd, struct t_uderr *uderr)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_rcvuderr(fd, uderr);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_rcvuderr_r,t_rcvuderr@@XNET_1.0");

int
__xnet_t_rcvv(int fd, struct t_iovec *iov, unsigned int iovcount, int *flags)
{
	struct _t_user *user;
	int copied = 0, nbytes, n = 0, event = 0, flag = 0, result;

	if (!(user = __xnet_t_tstuser(fd, -1, (1 << T_COTS) | (1 << T_COTS_ORD),
				      TSF_DATA_XFER | TSF_WIND_ORDREL)))
		goto error;
#ifdef DEBUG
	if (!flags)
		goto einval;
	if (!iov)
		goto einval;
#endif
	if (!iov)
		return (0);
	for (nbytes = 0, n = 0; n < iovcount; nbytes += iov[n].iov_len, n++) ;
	if (iovcount == 0 || iovcount > _T_IOV_MAX)
		goto tbaddata;
	{
		struct strbuf data;

		data.maxlen = iov[0].iov_len;
		data.len = 0;
		data.buf = iov[0].iov_base;
		if ((result =
		     __xnet_t_getdata(fd, &data, (T_DATA | T_EXDATA))) & (T_DATA | T_EXDATA))
			event = result;
		for (n = 0;;) {
			switch (result) {
			case T_DATA:
				if (event != T_DATA)
					goto tlook;
				flag = ((user->moresdu || user->moredat) ? T_MORE : 0);
				break;
			case T_EXDATA:
				if (event != T_EXDATA)
					goto tlook;
				flag =
				    ((user->moreedu || user->moreexp) ? T_MORE : 0) | T_EXPEDITED;
				break;
			case 0:
				goto tnodata;
			case -1:
				goto error;
			default:
				goto tlook;
			}
			if ((copied += data.len) >= nbytes)
				break;
			if (!(flag & T_MORE)) {
				__xnet_u_reset_event(user);
				break;
			}
			if (data.maxlen <= data.len) {
				if (++n >= iovcount)
					break;
				data.maxlen = iov[n].iov_len;
				data.buf = iov[n].iov_base;
				data.len = 0;
			} else {
				data.maxlen -= data.len;
				data.buf += data.len;
				data.len = 0;
			}
			result = __xnet_t_getdata(fd, &data, event);
		}
	}
	if (flags)
		*flags = flag;
	return (copied);
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tlook:
	t_errno = TLOOK;
	goto error;
      tnodata:
	t_errno = TNODATA;
	goto error;
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      error:
	if (!copied)
		return (-1);
	if (flags)
		*flags = flag;
	return (copied);
}

int
__xnet_t_rcvv_r(int fd, struct t_iovec *iov, unsigned int iovcount, int *flags)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_rcvv(fd, iov, iovcount, flags)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_rcvv_r,t_rcvv@@XNET_1.0");

int
__xnet_t_rcvvudata(int fd, struct t_unitdata *unitdata, struct t_iovec *iov, unsigned int iovcount,
		   int *flags)
{
	struct _t_user *user;
	int copied = 0, nbytes, n = 0, flag = 0, result;

	if (!(user = __xnet_t_tstuser(fd, T_DATA, (1 << T_CLTS), TSF_IDLE)))
		goto error;
	for (nbytes = 0, n = 0; n < iovcount; nbytes += iov[n].iov_len, n++) ;
	if (iovcount == 0 || iovcount > _T_IOV_MAX)
		goto tbaddata;
#ifdef DEBUG
	if (!unitdata)
		goto einval;
	if (!flags)
		goto einval;
#endif
	{
		struct strbuf data;

		data.maxlen = iov[0].iov_len;
		data.len = 0;
		data.buf = iov[0].iov_base;
		if ((result = __xnet_t_getdata(fd, &data, T_DATA)) == T_DATA) {
			union T_primitives *p = (typeof(p)) user->ctrl.buf;

			switch (user->prim) {
			case T_UNITDATA_IND:
				if (unitdata) {
					if (unitdata->addr.maxlen
					    && unitdata->addr.maxlen < p->unitdata_ind.SRC_length)
						goto tbufovflw;
					if (unitdata->opt.maxlen
					    && unitdata->opt.maxlen < p->unitdata_ind.OPT_length)
						goto tbufovflw;
					if (unitdata->addr.maxlen
					    && (unitdata->addr.len = p->unitdata_ind.OPT_length))
						memcpy(unitdata->addr.buf,
						       (char *) p + p->unitdata_ind.SRC_offset,
						       unitdata->addr.len);
					if (unitdata->opt.maxlen
					    && (unitdata->opt.len = p->unitdata_ind.OPT_length))
						memcpy(unitdata->opt.buf,
						       (char *) p + p->unitdata_ind.OPT_offset,
						       unitdata->opt.len);
				}
				break;
			default:
				if (unitdata) {
					unitdata->addr.len = 0;
					unitdata->opt.len = 0;
				}
				break;
			}
		}
		for (;;) {
			switch (result) {
			case T_DATA:
				flag = (user->moresdu || user->moredat) ? T_MORE : 0;
				break;
			case 0:
				goto tnodata;
			case -1:
				goto error;
			default:
				goto tlook;
			}
			if ((copied += data.len) >= nbytes)
				break;
			if (!(flag & T_MORE)) {
				__xnet_u_reset_event(user);
				break;
			}
			if (data.maxlen <= data.len) {
				if (++n >= iovcount)
					break;
				data.maxlen = iov[n].iov_len;
				data.len = 0;
				data.buf = iov[n].iov_base;
			} else {
				data.maxlen -= data.len;
				data.len = 0;
				data.buf += data.len;
			}
			result = __xnet_t_getdata(fd, &data, T_DATA);
		}
	}
	if (flags)
		*flags = flag;
	if (unitdata)
		unitdata->udata.len = copied;
	return (0);
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tlook:
	t_errno = TLOOK;
	goto error;
      tnodata:
	t_errno = TNODATA;
	goto error;
      tbufovflw:
	__xnet_u_reset_event(user);
	t_errno = TBUFOVFLW;
	goto error;
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      error:
	if (flags)
		*flags = flag;
	if (unitdata)
		unitdata->udata.len = copied;
	return (-1);
}

int __xnet_t_rcvvopt(int fd, struct t_unitdata *unitdata, struct t_iovec *iov, unsigned int
		iovcount, int *flags)
__attribute__((alias("__xnet_t_rcvvudata")));

int
__xnet_t_rcvvudata_r(int fd, struct t_unitdata *unitdata, struct t_iovec *iov,
		     unsigned int iovcount, int *flags)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_rcvvudata(fd, unitdata, iov, iovcount, flags)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

int __xnet_t_rcvvopt_r(int fd, struct t_unitdata *unitdata, struct t_iovec *iov, unsigned int
		iovcount, int *flags)
__attribute__((alias("__xnet_t_rcvvudata_r")));

__asm__(".symver __xnet_t_rcvvudata_r,t_rcvvudata@@XNET_1.0");
__asm__(".symver __xnet_t_rcvvopt_r,t_rcvvopt@@XNET_1.0");

int
__xnet_t_removeleaf(int fd, int leafid, int reason)
{
#if defined HAVE_XTI_ATM_H
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS) | (1 << T_COTS_ORD), TSF_DATA_XFER)))
		goto error;
	{
		struct {
			struct t_opthdr hdr;
			struct t_atm_drop_leaf leaf;
		} opts;
		struct t_optmgmt req, ret;

		req.opt.maxlen = 0;
		req.opt.len = sizeof(opts);
		req.opt.buf = (char *) &opts;
		req.flags = T_CURRENT;
		ret.opt.maxlen = sizeof(opts);
		ret.opt.len = 0;
		ret.opt.buf = (char *) &opts;
		ret.flags = 0;
		opts.hdr.len = sizeof(opts);
		opts.hdr.level = T_ATM_SIGNALLING;
		opts.hdr.name = T_ATM_DROP_LEAF;
		opts.hdr.status = 0;
		opts.leaf.leaf_ID = leafid;
		opts.leaf.reason = reason;
		if (__xnet_t_optmgmt(fd, &req, &ret) == -1)
			goto error;
		if (ret.opt.flags == T_FAILURE)
			goto tproto;
		if (ret.opt.len < sizeof(opts) || opts.hdr.len < sizeof(opts))
			goto tproto;
		if (opts.hdr.status != T_SUCCESS)
			goto tproto;
		if (opts.hdr.level != T_ATM_SIGNALLING || opts.hdr.name != T_ATM_DROP_LEAF)
			goto tproto;

		goto tnodata;
	}
      tnodata:
	t_errno = TNODATA;
	goto error;
      error:
	return (-1);
#else
	t_errno = TNOTSUPPORT;
	return (-1);
#endif
}

#pragma weak __xnet_t_removeleaf_r

int
__xnet_t_removeleaf_r(int fd, int leafid, int reason)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_removeleaf(fd, leafid, reason);
		__xnet_t_putuser(&fd);
	}
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_removeleaf_r,t_removeleaf@@XNET_1.0");

int
__xnet_t_snd(int fd, char *buf, unsigned int nbytes, int flags)
{
	struct _t_user *user;
	int written = 0;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS) | (1 << T_COTS_ORD),
				      TSF_DATA_XFER | TSF_WREQ_ORDREL)))
		goto error;
	if (__xnet_t_look(fd) > 0)
		goto tlook;
#ifdef DEBUG
	if (!buf)
		goto einval;
#endif
	if (nbytes == 0 && !(user->info.flags & T_SNDZERO))
		goto tbaddata;
	if (nbytes > ((flags & T_EXPEDITED) ? __xnet_u_max_etsdu(user) : __xnet_u_max_tsdu(user)))
		goto tbaddata;
	if (!buf)
		return (0);
	{
		int band = (flags & T_EXPEDITED) ? 1 : 0;
		struct T_exdata_req prim;
		struct strbuf ctrl, data;

		ctrl.maxlen = 0;
		ctrl.len = sizeof(prim);
		ctrl.buf = (char *) &prim;
		data.maxlen = 0;
		data.len = nbytes;
		data.buf = buf;
		prim.PRIM_type = (flags & T_EXPEDITED) ? T_EXDATA_REQ : T_DATA_REQ;
		do {
			data.len = min(__xnet_u_max_tidu(user), nbytes - written);
			data.buf = buf + written;
			prim.MORE_flag = (written + data.len < nbytes) || (flags & T_MORE);
			if (__xnet_t_putpmsg(fd, &ctrl, &data, band, MSG_BAND) == -1)
				goto error;
			written += data.len;
		} while (written < nbytes);
		return (written);
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      tlook:
	t_errno = TLOOK;
	goto error;
      error:
	if (!written) {
		if (t_errno != TLOOK && __xnet_t_look(fd))
			goto tlook;
		return (-1);
	}
	return (written);
}

int
__xnet_t_snd_r(int fd, char *buf, unsigned int nbytes, int flags)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_snd(fd, buf, nbytes, flags)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_snd_r,t_snd@@XNET_1.0");

int
__xnet_t_snddis(int fd, const struct t_call *call)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS) | (1 << T_COTS_ORD),
				      TSF_WCON_CREQ | TSF_WRES_CIND | TSF_DATA_XFER |
				      TSF_WIND_ORDREL | TSF_WREQ_ORDREL)))
		goto error;
	if (__xnet_t_look(fd) > 0)
		goto tlook;
	if (user->state == T_INREL && (!call || (user->ocnt > 1 && !call->sequence)))
		goto tbadseq;
#ifdef DEBUG
	if (call && (call->udata.len < 0 || (call->udata.len > 0 && !call->udata.buf)))
		goto einval;
#endif
	if (call && call->udata.len > __xnet_u_max_discon(user))
		goto tbaddata;
	{
		size_t dat_len = (call && call->udata.len > 0) ? call->udata.len : 0;
		struct {
			struct T_discon_req prim;
			unsigned char udata[dat_len];
		} req;

		req.prim.PRIM_type = T_DISCON_REQ;
		req.prim.SEQ_number = call ? call->sequence : 0;
		if (dat_len)
			memcpy(req.udata, call->udata.buf, dat_len);
		switch (user->state) {
		case T_DATAXFER:
		case T_OUTCON:
		case T_OUTREL:
		case T_INREL:
			if (__xnet_t_strioctl(fd, TI_SETPEERNAME, &req, sizeof(req)) != 0)
				goto error;
			if (user->ocnt)
				__xnet_u_setstate_const(user, TS_WRES_CIND);
			else
				__xnet_u_setstate_const(user, TS_IDLE);
			return (0);
		case T_INCON:
			if (__xnet_t_strioctl(fd, TI_SETMYNAME, &req, sizeof(req)) != 0)
				goto error;
			if (user->ocnt > 0 && --user->ocnt)
				__xnet_u_setstate_const(user, TS_WRES_CIND);
			else
				__xnet_u_setstate_const(user, TS_IDLE);
			return (0);
		default:
			goto toutstate;
		}
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      tbadseq:
	t_errno = TBADSEQ;
	goto error;
      toutstate:
	t_errno = TOUTSTATE;
	goto error;
      tlook:
	t_errno = TLOOK;
	goto error;
      error:
	if (t_errno != TLOOK && __xnet_t_look(fd) > 0)
		goto tlook;
	return (-1);
}

int
__xnet_t_snddis_r(int fd, const struct t_call *call)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		ret = __xnet_t_snddis(fd, call);
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_snddis_r,t_snddis@@XNET_1.0");

int
__xnet_t_sndrel(int fd)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS_ORD), TSF_DATA_XFER | TSF_WREQ_ORDREL)))
		goto error;
	if (__xnet_t_look(fd) > 0)
		goto tlook;
	{
		struct T_ordrel_req prim;
		struct strbuf ctrl;

		prim.PRIM_type = T_ORDREL_REQ;
		ctrl.maxlen = sizeof(prim);
		ctrl.len = sizeof(prim);
		ctrl.buf = (char *) &prim;
		if (__xnet_t_putmsg(fd, &ctrl, NULL, 0) != 0)
			goto error;
		switch (user->state) {
		case T_DATAXFER:
			__xnet_u_setstate_const(user, TS_WIND_ORDREL);
			break;
		case T_INREL:
			if (user->ocnt)
				__xnet_u_setstate_const(user, TS_WRES_CIND);
			else
				__xnet_u_setstate_const(user, TS_IDLE);
			break;
		}
		return (0);
	}
      tlook:
	t_errno = TLOOK;
	goto error;
      error:
	if (t_errno != TLOOK && __xnet_t_look(fd) > 0)
		goto tlook;
	return (-1);
}

int
__xnet_t_sndrel_r(int fd)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_sndrel(fd)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_sndrel_r,t_sndrel@@XNET_1.0");

int
__xnet_t_sndreldata(int fd, struct t_discon *discon)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS_ORD), TSF_DATA_XFER | TSF_WREQ_ORDREL)))
		goto error;
	if (__xnet_t_look(fd) > 0)
		goto tlook;
	if (!(user->info.flags & T_ORDRELDATA))
		goto tnotsupport;
#ifdef DEBUG
	if (discon && (discon->udata.len < 0 || (discon->udata.len > 0 && !discon->udata.buf)))
		goto einval;
#endif
	if (discon && discon->udata.len > __xnet_u_max_discon(user))
		goto tbaddata;
	{
		struct T_ordrel_req prim;
		struct strbuf ctrl, data;

		prim.PRIM_type = T_ORDREL_REQ;
		ctrl.maxlen = sizeof(prim);
		ctrl.len = sizeof(prim);
		ctrl.buf = (char *) &prim;
		data.maxlen = discon ? discon->udata.maxlen : 0;
		data.len = discon ? discon->udata.len : 0;
		data.buf = discon ? discon->udata.buf : NULL;
		if (__xnet_t_putmsg(fd, &ctrl, &data, 0) != 0)
			goto error;
		switch (user->state) {
		case T_DATAXFER:
			__xnet_u_setstate_const(user, TS_WIND_ORDREL);
			break;
		case T_INREL:
			if (user->ocnt)
				__xnet_u_setstate_const(user, TS_WRES_CIND);
			else
				__xnet_u_setstate_const(user, TS_IDLE);
			break;
		}
		return (0);
	}
      tbaddata:
	t_errno = TBADDATA;
	goto error;
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tnotsupport:
	t_errno = TNOTSUPPORT;
	goto error;
      tlook:
	t_errno = TLOOK;
	goto error;
      error:
	if (t_errno != TLOOK && __xnet_t_look(fd) > 0)
		goto tlook;
	return (-1);
}

int
__xnet_t_sndreldata_r(int fd, struct t_discon *discon)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_sndreldata(fd, discon)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_sndreldata_r,t_sndreldata@@XNET_1.0");

int
__xnet_t_sndopt(int fd, const struct t_unitdata *optdata, int flags)
{
	struct _t_user *user;
	int written = 0;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS) | (1 << T_COTS_ORD),
				      TSF_DATA_XFER | TSF_WIND_ORDREL)))
		goto error;
#ifdef DEBUG
	if (!optdata)
		goto einval;
#endif
	if (optdata && optdata->opt.len > __xnet_u_max_options(user))
		goto tbadopt;
	if (optdata && optdata->udata.len > __xnet_u_max_tsdu(user))
		goto tbaddata;
	if (optdata && optdata->udata.len == 0 && !(user->info.flags & T_SNDZERO))
		goto tbaddata;
	{
		int band = (flags & T_EXPEDITED) ? 1 : 0;
		size_t opt_len = (optdata && optdata->opt.len) > 0 ? optdata->opt.len : 0;
		size_t dat_len = (optdata && optdata->udata.len) > 0 ? optdata->udata.len : 0;
		struct {
			struct T_optdata_req prim;
			unsigned char opt[opt_len];
		} req;
		struct strbuf ctrl, data;

		ctrl.maxlen = 0;
		ctrl.len = sizeof(req);
		ctrl.buf = (char *) &req;
		data.maxlen = 0;
		data.len = dat_len;
		data.buf = optdata ? optdata->udata.buf : NULL;
		req.prim.PRIM_type = T_OPTDATA_REQ;
		req.prim.DATA_flag =
		    ((flags & T_EXPEDITED) ? T_ODF_EX : 0) | ((flags & T_MORE) ? T_ODF_MORE : 0);
		req.prim.OPT_length = opt_len;
		req.prim.OPT_offset = opt_len ? sizeof(req.prim) : 0;
		if (opt_len)
			memcpy(req.opt, optdata->opt.buf, opt_len);
		while (written < dat_len) {
			data.len = min(__xnet_u_max_tidu(user), dat_len - written);
			data.buf = optdata->udata.buf + written;
			req.prim.DATA_flag = (((flags & T_EXPEDITED)) ? T_ODF_EX : 0)
			    | (((flags & T_MORE) || (written + data.len < dat_len)) ? T_ODF_MORE :
			       0);
			if (__xnet_t_putpmsg(fd, &ctrl, &data, band, MSG_BAND))
				goto error;
			written += data.len;
		}
		return (written);
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tbadopt:
	t_errno = TBADOPT;
	goto error;
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      error:
	if (!written)
		return (-1);
	return (written);
}

int
__xnet_t_sndopt_r(int fd, const struct t_unitdata *optdata, int flags)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_sndopt(fd, optdata, flags)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_sndopt_r,t_sndopt@@XNET_1.0");

int
__xnet_t_sndvopt(int fd, const struct t_unitdata *optdata, const struct t_iovec *iov,
		 unsigned int iovcount, int flags)
{
	struct _t_user *user;
	int written = 0, nbytes, i;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS) | (1 << T_COTS_ORD),
				      TSF_DATA_XFER | TSF_WREQ_ORDREL)))
		goto error;
#ifdef DEBUG
	if (!iov)
		goto einval;
#endif
	for (nbytes = 0, i = 0; i < iovcount; nbytes += iov[i].iov_len, i++) ;
	if (nbytes == 0 && !(user->info.flags & T_SNDZERO))
		goto tbaddata;
	if (nbytes > ((flags & T_EXPEDITED) ? __xnet_u_max_etsdu(user) : __xnet_u_max_tsdu(user)))
		goto tbaddata;
	{
		size_t opt_len = (optdata && optdata->opt.len > 0) ? optdata->opt.len : 0;
		int band = (flags & T_EXPEDITED) ? 1 : 0;
		int n = 0, partial = 0;
		struct {
			struct T_optdata_req prim;
			unsigned char opt[opt_len];
		} req;
		struct strbuf ctrl, data;

		ctrl.maxlen = 0;
		ctrl.len = sizeof(req);
		ctrl.buf = (char *) &req;
		data.maxlen = 0;
		data.len = nbytes;
		data.buf = optdata ? optdata->udata.buf : NULL;
		req.prim.PRIM_type = T_OPTDATA_REQ;
		req.prim.OPT_length = opt_len;
		req.prim.OPT_offset = opt_len ? sizeof(req.prim) : 0;
		if (opt_len)
			memcpy(req.opt, optdata->opt.buf, opt_len);
		do {
			do {
				data.len = min(__xnet_u_max_tidu(user), iov[n].iov_len - partial);
				data.buf = iov[n].iov_base + partial;
				req.prim.DATA_flag = (((flags & T_EXPEDITED)) ? T_ODF_EX : 0) |
				    (((flags & T_MORE)
				      || (written + data.len < nbytes)) ? T_ODF_MORE : 0);
				if (__xnet_t_putpmsg(fd, &ctrl, &data, band, MSG_BAND) == -1)
					goto error;
				partial += data.len;
				written += data.len;
			} while (partial < iov[n].iov_len);
			n++;
			partial = 0;
		} while (written < nbytes && n < iovcount);
		return (written);
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      error:
	if (!written)
		return (-1);
	return (written);
}

int
__xnet_t_sndvopt_r(int fd, const struct t_unitdata *optdata, const struct t_iovec *iov,
		   unsigned int iovcount, int flags)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_sndvopt(fd, optdata, iov, iovcount, flags)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_sndvopt_r,t_sndvopt@@XNET_1.0");

__hot int
__xnet_t_sndudata(int fd, const struct t_unitdata *unitdata)
{
	struct _t_user *user;

	if (!(user = __xnet_t_tstuser(fd, T_DATA, (1 << T_CLTS), TSF_IDLE)))
		goto error;
#if 1
	if ((__xnet_t_look(fd) & ~T_DATA) > 0)
		goto tlook;
#endif
#ifdef DEBUG
	if (unlikely(!unitdata))
		goto einval;
	if (unlikely(unitdata->addr.len < 0 || (unitdata->addr.len > 0 && !unitdata->addr.buf)))
		goto einval;
	if (unlikely(unitdata->opt.len < 0 || (unitdata->opt.len > 0 && !unitdata->opt.buf)))
		goto einval;
	if (unlikely(unitdata->udata.len < 0 || (unitdata->udata.len > 0 && !unitdata->udata.buf)))
		goto einval;
#endif
	if (likely(unitdata)) {
		if (unlikely(unitdata->addr.len > __xnet_u_max_addr(user)))
			goto tbadaddr;
		if (unlikely(unitdata->opt.len > __xnet_u_max_options(user)))
			goto tbadopt;
		if (unlikely(unitdata->udata.len > __xnet_u_max_tsdu(user)))
			goto tbaddata;
		if (unlikely(unitdata->udata.len > __xnet_u_max_tidu(user)))
			goto tbaddata;
		if (unlikely(unitdata->udata.len == 0 && !(user->info.flags & T_SNDZERO)))
			goto tbaddata;
	} else {
		if (unlikely(!(user->info.flags & T_SNDZERO)))
			goto tbaddata;
	}
	{
		size_t add_len = (likely(unitdata)
				  && likely(unitdata->addr.len > 0)) ? unitdata->addr.len : 0;
		size_t opt_len = (likely(unitdata)
				  && unlikely(unitdata->opt.len > 0)) ? unitdata->opt.len : 0;
		struct {
			struct T_unitdata_req prim;
			unsigned char addr[add_len];
			unsigned char opt[opt_len];
		} req;
		struct strbuf ctrl, data;

		ctrl.maxlen = sizeof(req);
		ctrl.len = sizeof(req);
		ctrl.buf = (char *) &req;
		if (likely(unitdata)) {
			data.maxlen = unitdata->udata.maxlen;
			data.len = unitdata->udata.len;
			data.buf = unitdata->udata.buf;
		} else {
			data.maxlen = 0;
			data.len = 0;
			data.buf = NULL;
		}
		req.prim.PRIM_type = T_UNITDATA_REQ;
		req.prim.DEST_length = add_len;
		req.prim.DEST_offset = likely(add_len) ? sizeof(req.prim) : 0;
		req.prim.OPT_length = opt_len;
		req.prim.OPT_offset = unlikely(opt_len) ? sizeof(req.prim) + add_len : 0;
		if (likely(add_len))
			memcpy(req.addr, unitdata->addr.buf, add_len);
		if (unlikely(opt_len))
			memcpy(req.addr + add_len, unitdata->opt.buf, opt_len);
		if (unlikely(__xnet_t_putmsg(fd, &ctrl, &data, 0)))
			goto error;
		return (0);
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      tbadopt:
	t_errno = TBADOPT;
	goto error;
      tbadaddr:
	t_errno = TBADADDR;
	goto error;
      tlook:
	t_errno = TLOOK;
	goto error;
      error:
	if (t_errno != TLOOK && (__xnet_t_look(fd) & ~T_DATA) > 0)
		goto tlook;
	return (-1);
}

__hot int
__xnet_t_sndudata_r(int fd, const struct t_unitdata *unitdata)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_sndudata(fd, unitdata)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_sndudata_r,t_sndudata@@XNET_1.0");

int
__xnet_t_sndv(int fd, const struct t_iovec *iov, unsigned int iovcount, int flags)
{
	struct _t_user *user;
	int written = 0, nbytes, i;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS) | (1 << T_COTS_ORD),
				      TSF_DATA_XFER | TSF_WREQ_ORDREL)))
		goto error;
	if (__xnet_t_look(fd) > 0)
		goto tlook;
#ifdef DEBUG
	if (!iov)
		goto einval;
#endif
	if (iovcount == 0 || iovcount > _T_IOV_MAX)
		goto tbaddata;
	for (nbytes = 0, i = 0; i < iovcount; nbytes += iov[i].iov_len, i++) ;
	if (nbytes == 0 && !(user->info.flags & T_SNDZERO))
		goto tbaddata;
	if (nbytes > ((flags & T_EXPEDITED) ? __xnet_u_max_etsdu(user) : __xnet_u_max_tsdu(user)))
		goto tbaddata;
	{
		int band = (flags & T_EXPEDITED) ? 1 : 0;
		int n = 0, partial = 0;
		struct T_exdata_req prim;
		struct strbuf ctrl, data;

		ctrl.maxlen = 0;
		ctrl.len = sizeof(prim);
		ctrl.buf = (char *) &prim;
		data.maxlen = 0;
		data.len = nbytes;
		data.buf = NULL;
		prim.PRIM_type = (flags & T_EXPEDITED) ? T_EXDATA_REQ : T_DATA_REQ;
		do {
			do {
				data.len = min(__xnet_u_max_tidu(user), iov[n].iov_len - partial);
				data.buf = iov[n].iov_base + partial;
				prim.MORE_flag = (written + data.len < nbytes) || (flags & T_MORE);
				if (__xnet_t_putpmsg(fd, &ctrl, &data, band, MSG_BAND) == -1)
					goto error;
				partial += data.len;
				written += data.len;
			} while (partial < iov[n].iov_len);
			n++;
			partial = 0;
		} while (written < nbytes && n < iovcount);
		return (written);
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      tlook:
	t_errno = TLOOK;
	goto error;
      error:
	if (!written) {
		if (t_errno != TLOOK && __xnet_t_look(fd))
			goto tlook;
		return (-1);
	}
	return (written);
}

int
__xnet_t_sndv_r(int fd, const struct t_iovec *iov, unsigned int iovcount, int flags)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_sndv(fd, iov, iovcount, flags)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_sndv_r,t_sndv@@XNET_1.0");

#if 0

int
__xnet_t_sndvopt(int fd, struct t_optmgmt *options, const struct t_iovec *iov,
		 unsigned int iovcount, int flags)
{
	struct _t_user *user;
	int written = 0, nbytes, i;

	if (!(user = __xnet_t_tstuser(fd, 0, (1 << T_COTS) | (1 << T_COTS_ORD),
				      TSF_DATA_XFER | TSF_WREQ_ORDREL)))
		goto error;
#ifdef DEBUG
	if (!iov)
		goto einval;
	if (options && (options->opt.len < 0 || (options->opt.len > 0 && !options->opt.buf)))
		goto einval;
#endif
	if (options && options->opt.len > __xnet_u_max_options(user))
		goto tbadopt;
	if (iovcount == 0 || iovcount > _T_IOV_MAX)
		goto tbaddata;
	for (nbytes = 0, i = 0; i < iovcount; nbytes += iov[i].iov_len, i++) ;
	if (nbytes == 0 && !(user->info.flags & T_SNDZERO))
		goto tbaddata;
	if (nbytes > ((flags & T_EXPEDITED) ? __xnet_u_max_etsdu(user) : __xnet_u_max_tsdu(user)))
		goto tbaddata;
	{
		size_t opt_len = (options && options->opt.len > 0) ? options->opt.len : 0;
		int band = (flags & T_EXPEDITED) ? 1 : 0;
		int n = 0, partial = 0;
		struct {
			struct T_optdata_req prim;
			unsigned char opt[opt_len];
		} req;
		struct strbuf ctrl, data;

		ctrl.maxlen = 0;
		ctrl.len = sizeof(req);
		ctrl.buf = (char *) &req;
		data.maxlen = 0;
		data.len = nbytes;
		data.buf = NULL;
		req.prim.PRIM_type = T_OPTDATA_REQ;
		req.prim.DATA_flag =
		    ((flags & T_EXPEDITED) ? T_ODF_EX : 0) | ((flags & T_MORE) ? T_ODF_MORE : 0);
		req.prim.OPT_length = opt_len;
		req.prim.OPT_offset = opt_len ? sizeof(req.prim) : 0;
		if (opt_len)
			memcpy(req.opt, options->opt.buf, opt_len);
		do {
			do {
				data.len = min(__xnet_u_max_tidu(user), iov[n].iov_len - partial);
				data.buf = iov[n].iov_base + partial;
				req.prim.DATA_flag = (((flags & T_EXPEDITED)) ? T_ODF_EX : 0)
				    | (((flags & T_MORE) || (written + data.len < nbytes)) ?
				       T_ODF_MORE : 0);
				if (__xnet_t_putpmsg(fd, &ctrl, &data, band, MSG_BAND) == -1)
					goto error;
				partial += data.len;
				written += data.len;
			} while (partial < iov[n].iov_len);
			n++;
			partial = 0;
		} while (written < nbytes && n < iovcount);
		return (written);
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
#endif
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      tbadopt:
	t_errno = TBADOPT;
	goto error;
      error:
	if (!written)
		return (-1);
	return (written);
}

int
__xnet_t_sndvopt_r(int fd, struct t_optmgmt *options, const struct t_iovec *iov,
		   unsigned int iovcount, int flags)
{
	int ret = -1;

	pthread_cleanup_push_defer_np(__xnet_t_putuser, &fd);
	if (__xnet_t_getuser(fd)) {
		if ((ret = __xnet_t_sndvopt(fd, options, iov, iovcount, flags)) == -1)
			pthread_testcancel();
		__xnet_t_putuser(&fd);
	} else
		pthread_testcancel();
	pthread_cleanup_pop_restore_np(0);
	return (ret);
}

__asm__(".symver __xnet_t_sndvopt_r,t_sndvopt@@XNET_1.0");
#endif

int
__xnet_t_sndvudata(int fd, struct t_unitdata *unitdata, struct t_iovec *iov, unsigned int iovcount)
{
	struct _t_user *user;
	int nbytes, i;

	if (!(user = __xnet_t_tstuser(fd, T_DATA, (1 << T_CLTS), TSF_IDLE)))
		goto error;
	if ((__xnet_t_look(fd) & ~T_DATA) > 0)
		goto tlook;
#ifdef DEBUG
	if (!unitdata || !iov)
		goto einval;
#endif
	if (iovcount == 0 || iovcount > _T_IOV_MAX)
		goto tbaddata;
	for (nbytes = 0, i = 0; i < iovcount; nbytes += iov[i].iov_len, i++) ;
	if (unitdata && unitdata->addr.len > __xnet_u_max_addr(user))
		goto tbadaddr;
	if (unitdata && unitdata->opt.len > __xnet_u_max_options(user))
		goto tbadopt;
	if (nbytes > __xnet_u_max_tsdu(user))
		goto tbaddata;
	if (nbytes > __xnet_u_max_tidu(user))
		goto tbaddata;
	if (nbytes == 0 && !(user->info.flags & T_SNDZERO))
		goto tbaddata;
	{
		size_t add_len = (unitdata && unitdata->addr.len > 0) ? unitdata->addr.len : 0;
		size_t opt_len = (unitdata && unitdata->opt.len > 0) ? unitdata->opt.len : 0;
		int offset, n;
		struct {
			struct T_unitdata_req prim;
			unsigned char addr[add_len];
			unsigned char opt[opt_len];
		} req;
		struct strbuf ctrl, data;

		ctrl.maxlen = 0;
		ctrl.len = sizeof(req);
		ctrl.buf = (char *) &req;
		data.maxlen = 0;
		data.len = nbytes;
		data.buf = NULL;
		if (!(data.buf = (char *) malloc(nbytes)))
			goto enomem;
		for (n = 0, offset = 0; n < iovcount; offset += iov[n].iov_len, n++)
			memcpy(data.buf + offset, iov[n].iov_base, iov[n].iov_len);
		req.prim.PRIM_type = T_UNITDATA_REQ;
		req.prim.DEST_length = add_len;
		req.prim.DEST_offset = add_len ? sizeof(req.prim) : 0;
		req.prim.OPT_length = opt_len;
		req.prim.OPT_offset = opt_len ? sizeof(req.prim) + add_len : 0;
		if (add_len)
			memcpy(req.addr, unitdata->addr.buf, add_len);
		if (opt_len)
			memcpy(req.addr + add_len, unitdata->opt.buf, opt_len);
		if (__xnet_t_putmsg(fd, &ctrl, &data, 0))
			goto error;
		return (0);
	}
#ifdef DEBUG
      einval:
	errno = EINVAL;
	goto tsyserr;
#endif
      enomem:
	errno = ENOMEM;
	goto tsyserr;
      tsyserr:
	t_errno = TSYSERR;
	goto error;
      tbaddata:
	t_errno = TBADDATA;
	goto error;
      tbadopt