/* * Motorola Background Debug Mode Remote Library * Copyright (C) 1998 Chris Johns * * Based on `ser-tcp.c' in the gdb sources. * * 31-11-1999 Chris Johns (cjohns@users.sourceforge.net) * Extended to support remote operation. See bdmRemote.c for details. * * Chris Johns * Objective Design Systems * * cjohns@users.sourceforge.net * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define BDM_REMOTE_TRACE 0 #include #include #include #include #include #include #include #include #include #if defined (__WIN32__) #include #else #include #include #include #include #if !defined (__CYGWIN__) #include #endif #endif #include "bdmRemote.h" #define BDM_REMOTE_OPEN_WAIT (15) #define BDM_REMOTE_TIMEOUT (67) #define BDM_REMOTE_BUF_SIZE (4096) /* * Limit of one BDM per process */ static const char *hex = "0123456789abcdef"; /* * Ioctl code translation. */ static const int ioctl_code_table[] = { BDM_INIT, BDM_RESET_CHIP, BDM_RESTART_CHIP, BDM_STOP_CHIP, BDM_STEP_CHIP, BDM_GET_STATUS, BDM_SPEED, BDM_DEBUG, BDM_RELEASE_CHIP, BDM_GO, BDM_READ_REG, BDM_READ_SYSREG, BDM_READ_LONGWORD, BDM_READ_WORD, BDM_READ_BYTE, BDM_WRITE_REG, BDM_WRITE_SYSREG, BDM_WRITE_LONGWORD, BDM_WRITE_WORD, BDM_WRITE_BYTE, BDM_GET_DRV_VER, BDM_GET_CPU_TYPE, BDM_GET_IF_TYPE, BDM_GET_CF_PST, /* At the end to not break existing servers. */ BDM_SET_CF_PST, BDM_READ_CTLREG, BDM_WRITE_CTLREG, BDM_READ_DBREG, BDM_WRITE_DBREG }; /* * Win32 support not provided as standard. */ #if defined (__WIN32__) #define MAXHOSTNAMELEN 64 #define ECONNREFUSED WSAECONNREFUSED #define sleep(_s) Sleep((_s) * 1000000) static int ws_started; int bdmInitWinSock () { if (!ws_started) { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD (2, 2); err = WSAStartup (wVersionRequested, &wsaData); if (err) return 0; ws_started = 1; } return 1; } #endif /* * Like strerror(), but knows about BDM driver errors, too. */ const char * bdmRemoteStrerror (int error_no) { switch (error_no) { case BDM_FAULT_TIMEOUT: return "Remote BDM server timeout"; } return strerror (error_no); } /* * Translation of local ioctl numbers to a protocol independent number * to send over the link. Different client and server operating systems * will generate different numbers. */ static int bdmGenerateIOId (int code) { unsigned int id = 0; while (id < (sizeof (ioctl_code_table) / sizeof (ioctl_code_table[0]))) if (ioctl_code_table[id] == code) return id; else id++; return -1; } static int bdmSocketSend (int fd, char *buf, int buf_len) { int wrote; #if BDM_REMOTE_TRACE bdmPrint ("bdm-remote:send: [%d] %s\n", buf_len, buf); #endif #if defined (__WIN32__) wrote = send (fd, buf, buf_len, 0); #else wrote = write (fd, buf, buf_len); #endif if (wrote < 0) bdmPrint ("bdm-remote:send: socket write failed: %s\n", strerror (errno)); return wrote; } static int bdmRemoteWait (int fd, char *buf, int buf_len) { int numfds; struct timeval tv; fd_set readfds; fd_set exceptfds; int cread; FD_ZERO (&readfds); FD_ZERO (&exceptfds); tv.tv_sec = BDM_REMOTE_TIMEOUT; tv.tv_usec = 0; FD_SET (fd, &readfds); FD_SET (fd, &exceptfds); while (1) { numfds = select (fd + 1, &readfds, 0, &exceptfds, &tv); if (numfds <= 0) { if (errno != EINTR) { if ((errno == 0) && (tv.tv_sec == 0) && (tv.tv_usec == 0)) errno = BDM_FAULT_TIMEOUT; return -1; } } else { memset (buf, 0, buf_len); #if defined (__WIN32__) cread = recv (fd, buf, buf_len - 1, 0); #else cread = read (fd, buf, buf_len - 1); #endif if (cread > 0) { buf[cread] = 0; #if BDM_REMOTE_TRACE bdmPrint ("bdm-remote:wait: [%d] %s\n", cread, buf); #endif return cread; } if (cread < 0) if (errno != EINTR) return -1; } } return -1; } int bdmRemoteName (const char *name) { char lname[MAXHOSTNAMELEN]; char *port; struct hostent *hostent; #if defined (__WIN32__) if (!bdmInitWinSock ()) { errno = ENOENT; return -1; } #endif /* * We need a ':' in the name to be remote. */ if (!strchr (name, ':')) return 0; /* * If the user supplies a port, strip it. */ strncpy (lname, name, MAXHOSTNAMELEN); port = strchr (lname, ':'); if (port) *port = '\0'; hostent = gethostbyname (lname); if (!hostent) { bdmPrint ("bdm-remote:name: host look falied: %s\n", lname); errno = ENOENT; return 0; } return 1; } /* * Open a remote link. The name is of the form : * * host:port/device */ int bdmRemoteOpen (const char *name) { int fd = -1; char lname[256]; char *port_str; char *device = 0; int port = 0; struct hostent *hostent; struct sockaddr_in sockaddr; struct protoent *protoent; struct servent *servent; int reties; int optarg; char buf[BDM_REMOTE_BUF_SIZE]; int buf_len; char *s; #if defined (__WIN32__) if (!bdmInitWinSock ()) { bdmPrint ("bdm-remote:open: failed to initialise WinSock\n"); errno = ENOENT; return -1; } #endif strncpy (lname, name, sizeof (lname)); lname[sizeof (lname) - 1] = 0; /* * There can 2 types of files names passed: * * host:device * host:port:device */ port_str = strchr (lname, ':'); if (port_str) { device = strchr (port_str + 1, ':'); if (!device) device = strchr (port_str + 1, '/'); if (device) { lname[port_str - lname] = '\0'; port = atoi (port_str); } } /* * We cannot have a port of 0. */ if (port == 0) { if ((servent = getservbyname ("bdm", "tcp"))) { port = ntohs (servent->s_port); } } if (port == 0) { port = 6543; } if (!device) device = port_str; if (!device) { bdmPrint ("bdm-remote:open: no device found\n"); errno = ENOENT; return -1; } /* * Get the host name. */ hostent = gethostbyname (lname); if (!hostent) { bdmPrint ("bdm-remote:open: gethostbyname (%s) failed\n", lname); errno = ENOENT; return -1; } /* * Attempt to make a connection for 15 second. That is * 15 attempts a second apart. */ for (reties = 0; reties < BDM_REMOTE_OPEN_WAIT; reties++) { fd = socket (PF_INET, SOCK_STREAM, 0); if (fd < 0) return -1; /* * Allow rapid reuse of this port. */ optarg = 1; if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optarg, sizeof (optarg)) < 0) { int save_errno = errno; close (fd); fd = -1; errno = save_errno; return -1; } /* * Enable TCP keep alive process. */ optarg = 1; if (setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &optarg, sizeof (optarg)) < 0) { int save_errno = errno; close (fd); fd = -1; errno = save_errno; return -1; } sockaddr.sin_family = PF_INET; sockaddr.sin_port = htons (port); memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr, sizeof (struct in_addr)); if (!connect (fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr))) break; close (fd); fd = -1; /* * We retry for ECONNREFUSED because that is often a temporary * condition, which happens when the server is being restarted. */ if (errno != ECONNREFUSED) return -1; sleep (1); } if (reties == BDM_REMOTE_OPEN_WAIT) { bdmPrint ("bdm-remote:open: %s:%d:%s failed\n", lname, port, device); errno = ENXIO; return -1; } protoent = getprotobyname ("tcp"); if (!protoent) { bdmPrint ("bdm-remote:open: getprotobyname failed\n"); close (fd); return -1; } optarg = 1; if (setsockopt (fd, protoent->p_proto, TCP_NODELAY, (char *) &optarg, sizeof (optarg))) { int save_errno = errno; bdmPrint ("bdm-remote:open: setsockopt failed\n"); close (fd); fd = -1; errno = save_errno; return -1; } /* * If we don't do this, then GDB simply exits when the remote * side dies. */ #if !defined (__WIN32__) signal (SIGPIPE, SIG_IGN); #endif /* * Say hello. This will cause the server to open the port. */ buf_len = 1 + sprintf (buf, "HELO %s", device); if (bdmSocketSend (fd, buf, buf_len) != buf_len) { int save_errno = errno; close (fd); fd = -1; errno = save_errno; return -1; } if (bdmRemoteWait (fd, buf, BDM_REMOTE_BUF_SIZE) < 0) { int save_errno = errno; bdmPrint ("bdm-remote:open: wait failed\n"); close (fd); fd = -1; errno = save_errno; return -1; } /* * Unpack the result. */ s = strstr (buf, "HELO"); if (!s) { /* FIXME: need error message */ return -1; } s += sizeof "HELO"; errno = strtoul (s, NULL, 0); if (errno) { int save_errno = errno; bdmRemoteClose (fd); fd = -1; errno = save_errno; } return fd; } /* * Close a remote link. */ int bdmRemoteClose (int fd) { char buf[BDM_REMOTE_BUF_SIZE]; strcpy (buf, "QUIT Later."); bdmSocketSend (fd, buf, strlen (buf) + 1); return close (fd); } /* * Do an int-argument BDM ioctl */ int bdmRemoteIoctlInt (int fd, int code, int *var) { char buf[BDM_REMOTE_BUF_SIZE]; int buf_len; char *s; int id; /* * Get a network id for the IO code. */ id = bdmGenerateIOId (code); if (id < 0) { errno = EINVAL; return -1; } /* * Pack the message and send. */ buf_len = 1 + sprintf (buf, "IOINT 0x%x,0x%x", id, *var); if (bdmSocketSend (fd, buf, buf_len) != buf_len) return -1; if (bdmRemoteWait (fd, buf, BDM_REMOTE_BUF_SIZE) < 0) return -1; /* * Unpack the result. */ s = strstr (buf, "IOINT"); if (!s) { /* FIXME: need error message */ return -1; } s += sizeof "IOINT"; errno = strtoul (s, NULL, 0); s = strchr (s, ',') + 1; *var = strtoul (s, NULL, 0); if (errno) return -1; return 0; } /* * Do a command (no-argument) BDM ioctl */ int bdmRemoteIoctlCommand (int fd, int code) { char buf[BDM_REMOTE_BUF_SIZE]; int buf_len; char *s; int id; /* * Get a network id for the IO code. */ id = bdmGenerateIOId (code); if (id < 0) { errno = EINVAL; return -1; } /* * Pack the message and send. */ buf_len = 1 + sprintf (buf, "IOCMD 0x%x", id); if (bdmSocketSend (fd, buf, buf_len) != buf_len) return -1; if (bdmRemoteWait (fd, buf, BDM_REMOTE_BUF_SIZE) < 0) return -1; /* * Unpack the result. */ s = strstr (buf, "IOCMD"); if (!s) { /* FIXME: need error message */ return -1; } s += sizeof "IOCMD"; errno = strtoul (s, NULL, 0); if (errno) return -1; return 0; } /* * Do a BDMioctl-argument BDM ioctl */ int bdmRemoteIoctlIo (int fd, int code, struct BDMioctl *ioc) { char buf[BDM_REMOTE_BUF_SIZE]; int buf_len; char *s; int id; /* * Get a network id for the IO code. */ id = bdmGenerateIOId (code); if (id < 0) { errno = EINVAL; return -1; } /* * Pack the message and send. */ buf_len = 1 + sprintf (buf, "IOIO 0x%x,0x%x,0x%x", id, ioc->address, ioc->value); if (bdmSocketSend (fd, buf, buf_len) != buf_len) return -1; if (bdmRemoteWait (fd, buf, BDM_REMOTE_BUF_SIZE) < 0) return -1; /* * Unpack the result. */ s = strstr (buf, "IOIO"); if (!s) { /* FIXME: need error message */ return -1; } s += sizeof "IOIO"; errno = strtoul (s, NULL, 0); s = strchr (s, ',') + 1; ioc->address = strtoul (s, NULL, 0); s = strchr (s, ',') + 1; ioc->value = strtoul (s, NULL, 0); if (errno) return -1; return 0; } /* * Do a BDM read */ int bdmRemoteRead (int fd, unsigned char *cbuf, unsigned long nbytes) { char buf[BDM_REMOTE_BUF_SIZE]; int buf_index; int buf_len; unsigned long remote_nbytes; unsigned long bytes; unsigned char octet; char *s; /* * Pack the message and send. For a read send the address and length. * The server will return a protocol status code, the read protocol * label, errno, the length then the data. */ buf_len = 1 + sprintf (buf, "READ %ld", nbytes); if (bdmSocketSend (fd, buf, buf_len) != buf_len) return -1; buf_len = bdmRemoteWait (fd, buf, BDM_REMOTE_BUF_SIZE); if (buf_len < 0) return -1; /* * Unpack the result. */ s = strstr (buf, "READ"); if (!s) { /* FIXME: need error message */ return -1; } s += sizeof "READ"; errno = strtoul (s, NULL, 0); s = strchr (s, ',') + 1; remote_nbytes = strtoul (s, NULL, 0); /* * We could receive an odd number of characters in a buffer * and we need an even number of characters to complete a * byte. So if a single character is left move it to the * start of the buffer and append the next buffer of data. */ if (remote_nbytes) { bytes = 0; buf_index = strchr (s, ',') - buf + 1; while (bytes < nbytes) { if (buf_len < 2) { int new_read; new_read = bdmRemoteWait (fd, buf + buf_len, BDM_REMOTE_BUF_SIZE - buf_len); if (new_read < 0) return -1; buf_len += new_read; } while ((bytes < nbytes) && ((buf_len - buf_index) > 1)) { if (buf[buf_index] > '9') { buf[buf_index] = tolower (buf[buf_index]); octet = buf[buf_index] - 'a' + 10; } else octet = buf[buf_index] - '0'; buf_index++; octet <<= 4; if (buf[buf_index] > '9') { buf[buf_index] = tolower (buf[buf_index]); octet |= buf[buf_index] - 'a' + 10; } else octet |= buf[buf_index] - '0'; buf_index++; *cbuf = octet; cbuf++; bytes++; } if (buf_index < buf_len) { buf[0] = buf[buf_index]; buf_len = 1; } else buf_len = 0; buf_index = 0; } } return nbytes; } /* * Do a BDM write */ int bdmRemoteWrite (int fd, unsigned char *cbuf, unsigned long nbytes) { char buf[BDM_REMOTE_BUF_SIZE]; int buf_len; unsigned long bytes; char *s; /* * Pack the message and send. A write is a matter of * formatting buffers of BDM_REMOTE_BUF_SIZE and streaming them to the * server. This server uses the number of bytes at the start * of the message to detect the number of bytes being sent. */ if (nbytes == 0) return 0; buf_len = sprintf (buf, "WRITE %ld,", nbytes); bytes = 0; while (bytes < nbytes) { while ((bytes < nbytes) && ((BDM_REMOTE_BUF_SIZE - buf_len) > 2)) { buf[buf_len] = hex[(*cbuf >> 4) & 0xf]; buf_len++; buf[buf_len] = hex[*cbuf & 0xf]; buf_len++; cbuf++; bytes++; } buf[buf_len] = 0; if (bdmSocketSend (fd, buf, buf_len) != buf_len) return -1; buf_len = 0; } if (bdmRemoteWait (fd, buf, BDM_REMOTE_BUF_SIZE) < 0) return -1; /* * Unpack the result. */ s = strstr (buf, "WRITE"); if (!s) { /* FIXME: need error message */ return -1; } s += sizeof "WRITE"; errno = strtoul (s, NULL, 0); s = strchr (s, ',') + 1; nbytes = strtoul (s, NULL, 0); return nbytes; }