#ifdef __hpux
# define _POSIX_SOURCE 1
# define _HPUX_SOURCE 1
# define _XOPEN_SOURCE 1
#endif
#include <stdio.h>
#ifdef __hpux
# define uint hide_HPs_uint
#endif
#ifdef STDC_HEADERS
# include <unistd.h>
# ifdef __hpux
# undef uint
# endif
#endif
#include <stdlib.h>
#include <string.h>
#ifdef __hpux
# define uint hide_HPs_uint
#endif
#include <fcntl.h>
#ifdef __hpux
# undef uint
#endif
#include <errno.h>
#include <stdarg.h>
#include <ctype.h>
#include "host.h"
#ifdef COMPILING_ON_WINDOWS
typedef char * caddr_t;
# undef IGNORE
# include <winsock.h>
# include "angeldll.h"
#else
# ifdef __hpux
# define uint hide_HPs_uint
# endif
# include <sys/types.h>
# include <sys/socket.h>
# ifdef __hpux
# undef uint
# endif
# include <netdb.h>
# include <sys/time.h>
# include <sys/ioctl.h>
# ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
# endif
# include <netinet/in.h>
# include <arpa/inet.h>
#endif
#include "hsys.h"
#include "devices.h"
#include "angel_endian.h"
#include "buffers.h"
#include "hostchan.h"
#include "params.h"
#include "logging.h"
#include "ethernet.h"
#if !defined(COMPILING_ON_WINDOWS) && !defined(STDC_HEADERS)
extern int sys_nerr;
extern char * sys_errlist[];
#endif
#ifndef UNUSED
# define UNUSED(x) (x = x)
#endif
static int EthernetOpen(const char *name, const char *arg);
static int EthernetMatch(const char *name, const char *arg);
static void EthernetClose(void);
static int EthernetRead(DriverCall *dc, bool block);
static int EthernetWrite(DriverCall *dc);
static int EthernetIoctl(const int opcode, void *args);
DeviceDescr angel_EthernetDevice =
{
"Ethernet",
EthernetOpen,
EthernetMatch,
EthernetClose,
EthernetRead,
EthernetWrite,
EthernetIoctl
};
static int sock = -1;
static struct sockaddr_in remote, *ia = &remote;
static unsigned short int ports[2];
static int set_address(const char *const addr, struct sockaddr_in *const ia)
{
ia->sin_family = AF_INET;
ia->sin_addr.s_addr = inet_addr(addr);
if (ia->sin_addr.s_addr == (u_int)-1)
{
struct hostent *hp = gethostbyname(addr);
if (hp == NULL)
return -1;
(void)memcpy((caddr_t)&ia->sin_addr, hp->h_addr, hp->h_length);
}
return 0;
}
static int open_socket(void)
{
int sfd;
#if 0
int yesplease = 1;
#endif
struct sockaddr_in local;
#ifdef COMPILING_ON_WINDOWS
if ((sfd = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
return -1;
#else
if ((sfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
# ifdef DEBUG
perror("socket");
# endif
return -1;
}
#endif
#if 0
if (ioctlsocket(sfd, FIONBIO, &yesplease) < 0)
{
# ifdef DEBUG
perror("ioctl(FIONBIO)");
# endif
closesocket(sfd);
return -1;
}
#endif
memset((char *)&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(0);
local.sin_addr.s_addr = INADDR_ANY;
if (bind(sfd, (struct sockaddr *)&local, sizeof(local)) < 0)
{
#ifdef DEBUG
perror("bind");
#endif
closesocket(sfd);
return -1;
}
return sfd;
}
static void fetch_ports(void)
{
int i;
char ctrlpacket[10];
CtrlResponse response;
memset (ctrlpacket, 0, 10);
strcpy (ctrlpacket, CTRL_MAGIC);
memset (response, 0, sizeof(CtrlResponse));
for (i = 0; i < 3; ++i)
{
struct timeval tv;
fd_set fdset;
ia->sin_port = htons(CTRL_PORT);
#ifdef DEBUG
printf("CTLR_PORT=0x%04x sin_port=0x%04x\n");
#endif
if (sendto(sock, ctrlpacket, sizeof(ctrlpacket), 0,
(struct sockaddr *)ia, sizeof(*ia)) < 0)
{
#ifdef DEBUG
perror("fetch_ports: sendto");
#endif
return;
}
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
tv.tv_sec = 0;
tv.tv_usec = 250000;
if (select(sock + 1, &fdset, NULL, NULL, &tv) < 0)
{
#ifdef DEBUG
perror("fetch_ports: select");
#endif
return;
}
if (FD_ISSET(sock, &fdset))
{
if (recv(sock, (char *)&response, sizeof(response), 0) < 0)
{
#ifdef COMPILING_ON_WINDOWS
unsigned int werrno = WSAGetLastError();
if (werrno == WSAEWOULDBLOCK || werrno == 0)
#else
if (errno == EWOULDBLOCK)
#endif
{
--i;
continue;
}
else
{
#ifdef DEBUG
perror("fetch_ports: recv");
#endif
return;
}
}
{
unsigned short *sptr = (unsigned short *)(response + RESP_DBUG);
if (strcmp(response, ctrlpacket) == 0)
{
ports[DBUG_INDEX] = htons(*sptr);
sptr++;
ports[APPL_INDEX] = htons(*sptr);
}
#ifdef DEBUG
printf("fetch_ports: got response, DBUG=%d, APPL=%d\n",
ports[DBUG_INDEX], ports[APPL_INDEX]);
#endif
return;
}
}
}
#ifdef DEBUG
printf("fetch_ports: failed to get a real answer\n");
#endif
}
static int read_packet(struct data_packet *const packet)
{
struct sockaddr_in from;
int nbytes, fromlen = sizeof(from);
DevChanID devchan;
if ((nbytes = recvfrom(sock, (char *)(packet->data), packet->buf_len, 0,
(struct sockaddr *)&from, &fromlen)) < 0)
{
#ifdef COMPILING_ON_WINDOWS
if (nbytes == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
MessageBox(GetFocus(), "Error receiving packet\n", "Angel", MB_OK | MB_ICONSTOP);
#else
if (errno != EWOULDBLOCK)
{
# ifdef DEBUG
perror("recv");
# endif
panic("ethernet recv failure");
}
#endif
return 0;
}
#ifdef COMPILING_ON_WINDOWS
if (pfnProgressCallback != NULL && nbytes != SOCKET_ERROR)
{
progressInfo.nRead += nbytes;
(*pfnProgressCallback)(&progressInfo);
}
#endif
if (from.sin_addr.s_addr != remote.sin_addr.s_addr)
{
#ifdef DEBUG
printf("read_packet: ignoring packet from %s\n",
inet_ntoa(from.sin_addr));
#endif
return 0;
}
else if (ntohs(from.sin_port) == ports[DBUG_INDEX])
devchan = DC_DBUG;
else if (ntohs(from.sin_port) == ports[APPL_INDEX])
devchan = DC_APPL;
else
{
#ifdef DEBUG
printf("read_packet: ignore packet from port %hd\n",
htons(from.sin_port));
#endif
return 0;
}
#if defined(DEBUG) && !defined(DO_TRACE)
printf("EthernetRead: %d bytes from %s channel\n",
nbytes, (devchan == DC_DBUG) ? "DBUG" : "APPL");
#endif
#ifdef DO_TRACE
printf("[%d on %d]\n", nbytes, devchan);
{
int i = 0;
unsigned char *cptr = packet->data;
while (i < nbytes)
{
printf("<%02X ", *(cptr++));
if (!(++i % 16))
printf("\n");
}
if (i % 16)
printf("\n");
}
#endif
packet->type = devchan;
packet->len = nbytes;
return 1;
}
static int EthernetOpen(const char *name, const char *arg)
{
#ifdef COMPILING_ON_WINDOWS
WORD wVersionRequested;
WSADATA wsaData;
#endif
const char *etheraddr = name + 2;
#ifdef DEBUG
printf("EthernetOpen: name `%s'\n", name);
#endif
if (EthernetMatch(name, arg) != 0)
return -1;
#ifdef COMPILING_ON_WINDOWS
wVersionRequested = MAKEWORD(1, 1);
if (WSAStartup(wVersionRequested, &wsaData) != 0)
return -1;
if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )
{
WSACleanup();
return -1;
}
#endif
memset((char *)ia, 0, sizeof(*ia));
if (set_address(etheraddr, ia) < 0)
{
#ifdef COMPILING_ON_WINDOWS
#else
Fail("EthernetOpen: bad name `%s'\n", etheraddr);
#endif
return -1;
}
if ((sock = open_socket()) < 0)
return -1;
fetch_ports();
return 0;
}
static int EthernetMatch(const char *name, const char *arg)
{
if (0)
arg = arg;
if (name == NULL)
return -1;
if (tolower(name[0]) != 'e' || name[1] != '=')
return -1;
return 0;
}
static void EthernetClose(void)
{
if (sock >= 0)
{
closesocket(sock);
sock = -1;
}
#ifdef COMPILING_ON_WINDOWS
WSACleanup();
#endif
}
static int EthernetRead(DriverCall *dc, bool block)
{
fd_set fdset;
struct timeval tv;
int err;
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
#ifdef COMPILING_ON_WINDOWS
UNUSED(block);
tv.tv_sec = tv.tv_usec = 0;
#else
tv.tv_sec = 0;
tv.tv_usec = (block ? 10000 : 0);
#endif
err = select(sock + 1, &fdset, NULL, NULL, &tv);
if (err < 0) {
if (errno == EINTR) {
return 0;
}
panic("ethernet select failure (errno=%i)",errno);
return 0;
}
if (FD_ISSET(sock, &fdset))
return read_packet(&dc->dc_packet);
else
return 0;
}
static int EthernetWrite(DriverCall *dc)
{
int nbytes;
struct data_packet *packet = &dc->dc_packet;
if (packet->type == DC_DBUG)
ia->sin_port = htons(ports[DBUG_INDEX]);
else if (packet->type == DC_APPL)
ia->sin_port = htons(ports[APPL_INDEX]);
else
{
panic("EthernetWrite: unknown devchan");
return 0;
}
#if defined(DEBUG) && !defined(DO_TRACE)
printf("EthernetWrite: %d bytes to %s channel\n",
packet->len, (packet->type == DC_DBUG) ? "DBUG" : "APPL");
#endif
#ifdef DO_TRACE
printf("[%d on %d]\n", packet->len, packet->type);
{
int i = 0;
unsigned char *cptr = packet->data;
while (i < packet->len)
{
printf(">%02X ", *(cptr++));
if (!(++i % 16))
printf("\n");
}
if (i % 16)
printf("\n");
}
#endif
if ((nbytes = sendto(sock, (char *)(packet->data), packet->len, 0,
(struct sockaddr *)ia, sizeof(*ia))) != packet->len)
{
#ifdef COMPILING_ON_WINDOWS
if (nbytes == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
#else
if (nbytes < 0 && errno != EWOULDBLOCK)
#endif
{
#ifdef DEBUG
perror("sendto");
#endif
#ifdef COMPILING_ON_WINDOWS
panic("ethernet send failure\n");
#else
panic("ethernet send failure [%s]\n",
#ifdef STDC_HEADERS
strerror(errno));
#else
errno < sys_nerr ? sys_errlist[errno] : "unknown errno");
#endif
#endif
}
#ifdef DEBUG
else if (nbytes >= 0)
fprintf(stderr, "ethernet send: asked for %d, sent %d\n", packet->len, nbytes);
#endif
return 0;
}
#ifdef COMPILING_ON_WINDOWS
if (pfnProgressCallback != NULL && nbytes != SOCKET_ERROR)
{
progressInfo.nWritten += nbytes;
(*pfnProgressCallback)(&progressInfo);
}
#endif
return 1;
}
static int EthernetIoctl(const int opcode, void *args)
{
#ifdef DEBUG
printf( "EthernetIoctl: op %d arg %x\n", opcode, args );
#endif
if (0)
{
int dummy = opcode;
UNUSED(dummy);
}
UNUSED(args);
switch ( opcode )
{
case DC_RESYNC:
{
#ifdef DEBUG
printf( "EthernetIoctl: resync\n" );
#endif
fetch_ports();
return 0;
}
default:
{
return -1;
}
}
}