882 lines
16 KiB
C
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;
|
|
}
|