/* * 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 #ifdef __hpux # define uint hide_HPs_uint #endif #ifdef __unix # include # ifdef __hpux # undef uint # endif #endif #include #include #ifdef __hpux # define uint hide_HPs_uint #endif #include #ifdef __hpux # undef uint #endif #include #include #include #include "host.h" #ifdef COMPILING_ON_WINDOWS typedef char * caddr_t; # undef IGNORE # include # include "angeldll.h" #else # ifdef __hpux # define uint hide_HPs_uint # endif # include # include # ifdef __hpux # undef uint # endif # include # include # include # if !defined(__hpux) && !defined(__linux__) && !defined(_WIN32) # include # endif # include # include #endif #include "hsys.h" #include "devices.h" #include "endian.h" #include "buffers.h" #include "hostchan.h" #include "params.h" #include "logging.h" #include "ethernet.h" #ifndef COMPILING_ON_WINDOWS /* 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; const char ctrlpacket[] = CTRL_MAGIC; CtrlResponse response; /* * 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); 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=, 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", errno < sys_nerr ? sys_errlist[errno] : "unknown errno"); #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 */