| /* |
| * 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 */ |