#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <netat/appletalk.h>
#include <netat/adsp.h>
#include "at_proto.h"
#define SET_ERRNO(e) errno = e
static int at_sndcmd();
static int at_getcmd();
static int at_pollcmd();
int
ADSPaccept(int fd, void *name, int *namelen)
{
int newfd, tmp_errno;
struct {
struct at_inet remaddr;
unsigned short cid;
unsigned short filler;
unsigned long sendseq;
unsigned long attnsendseq;
unsigned long sendwindow;
} accept;
struct adspcmd cmd;
if ((name != NULL) &&
((namelen == NULL) || (*namelen < sizeof(at_inet_t))) ) {
SET_ERRNO(EINVAL);
return -1;
}
l_again:
if (read(fd, &cmd, sizeof(cmd)) < 0)
return -1;
accept.cid = cmd.u.openParams.remoteCID;
accept.sendseq = cmd.u.openParams.sendSeq;
accept.attnsendseq = cmd.u.openParams.attnSendSeq;
accept.sendwindow = cmd.u.openParams.sendWindow;
accept.remaddr = cmd.u.openParams.remoteAddress;
if ((newfd = ADSPsocket(AF_APPLETALK, SOCK_STREAM, 0)) < 0) {
l_err:
tmp_errno = errno;
if (newfd >= 0)
close(newfd);
ADSPlisten(fd, 1);
SET_ERRNO(tmp_errno);
if (tmp_errno == 0)
goto l_again;
return -1;
}
bzero((char*)&cmd, sizeof(cmd));
cmd.csCode = dspOpen;
cmd.u.openParams.remoteCID = accept.cid;
cmd.u.openParams.sendSeq = accept.sendseq;
cmd.u.openParams.attnSendSeq = accept.attnsendseq;
cmd.u.openParams.sendWindow = accept.sendwindow;
cmd.u.openParams.remoteAddress = accept.remaddr;
cmd.u.openParams.ocMode = ocAccept;
if (at_sndcmd(newfd, &cmd, ADSPOPEN) < 0)
goto l_err;
if (cmd.ioResult < 0)
goto l_err;
if (cmd.ioResult == 1) {
if (at_getcmd(newfd, NULL) < 0)
goto l_err;
}
if (name != NULL) {
*((at_inet_t*)name) = accept.remaddr;
*namelen = sizeof(at_inet_t);
}
ADSPlisten(fd, 1);
return newfd;
}
int
ADSPbind(int fd, void *name, int namelen)
{
int len;
at_socket newsock;
newsock = (name == NULL) ? 0 : ((at_inet_t *)name)->socket;
len = sizeof(newsock);
return at_send_to_dev(fd, ADSPBINDREQ, &newsock, &len);
}
int
ADSPclose(int fd)
{
return close(fd);
}
int
ADSPconnect(int fd, void *name, int namelen)
{
struct adspcmd cmd;
if ((name == NULL) || (namelen != sizeof(at_inet_t))) {
SET_ERRNO(EINVAL);
return -1;
}
bzero((char *)&cmd, sizeof(cmd));
cmd.csCode = dspOpen;
cmd.u.openParams.remoteAddress = *((at_inet_t*)name);
cmd.u.openParams.ocMode = ocRequest;
if (at_sndcmd(fd, &cmd, ADSPOPEN) < 0)
return -1;
if (cmd.ioResult < 0)
return -1;
if (cmd.ioResult == 1) {
if (at_getcmd(fd, &cmd) < 0) {
if (cmd.ioResult)
SET_ERRNO(ETIMEDOUT);
return -1;
}
}
return 0;
}
int
ADSPdisconnect(int fd, int abort)
{
struct adspcmd cmd;
bzero((char*)&cmd, sizeof(cmd));
cmd.csCode = dspClose;
cmd.u.closeParams.abort = abort;
if (at_sndcmd(fd, &cmd, ADSPCLOSE) < 0)
return -1;
if (cmd.ioResult < 0)
return -1;
if (cmd.ioResult == 1) {
if (at_getcmd(fd) < 0)
return -1;
}
return 0;
}
int
ADSPfwdreset(int fd)
{
struct adspcmd cmd;
bzero((char*)&cmd, sizeof(cmd));
cmd.csCode = dspReset;
if (at_sndcmd(fd, &cmd, ADSPRESET) < 0)
return -1;
if (cmd.ioResult < 0)
return -1;
if (cmd.ioResult == 1) {
if (at_getcmd(fd) < 0)
return -1;
}
return 0;
}
int
ADSPgetpeername(int fd, void *name, int *namelen)
{
int len;
if ((name == NULL) || (namelen == NULL) || (*namelen < sizeof(at_inet_t))) {
SET_ERRNO(EINVAL);
return -1;
}
len = 0;
if (at_send_to_dev(fd, ADSPGETPEER, name, &len) == -1)
return -1;
*namelen = len;
return 0;
}
int
ADSPgetsockname(int fd, void *name, int *namelen)
{
int len;
if ((name == NULL) || (namelen == NULL) || (*namelen < sizeof(at_inet_t))) {
SET_ERRNO(EINVAL);
return -1;
}
len = 0;
if (at_send_to_dev(fd, ADSPGETSOCK, name, &len) == -1)
return -1;
*namelen = len;
return 0;
}
int
ADSPgetsockopt(int fd, int level, int optname, char *optval, int *optlen)
{
SET_ERRNO(EOPNOTSUPP);
return -1;
}
int
ADSPlisten(int fd, int backlog)
{
struct adspcmd cmd;
bzero((char*)&cmd, sizeof(cmd));
cmd.csCode = dspCLListen;
cmd.socket = (at_socket)backlog;
if (at_sndcmd(fd, &cmd, ADSPCLLISTEN) < 0)
return -1;
if (cmd.ioResult < 0)
return -1;
return 0;
}
int
ADSPrecv(int fd, char *buf, int len, int flags)
{
return ADSPrecvfrom(fd, buf, len, flags, NULL, 0);
}
int
ADSPrecvfrom(int fd, char *buf, int len, int flags, void *from, int fromlen)
{
int msgflags;
strbuf_t msgdata;
struct timeval polltime;
struct adspcmd cmd;
if (len == 0)
return 0;
if (buf == NULL) {
SET_ERRNO(EINVAL);
return -1;
}
bzero((char *)&cmd, sizeof(cmd));
if (flags & 0x01) {
cmd.csCode = dspAttention;
cmd.u.attnParams.attnSize = len-2;
cmd.u.attnParams.attnData = (unsigned char *)0;
if (at_sndcmd(fd, &cmd, ADSPATTENTION) < 0)
return -1;
} else {
cmd.csCode = dspRead;
cmd.u.ioParams.reqCount = len;
cmd.u.ioParams.dataPtr = (unsigned char *)0;
if (at_sndcmd(fd, &cmd, ADSPREAD) < 0)
return -1;
}
if (cmd.ioResult < 0)
return -1;
if (cmd.ioResult == 1) {
if (flags & 01) {
polltime.tv_sec = 0;
polltime.tv_usec = 0;
if (at_pollcmd(fd, &polltime) == 0)
return 0;
SET_ERRNO(EPROTOTYPE);
return -1;
}
if (at_getcmd(fd, &cmd) < 0)
return -1;
else if ((cmd.csCode != dspRead) && (cmd.csCode != dspAttention)) {
SET_ERRNO(EPROTOTYPE);
return -1;
}
}
if (flags & 01) {
len = cmd.u.attnParams.attnSize;
buf[0] = (char)(cmd.u.attnParams.attnCode >> 8);
buf[1] = (char)(cmd.u.attnParams.attnCode);
if (len > 0) {
msgdata.maxlen = len;
msgdata.len = 0;
msgdata.buf = &buf[2];
msgflags = 0;
if (ATgetmsg(fd, 0, &msgdata, &msgflags) < 0)
len = -1;
else
len = msgdata.len + 2;
} else
len = 2;
} else {
len = cmd.u.ioParams.actCount;
if (len > 0)
len = read(fd, buf, len);
}
if (from && (fromlen >= sizeof(at_inet_t)))
ADSPgetpeername(fd, from, &fromlen);
return len;
}
#define MAX_PKTSIZE 0x1000
int
ADSPsendto(int fd, char *buf, int len, int flags, void *to, int tolen)
{
struct adspcmd cmd;
int size, snd_len;
struct iovec iov[2];
if (len == 0)
return 0;
if (buf == NULL) {
SET_ERRNO(EINVAL);
return -1;
}
for (snd_len=0; snd_len < len; snd_len += size) {
bzero((char *)&cmd, sizeof(cmd));
if (flags & 0x01) {
if ((len < 2) || (len > ADSP_MAX_DATA_LEN)) {
SET_ERRNO(ERANGE);
return -1;
}
size = len;
cmd.csCode = dspAttention;
cmd.u.attnParams.attnCode = (((unsigned short)buf[0])<<8)
+ (unsigned short)buf[1];
cmd.u.attnParams.attnSize = size-2;
cmd.u.attnParams.attnData = (unsigned char *)0;
iov[1].iov_base = (caddr_t)&buf[2];
iov[1].iov_len = (int)size-2;
} else {
size = ((len-snd_len) < MAX_PKTSIZE) ? (len-snd_len) : MAX_PKTSIZE;
cmd.csCode = dspWrite;
cmd.u.ioParams.reqCount = size;
cmd.u.ioParams.dataPtr = (unsigned char *)0;
cmd.u.ioParams.flush = 1;
cmd.u.ioParams.eom = (size == (len-snd_len)) ? 1 : 0;
if (cmd.u.ioParams.eom && (flags & 0x10000000))
cmd.u.ioParams.eom = 0;
iov[1].iov_base = (caddr_t)&buf[snd_len];
iov[1].iov_len = (int)size;
}
iov[0].iov_base = (caddr_t)&cmd;
iov[0].iov_len = (int)sizeof(cmd);
while (writev(fd, iov, 2) == -1) {
if (errno != EINTR)
return snd_len ? snd_len : -1;
}
}
return len;
}
int
ADSPsend(int fd, char *buf, int len, int flags)
{
return ADSPsendto(fd, buf, len, flags, NULL, 0);
}
int
ADSPsetsockopt(int fd, int level, int optname, char *optval, int optlen)
{
struct adspcmd cmd;
if ((optval == NULL) || (optlen != sizeof(struct TRoptionParams))) {
SET_ERRNO(EINVAL);
return -1;
}
bzero((char*)&cmd, sizeof(cmd));
cmd.csCode = dspOptions;
cmd.u.optionParams = *(struct TRoptionParams *)optval;
if (at_sndcmd(fd, &cmd, ADSPOPTIONS) < 0)
return -1;
return 0;
}
int
ADSPsocket(int domain, int type, int protocol)
{
int fd, namelen, tmp_errno;
at_inet_t name;
if ((domain != PF_APPLETALK) || (type != SOCK_STREAM) || (protocol != 0)) {
SET_ERRNO(EINVAL);
return -1;
}
fd = ATsocket(ATPROTO_ADSP);
if (fd < 0)
return -1;
namelen = sizeof(name);
if (ADSPgetsockname(fd, &name, &namelen) < 0) {
tmp_errno = errno;
close(fd);
SET_ERRNO(tmp_errno);
return -1;
}
return fd;
}
static int
at_sndcmd(int fd, struct adspcmd *cmd, int _cmd)
{
int rc, len;
len = sizeof(struct adspcmd);
while (((rc = at_send_to_dev(fd,
_cmd, cmd, &len)) < 0) && (errno == EINTR))
;
return rc;
}
static int
at_getcmd(int fd, struct adspcmd *cmd)
{
struct adspcmd cmdbuf;
if (cmd == NULL)
cmd = (struct adspcmd *)&cmdbuf;
while ((read(fd, cmd, sizeof(*cmd)) != sizeof(*cmd)) || cmd->ioResult) {
if (errno != EINTR)
return -1;
}
return 0;
}
static int
at_pollcmd(int fd, struct timeval *polltime)
{
int rc;
fd_set readset;
FD_ZERO(&readset);
FD_SET(fd, &readset);
while ((rc = select(FD_SETSIZE, &readset, NULL, NULL, polltime)) <= 0) {
if (errno != EINTR)
return rc;
}
return 1;
}
int
ASYNCread(int fd, char *buf, int len)
{
struct adspcmd cmd;
if (len == 0)
return 0;
if (buf == NULL) {
SET_ERRNO(EINVAL);
return -1;
}
bzero((char *)&cmd, sizeof(cmd));
cmd.csCode = dspRead;
cmd.u.ioParams.reqCount = len;
cmd.u.ioParams.dataPtr = (unsigned char *)0;
if (at_sndcmd(fd, &cmd, ADSPREAD) < 0)
return -1;
if (cmd.ioResult < 0)
return -1;
if (cmd.ioResult == 1)
len = 0;
else {
len = cmd.u.ioParams.actCount;
if (len > 0)
len = read(fd, buf, len);
}
return len;
}
int
ASYNCread_complete(int fd, char *buf, int len)
{
struct adspcmd cmd;
if (len == 0)
return 0;
if (buf == NULL) {
SET_ERRNO(EINVAL);
return -1;
}
if (at_getcmd(fd, &cmd) < 0)
return -1;
else if (cmd.csCode != dspRead) {
SET_ERRNO(EPROTOTYPE);
return -1;
}
len = cmd.u.ioParams.actCount;
if (len > 0)
len = read(fd, buf, len);
return len;
}