blob: b3156b226cd8b44829c130a91a19a234a0f40bbd [file] [log] [blame]
/*
* Copyright (C) 1995 Advanced RISC Machines Limited. All rights reserved.
*
* This software may be freely used, copied, modified, and distributed
* provided that the above copyright notice is preserved in all copies of the
* software.
*/
/* -*-C-*-
*
* $Revision$
* $Date$
*
*
* etherdrv.c - Ethernet Driver for Angel.
*/
#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)
/* These two might not work for windows. */
extern int sys_nerr;
extern char * sys_errlist[];
#endif
#ifndef UNUSED
# define UNUSED(x) (x = x) /* Silence compiler warnings */
#endif
/*
* forward declarations of static functions
*/
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);
/*
* the device descriptor for Ethernet
*/
DeviceDescr angel_EthernetDevice =
{
"Ethernet",
EthernetOpen,
EthernetMatch,
EthernetClose,
EthernetRead,
EthernetWrite,
EthernetIoctl
};
/*
* descriptor for the socket that we talk down
*/
static int sock = -1;
/*
* address of the remote target
*/
static struct sockaddr_in remote, *ia = &remote;
/*
* array of dynamic port numbers on target
*/
static unsigned short int ports[2];
/*
* Function: set_address
* Purpose: Try to get an address into an understandable form
*
* Params:
* Input: addr The address to parse
*
* Output: ia Structure to hold the parsed address
*
* Returns:
* OK: 0
* Error: -1
*/
static int set_address(const char *const addr, struct sockaddr_in *const ia)
{
ia->sin_family = AF_INET;
/*
* Try address as a dotted decimal
*/
ia->sin_addr.s_addr = inet_addr(addr);
/*
* If that failed, try it as a hostname
*/
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;
}
/*
* Function: open_socket
* Purpose: Open a non-blocking UDP socket, and bind it to a port
* assigned by the system.
*
* Params: None
*
* Returns:
* OK: socket descriptor
* Error: -1
*/
static int open_socket(void)
{
int sfd;
#if 0 /* see #if 0 just below -VVV- */
int yesplease = 1;
#endif
struct sockaddr_in local;
/*
* open the socket
*/
#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
/*
* 960731 KWelton
*
* I don't believe that this should be necessary - if we
* use select(), then non-blocking I/O is redundant.
* Unfortunately, select() appears to be broken (under
* Solaris, with a limited amount of time available for
* debug), so this code stays in for the time being
*/
#if 0
/*
* enable non-blocking I/O
*/
if (ioctlsocket(sfd, FIONBIO, &yesplease) < 0)
{
# ifdef DEBUG
perror("ioctl(FIONBIO)");
# endif
closesocket(sfd);
return -1;
}
#endif /* 0/1 */
/*
* bind local address to a system-assigned port
*/
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;
}
/*
* all done
*/
return sfd;
}
/*
* Function: fetch_ports
* Purpose: Request assigned port numbers from remote target
*
* Params: None
*
* Returns: Nothing
*
* Post-conditions: This routine will *always* return something for the
* port numbers. If the remote target does not
* respond, then it makes something up - this allows
* the standard error message (from ardi.c) to be
* generated when the target is dead for whatever
* reason.
*/
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));
/*
* we will try 3 times to elicit a response from the target
*/
for (i = 0; i < 3; ++i)
{
struct timeval tv;
fd_set fdset;
/*
* send the magic string to the control
* port on the remote target
*/
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))
{
/*
* there is something there - read it
*/
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;
}
}
{
/*
* XXX
*
* this is *very* unpleasant - try to match the structure
* layout
*/
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;
}
}
}
/*
* we failed to get a response
*/
#ifdef DEBUG
printf("fetch_ports: failed to get a real answer\n");
#endif
}
/*
* Function: read_packet
* Purpose: read a packet, and pass it back to higher levels
*
* Params:
* In/Out: packet Holder for the read packet
*
* Returns: 1 - Packet is complete
* 0 - No complete packet read
*
* Post-conditions: Will call panic() if something goes wrong with the OS
*/
static int read_packet(struct data_packet *const packet)
{
struct sockaddr_in from;
int nbytes, fromlen = sizeof(from);
DevChanID devchan;
/*
* try to get the packet
*/
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
/*
* work out where the packet was from
*/
if (from.sin_addr.s_addr != remote.sin_addr.s_addr)
{
/*
* not from our target - ignore it
*/
#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
{
/*
* unknown port number - ignore it
*/
#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
/*
* OK - fill in the details
*/
packet->type = devchan;
packet->len = nbytes;
return 1;
}
/**********************************************************************/
/*
* Function: Ethernet_Open
* Purpose: Open the Ethernet device. See the documentation for
* DeviceOpen in drivers.h
*
* Post-conditions: Will have updated struct sockaddr_in remote (*ia)
* with the address of the remote target.
*/
static int EthernetOpen(const char *name, const char *arg)
{
#ifdef COMPILING_ON_WINDOWS
WORD wVersionRequested;
WSADATA wsaData;
#endif
/*
* name is passed as e=<blah>, so skip 1st two characters
*/
const char *etheraddr = name + 2;
#ifdef DEBUG
printf("EthernetOpen: name `%s'\n", name);
#endif
/* Check that the name is a valid one */
if (EthernetMatch(name, arg) != 0)
return -1;
#ifdef COMPILING_ON_WINDOWS
wVersionRequested = MAKEWORD(1, 1);
if (WSAStartup(wVersionRequested, &wsaData) != 0)
/*
* Couldn't find a useable winsock.dll.
*/
return -1;
if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )
{
WSACleanup();
/*
* Couldn't find a winsock.dll with supported version.
*/
return -1;
}
#endif
memset((char *)ia, 0, sizeof(*ia));
if (set_address(etheraddr, ia) < 0)
{
#ifdef COMPILING_ON_WINDOWS
/*
* SJ - I'm not sure that this is the correct way to handle this
* as Fail calls remote_disable and exits, while panic just exits.
* However at the time of writing remote_disable does nothing!
*/
/* Panic("EthernetOpen: bad name `%s'\n", etheraddr); */
#else
Fail("EthernetOpen: bad name `%s'\n", etheraddr);
#endif
return -1;
}
if ((sock = open_socket()) < 0)
return -1;
/*
* fetch the port numbers assigned by the remote target
* to its Debug and Application sockets
*/
fetch_ports();
return 0;
}
static int EthernetMatch(const char *name, const char *arg)
{
/* IGNORE 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
/* might not work for Windows */
panic("ethernet send failure [%s]\n",
#ifdef STDC_HEADERS
strerror(errno));
#else
errno < sys_nerr ? sys_errlist[errno] : "unknown errno");
#endif /* STDC_HEADERS */
#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
/*
* IGNORE(opcode)
*/
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;
}
}
}
/* EOF etherdrv.c */