738 lines
16 KiB
C
738 lines
16 KiB
C
/*
|
|
* 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 */
|