Files
m68k-bdm/m68k/lib/bdmRemote.c
Bernd Mueller adfd70813f initial push
2026-06-17 13:44:30 +02:00

882 lines
16 KiB
C

/*
* 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 <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/time.h>
#if defined (__WIN32__)
#include <winsock2.h>
#else
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#if !defined (__CYGWIN__)
#include <netinet/tcp.h>
#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;
}