/* sim_frontpanel.c: simulator frontpanel API | |
Copyright (c) 2015, Mark Pizzolato | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
MARK PIZZOLATO BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
Except as contained in this notice, the name of Mark Pizzolato shall not be | |
used in advertising or otherwise to promote the sale, use or other dealings | |
in this Software without prior written authorization from Mark Pizzolato. | |
05-Feb-15 MP Initial implementation | |
01-Apr-15 MP Added register indirect, mem_examine and mem_deposit | |
03-Apr-15 MP Added logic to pass simulator startup messages in | |
panel error text if the connection to the simulator | |
shuts down while it is starting. | |
04-Apr-15 MP Added mount and dismount routines to connect and | |
disconnect removable media | |
This module provides interface between a front panel application and a simh | |
simulator. Facilities provide ways to gather information from and to | |
observe and control the state of a simulator. | |
The details of the 'wire protocol' are internal to the API interfaces | |
provided here and described in sim_frontpanel.h. These details are subject | |
to change from one sim_frontpanel version to the next, while all efforts | |
will be made to retain any prior sim_frontpanel API interfaces. | |
*/ | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
#include "sim_frontpanel.h" | |
#include <stdio.h> | |
#include <stdarg.h> | |
#include <string.h> | |
#include <ctype.h> | |
#include <stdlib.h> | |
#include <sys/stat.h> | |
#include <errno.h> | |
#include <signal.h> | |
#include <pthread.h> | |
#include "sim_sock.h" | |
#if defined(_WIN32) | |
#include <process.h> | |
#include <windows.h> | |
#include <winerror.h> | |
#define sleep(n) Sleep(n*1000) | |
#define msleep(n) Sleep(n) | |
#define strtoull _strtoui64 | |
#define CLOCK_REALTIME 0 | |
int clock_gettime(int clk_id, struct timespec *tp) | |
{ | |
unsigned long long now, unixbase; | |
unixbase = 116444736; | |
unixbase *= 1000000000; | |
GetSystemTimeAsFileTime((FILETIME*)&now); | |
now -= unixbase; | |
tp->tv_sec = (long)(now/10000000); | |
tp->tv_nsec = (now%10000000)*100; | |
return 0; | |
} | |
#else /* NOT _WIN32 */ | |
#include <unistd.h> | |
#define msleep(n) usleep(1000*n) | |
#include <sys/wait.h> | |
#if defined (__APPLE__) | |
#define HAVE_STRUCT_TIMESPEC 1 /* OSX defined the structure but doesn't tell us */ | |
#endif | |
/* on HP-UX, CLOCK_REALTIME is enum, not preprocessor define */ | |
#if !defined(CLOCK_REALTIME) && !defined(__hpux) | |
#define CLOCK_REALTIME 1 | |
#define NEED_CLOCK_GETTIME 1 | |
#if !defined(HAVE_STRUCT_TIMESPEC) | |
#define HAVE_STRUCT_TIMESPEC 1 | |
#if !defined(_TIMESPEC_DEFINED) | |
#define _TIMESPEC_DEFINED | |
struct timespec { | |
long tv_sec; | |
long tv_nsec; | |
}; | |
#endif /* _TIMESPEC_DEFINED */ | |
#endif /* HAVE_STRUCT_TIMESPEC */ | |
#if defined(NEED_CLOCK_GETTIME) | |
int clock_gettime(int clk_id, struct timespec *tp) | |
{ | |
struct timeval cur; | |
struct timezone foo; | |
gettimeofday (&cur, &foo); | |
tp->tv_sec = cur.tv_sec; | |
tp->tv_nsec = cur.tv_usec*1000; | |
return 0; | |
} | |
#endif /* defined(NEED_CLOCK_GETTIME) */ | |
#endif /* !defined(CLOCK_REALTIME) && !defined(__hpux) */ | |
#endif /* NOT _WIN32 */ | |
typedef struct { | |
char *name; | |
char *device_name; | |
void *addr; | |
size_t size; | |
int indirect; | |
size_t element_count; | |
int *bits; | |
size_t bit_count; | |
} REG; | |
struct PANEL { | |
PANEL *parent; /* Device Panels can have parent panels */ | |
char *path; /* simulator path */ | |
char *config; | |
char *device_name; /* device name */ | |
char *temp_config; | |
char hostport[64]; | |
size_t device_count; | |
PANEL **devices; | |
SOCKET sock; | |
size_t reg_count; | |
REG *regs; | |
char *reg_query; | |
int new_register; | |
size_t reg_query_size; | |
unsigned long long array_element_data; | |
volatile OperationalState State; | |
unsigned long long simulation_time; | |
unsigned long long simulation_time_base; | |
pthread_t io_thread; | |
int io_thread_running; | |
pthread_mutex_t io_lock; | |
pthread_mutex_t io_send_lock; | |
pthread_mutex_t io_command_lock; | |
int command_count; | |
int io_waiting; | |
char *io_response; | |
char *halt_reason; | |
size_t io_response_data; | |
size_t io_response_size; | |
const char *completion_string; | |
pthread_cond_t io_done; | |
pthread_cond_t startup_done; | |
PANEL_DISPLAY_PCALLBACK callback; | |
pthread_t callback_thread; | |
int callback_thread_running; | |
void *callback_context; | |
int usecs_between_callbacks; | |
pthread_t debugflush_thread; | |
int debugflush_thread_running; | |
unsigned int sample_frequency; | |
unsigned int sample_dither_pct; | |
unsigned int sample_depth; | |
int debug; | |
char *simulator_version; | |
int radix; | |
FILE *Debug; | |
#if defined(_WIN32) | |
HANDLE hProcess; | |
DWORD dwProcessId; | |
#else | |
pid_t pidProcess; | |
#endif | |
}; | |
/* | |
* Thread synchronization model: | |
* | |
* Mutex: Role: | |
* io_lock Serialize access to panel state variables | |
* acquired and released in application threads: | |
* _panel_register_query_string, | |
* _panel_establish_register_bits_collection, | |
* _panel_sendf | |
* acquired and released in internal threads: | |
* _panel_callback | |
* _panel_reader | |
* io_send_lock Serializes writes to a panel's sockets so that complete | |
* command/request data can be delivered before another | |
* thread attempts to write to the socket. | |
* acquired and released in: _panel_send | |
* io_command_lock To serialize frontpanel application command requests | |
* acquired and released in: _panel_get_registers, | |
* _panel_sendf_completion | |
* | |
* Condition Var: Sync Mutex: Purpose & Duration: | |
* io_done io_lock | |
* startup_done io_lock Indicate background thread setup is complete. | |
* Once signaled, it is immediately destroyed. | |
*/ | |
static const char *sim_prompt = "sim> "; | |
static const char *register_repeat_prefix = "repeat every "; | |
static const char *register_repeat_stop = "repeat stop"; | |
static const char *register_repeat_stop_all = "repeat stop all"; | |
static const char *register_repeat_units = " usecs "; | |
static const char *register_get_prefix = "show time"; | |
static const char *register_collect_prefix = "collect "; | |
static const char *register_collect_mid1 = " samples every "; | |
static const char *register_collect_mid2 = " cycles dither "; | |
static const char *register_collect_mid3 = " percent "; | |
static const char *register_get_postfix = "sampleout"; | |
static const char *register_get_start = "# REGISTERS-START"; | |
static const char *register_get_end = "# REGISTERS-DONE"; | |
static const char *register_repeat_start = "# REGISTERS-REPEAT-START"; | |
static const char *register_repeat_end = "# REGISTERS-REPEAT-DONE"; | |
static const char *register_dev_echo = "# REGISTERS-FOR-DEVICE:"; | |
static const char *register_ind_echo = "# REGISTER-INDIRECT:"; | |
static const char *command_status = "ECHO Status:%STATUS%-%TSTATUS%"; | |
static const char *command_done_echo = "# COMMAND-DONE"; | |
static int little_endian; | |
static void *_panel_reader(void *arg); | |
static void *_panel_callback(void *arg); | |
static void *_panel_debugflusher(void *arg); | |
static int sim_panel_set_error (PANEL *p, const char *fmt, ...); | |
static pthread_key_t panel_thread_id; | |
#define TN_IAC 0xFFu /* -1 */ /* protocol delim */ | |
#define TN_DONT 0xFEu /* -2 */ /* dont */ | |
#define TN_DO 0xFDu /* -3 */ /* do */ | |
#define TN_WONT 0xFCu /* -4 */ /* wont */ | |
#define TN_WILL 0xFBu /* -5 */ /* will */ | |
#define TN_BIN 0 /* bin */ | |
#define TN_ECHO 1 /* echo */ | |
#define TN_SGA 3 /* sga */ | |
#define TN_CR 015 /* carriage return */ | |
#define TN_LF 012 /* line feed */ | |
#define TN_LINE 34 /* line mode */ | |
static unsigned char mantra[] = { | |
TN_IAC, TN_WILL, TN_LINE, | |
TN_IAC, TN_WILL, TN_SGA, | |
TN_IAC, TN_WILL, TN_ECHO, | |
TN_IAC, TN_WILL, TN_BIN, | |
TN_IAC, TN_DO, TN_BIN | |
}; | |
static void * | |
_panel_malloc (size_t size) | |
{ | |
void *p = malloc (size); | |
if (p == NULL) | |
sim_panel_set_error (NULL, "Out of Memory"); | |
return p; | |
} | |
/* Allow compiler to help validate printf style format arguments */ | |
#if !defined __GNUC__ | |
#define GCC_FMT_ATTR(n, m) | |
#endif | |
#if !defined(GCC_FMT_ATTR) | |
#define GCC_FMT_ATTR(n, m) __attribute__ ((format (__printf__, n, m))) | |
#endif | |
static void __panel_debug (PANEL *p, int dbits, const char *fmt, const char *buf, int bufsize, ...) GCC_FMT_ATTR(3, 6); | |
#define _panel_debug(p, dbits, fmt, buf, bufsize, ...) do { if (p && p->Debug && ((dbits) & p->debug)) __panel_debug (p, dbits, fmt, buf, bufsize, ##__VA_ARGS__);} while (0) | |
static void __panel_vdebug (PANEL *p, int dbits, const char *fmt, const char *buf, int bufsize, va_list arglist) | |
{ | |
size_t obufsize = 10240 + 9*bufsize; | |
while (p && p->Debug && (dbits & p->debug)) { | |
int i, len; | |
struct timespec time_now; | |
char timestamp[32]; | |
char threadname[50]; | |
char *obuf = (char *)_panel_malloc (obufsize); | |
clock_gettime(CLOCK_REALTIME, &time_now); | |
sprintf (timestamp, "%lld.%03d ", (long long)(time_now.tv_sec), (int)(time_now.tv_nsec/1000000)); | |
sprintf (threadname, "%s:%s ", p->parent ? p->device_name : "CPU", (pthread_getspecific (panel_thread_id)) ? (char *)pthread_getspecific (panel_thread_id) : ""); | |
obuf[obufsize - 1] = '\0'; | |
len = vsnprintf (obuf, obufsize - 1, fmt, arglist); | |
if (len < 0) | |
return; | |
/* If the formatted result didn't fit into the buffer, then grow the buffer and try again */ | |
if (len >= (int)(obufsize - 9*bufsize)) { | |
obufsize = len + 1 + 9*bufsize; | |
free (obuf); | |
continue; | |
} | |
for (i=0; i<bufsize; ++i) { | |
switch ((unsigned char)buf[i]) { | |
case TN_CR: | |
sprintf (&obuf[strlen (obuf)], "_TN_CR_"); | |
break; | |
case TN_LF: | |
sprintf (&obuf[strlen (obuf)], "_TN_LF_"); | |
break; | |
case TN_IAC: | |
sprintf (&obuf[strlen (obuf)], "_TN_IAC_"); | |
switch ((unsigned char)buf[i+1]) { | |
case TN_IAC: | |
sprintf (&obuf[strlen (obuf)], "_TN_IAC_"); ++i; | |
break; | |
case TN_DONT: | |
sprintf (&obuf[strlen (obuf)], "_TN_DONT_"); ++i; | |
break; | |
case TN_DO: | |
sprintf (&obuf[strlen (obuf)], "_TN_DO_"); ++i; | |
break; | |
case TN_WONT: | |
sprintf (&obuf[strlen (obuf)], "_TN_WONT_"); ++i; | |
break; | |
case TN_WILL: | |
sprintf (&obuf[strlen (obuf)], "_TN_WILL_"); ++i; | |
break; | |
default: | |
sprintf (&obuf[strlen (obuf)], "_0x%02X_", (unsigned char)buf[i+1]); ++i; | |
break; | |
} | |
switch ((unsigned char)buf[i+1]) { | |
case TN_BIN: | |
sprintf (&obuf[strlen (obuf)], "_TN_BIN_"); ++i; | |
break; | |
case TN_ECHO: | |
sprintf (&obuf[strlen (obuf)], "_TN_ECHO_"); ++i; | |
break; | |
case TN_SGA: | |
sprintf (&obuf[strlen (obuf)], "_TN_SGA_"); ++i; | |
break; | |
case TN_LINE: | |
sprintf (&obuf[strlen (obuf)], "_TN_LINE_"); ++i; | |
break; | |
default: | |
sprintf (&obuf[strlen (obuf)], "_0x%02X_", (unsigned char)buf[i+1]); ++i; | |
break; | |
} | |
break; | |
default: | |
if (isprint((u_char)buf[i])) | |
sprintf (&obuf[strlen (obuf)], "%c", buf[i]); | |
else { | |
sprintf (&obuf[strlen (obuf)], "_"); | |
if ((buf[i] >= 1) && (buf[i] <= 26)) | |
sprintf (&obuf[strlen (obuf)], "^%c", 'A' + buf[i] - 1); | |
else | |
sprintf (&obuf[strlen (obuf)], "\\%03o", (u_char)buf[i]); | |
sprintf (&obuf[strlen (obuf)], "_"); | |
} | |
break; | |
} | |
} | |
fprintf(p->Debug, "%s%s%s\n", timestamp, threadname, obuf); | |
free (obuf); | |
break; | |
} | |
} | |
static void __panel_debug (PANEL *p, int dbits, const char *fmt, const char *buf, int bufsize, ...) | |
{ | |
va_list arglist; | |
va_start (arglist, bufsize); | |
__panel_vdebug (p, dbits, fmt, buf, bufsize, arglist); | |
va_end (arglist); | |
} | |
void | |
sim_panel_debug (PANEL *panel, const char *fmt, ...) | |
{ | |
va_list arglist; | |
va_start (arglist, fmt); | |
__panel_vdebug (panel, DBG_APP, fmt, NULL, 0, arglist); | |
va_end (arglist); | |
} | |
static void * | |
_panel_debugflusher(void *arg) | |
{ | |
PANEL *p = (PANEL*)arg; | |
int flush_interval = 15; | |
int sleeps = 0; | |
pthread_setspecific (panel_thread_id, "debugflush"); | |
pthread_mutex_lock (&p->io_lock); | |
p->debugflush_thread_running = 1; | |
pthread_mutex_unlock (&p->io_lock); | |
pthread_cond_signal (&p->startup_done); /* Signal we're ready to go */ | |
msleep (100); | |
pthread_mutex_lock (&p->io_lock); | |
while (p->sock != INVALID_SOCKET) { | |
pthread_mutex_unlock (&p->io_lock); | |
msleep (1000); | |
pthread_mutex_lock (&p->io_lock); | |
if (0 == (sleeps++)%flush_interval) | |
sim_panel_flush_debug (p); | |
} | |
pthread_mutex_unlock (&p->io_lock); | |
pthread_mutex_lock (&p->io_lock); | |
pthread_setspecific (panel_thread_id, NULL); | |
p->debugflush_thread_running = 0; | |
pthread_mutex_unlock (&p->io_lock); | |
return NULL; | |
} | |
static void | |
_set_debug_file (PANEL *panel, const char *debug_file) | |
{ | |
if (!panel) | |
return; | |
panel->Debug = fopen(debug_file, "w"); | |
if (panel->Debug) | |
setvbuf (panel->Debug, NULL, _IOFBF, 65536); | |
} | |
void | |
sim_panel_set_debug_mode (PANEL *panel, int debug_bits) | |
{ | |
if (panel) | |
panel->debug = debug_bits; | |
} | |
void | |
sim_panel_flush_debug (PANEL *panel) | |
{ | |
if (!panel) | |
return; | |
if (panel->Debug) | |
fflush (panel->Debug); | |
} | |
static int | |
_panel_send (PANEL *p, const char *msg, int len) | |
{ | |
int sent = 0; | |
if (p->sock == INVALID_SOCKET) | |
return sim_panel_set_error (p, "Invalid Socket for write"); | |
pthread_mutex_lock (&p->io_send_lock); | |
while (len) { | |
int bsent = sim_write_sock (p->sock, msg, len); | |
if (bsent < 0) { | |
pthread_mutex_unlock (&p->io_send_lock); | |
return sim_panel_set_error (p, "%s", sim_get_err_sock("Error writing to socket")); | |
} | |
_panel_debug (p, DBG_XMT, "Sent %d bytes: ", msg, bsent, bsent); | |
len -= bsent; | |
msg += bsent; | |
sent += bsent; | |
} | |
pthread_mutex_unlock (&p->io_send_lock); | |
return sent; | |
} | |
static int | |
_panel_sendf (PANEL *p, int *completion_status, char **response, const char *fmt, ...); | |
static int | |
_panel_sendf_completion (PANEL *p, char **response, const char *completion, const char *fmt, ...); | |
static int | |
_panel_register_query_string (PANEL *panel, char **buf, size_t *buf_size) | |
{ | |
size_t i, j, buf_data, buf_needed = 0, reg_count = 0, bit_reg_count = 0; | |
const char *dev; | |
pthread_mutex_lock (&panel->io_lock); | |
buf_needed = 3 + 7 + /* EXECUTE */ | |
strlen (register_get_start) + /* # REGISTERS-START */ | |
strlen (register_get_prefix); /* SHOW TIME */ | |
for (i=0; i<panel->reg_count; i++) { | |
if (panel->regs[i].bits) | |
++bit_reg_count; | |
else { | |
++reg_count; | |
buf_needed += 10 + strlen (panel->regs[i].name) + (panel->regs[i].device_name ? strlen (panel->regs[i].device_name) : 0); | |
if (panel->regs[i].element_count > 0) | |
buf_needed += 4 + 6 /* 6 digit register array index */; | |
if (panel->regs[i].indirect) | |
buf_needed += 12 + strlen (register_ind_echo) + strlen (panel->regs[i].name); | |
} | |
} | |
if (bit_reg_count) | |
buf_needed += 2 + strlen (register_get_postfix); | |
buf_needed += 10 + strlen (register_get_end); /* # REGISTERS-DONE */ | |
if (buf_needed > *buf_size) { | |
free (*buf); | |
*buf = (char *)_panel_malloc (buf_needed); | |
if (!*buf) { | |
panel->State = Error; | |
pthread_mutex_unlock (&panel->io_lock); | |
return -1; | |
} | |
*buf_size = buf_needed; | |
} | |
buf_data = 0; | |
if (reg_count) { | |
sprintf (*buf + buf_data, "EXECUTE %s;%s;", register_get_start, register_get_prefix); | |
buf_data += strlen (*buf + buf_data); | |
} | |
dev = ""; | |
for (i=j=0; i<panel->reg_count; i++) { | |
const char *reg_dev = panel->regs[i].device_name ? panel->regs[i].device_name : ""; | |
if ((panel->regs[i].indirect) || (panel->regs[i].bits)) | |
continue; | |
if (strcmp (dev, reg_dev)) {/* devices are different */ | |
char *tbuf; | |
buf_needed += 4 + strlen (register_dev_echo) + strlen (reg_dev); /* # REGISTERS-for-DEVICE:XXX */ | |
tbuf = (char *)_panel_malloc (buf_needed); | |
if (tbuf == NULL) { | |
panel->State = Error; | |
pthread_mutex_unlock (&panel->io_lock); | |
return -1; | |
} | |
strcpy (tbuf, *buf); | |
free (*buf); | |
*buf = tbuf; | |
sprintf (*buf + buf_data, "%s%s%s;", (i == 0)? "" : ";", register_dev_echo, reg_dev); | |
buf_data += strlen (*buf + buf_data); | |
dev = reg_dev; | |
j = 0; | |
*buf_size = buf_needed; | |
} | |
if (panel->regs[i].element_count == 0) { | |
if (j == 0) | |
sprintf (*buf + buf_data, "E -16 %s %s", dev, panel->regs[i].name); | |
else | |
sprintf (*buf + buf_data, ",%s", panel->regs[i].name); | |
} | |
else { | |
if (j == 0) | |
sprintf (*buf + buf_data, "E -16 %s %s[0:%d]", dev, panel->regs[i].name, (int)(panel->regs[i].element_count-1)); | |
else | |
sprintf (*buf + buf_data, ",%s[0:%d]", panel->regs[i].name, (int)(panel->regs[i].element_count-1)); | |
} | |
++j; | |
buf_data += strlen (*buf + buf_data); | |
} | |
if (buf_data && ((*buf)[buf_data-1] != ';')) { | |
strcpy (*buf + buf_data, ";"); | |
buf_data += strlen (*buf + buf_data); | |
} | |
for (i=j=0; i<panel->reg_count; i++) { | |
const char *reg_dev = panel->regs[i].device_name ? panel->regs[i].device_name : ""; | |
if ((!panel->regs[i].indirect) || (panel->regs[i].bits)) | |
continue; | |
sprintf (*buf + buf_data, "%s%s;E -16 %s %s,$;", register_ind_echo, panel->regs[i].name, reg_dev, panel->regs[i].name); | |
buf_data += strlen (*buf + buf_data); | |
} | |
if (bit_reg_count) { | |
strcpy (*buf + buf_data, register_get_postfix); | |
buf_data += strlen (*buf + buf_data); | |
strcpy (*buf + buf_data, ";"); | |
buf_data += strlen (*buf + buf_data); | |
} | |
strcpy (*buf + buf_data, register_get_end); | |
buf_data += strlen (*buf + buf_data); | |
strcpy (*buf + buf_data, "\r"); | |
buf_data += strlen (*buf + buf_data); | |
*buf_size = buf_data; | |
pthread_mutex_unlock (&panel->io_lock); | |
return 0; | |
} | |
static int | |
_panel_establish_register_bits_collection (PANEL *panel) | |
{ | |
size_t i, buf_data, buf_needed = 0, reg_count = 0, bit_reg_count = 0; | |
int cmd_stat, bits_count = 0; | |
char *buf, *response = NULL; | |
pthread_mutex_lock (&panel->io_lock); | |
for (i=0; i<panel->reg_count; i++) { | |
if (panel->regs[i].bits) | |
buf_needed += 9 + strlen (panel->regs[i].name) + (panel->regs[i].device_name ? strlen (panel->regs[i].device_name) : 0); | |
} | |
buf = (char *)_panel_malloc (buf_needed); | |
if (!buf) { | |
panel->State = Error; | |
pthread_mutex_unlock (&panel->io_lock); | |
return -1; | |
} | |
*buf = '\0'; | |
buf_data = 0; | |
for (i=0; i<panel->reg_count; i++) { | |
if (panel->regs[i].bits) { | |
++bits_count; | |
sprintf (buf + buf_data, "%s%s", (bits_count != 1) ? "," : "", panel->regs[i].indirect ? "-I " : ""); | |
buf_data += strlen (buf + buf_data); | |
if (panel->regs[i].device_name) { | |
sprintf (buf + buf_data, "%s ", panel->regs[i].device_name); | |
buf_data += strlen (buf + buf_data); | |
} | |
sprintf (buf + buf_data, "%s", panel->regs[i].name); | |
buf_data += strlen (buf + buf_data); | |
} | |
} | |
pthread_mutex_unlock (&panel->io_lock); | |
if (_panel_sendf (panel, &cmd_stat, &response, "%s%u%s%u%s%u%s%s\r", register_collect_prefix, panel->sample_depth, | |
register_collect_mid1, panel->sample_frequency, | |
register_collect_mid2, panel->sample_dither_pct, | |
register_collect_mid3, buf)) { | |
sim_panel_set_error (NULL, "Error establishing bit data collection:%s", response); | |
free (response); | |
free (buf); | |
return -1; | |
} | |
free (response); | |
free (buf); | |
return 0; | |
} | |
static PANEL **panels = NULL; | |
static int panel_count = 0; | |
static char *sim_panel_error_buf = NULL; | |
static size_t sim_panel_error_bufsize = 0; | |
static void | |
_panel_cleanup (void) | |
{ | |
while (panel_count) | |
sim_panel_destroy (*panels); | |
} | |
static void | |
_panel_register_panel (PANEL *p) | |
{ | |
if (panel_count == 0) | |
pthread_key_create (&panel_thread_id, free); | |
if (!pthread_getspecific (panel_thread_id)) | |
pthread_setspecific (panel_thread_id, p->device_name ? p->device_name : "PanelCreator"); | |
++panel_count; | |
panels = (PANEL **)realloc (panels, sizeof(*panels)*panel_count); | |
panels[panel_count-1] = p; | |
if (panel_count == 1) | |
atexit (&_panel_cleanup); | |
} | |
static void | |
_panel_deregister_panel (PANEL *p) | |
{ | |
int i; | |
for (i=0; i<panel_count; i++) { | |
if (panels[i] == p) { | |
int j; | |
for (j=i+1; j<panel_count; j++) | |
panels[j-1] = panels[j]; | |
--panel_count; | |
if (panel_count == 0) { | |
free (panels); | |
panels = NULL; | |
pthread_setspecific (panel_thread_id, NULL); | |
pthread_key_delete (panel_thread_id); | |
} | |
break; | |
} | |
} | |
} | |
static PANEL * | |
_sim_panel_create (const char *sim_path, | |
const char *sim_config, | |
size_t device_panel_count, | |
PANEL *simulator_panel, | |
const char *device_name, | |
const char *debug_file) | |
{ | |
PANEL *p = NULL; | |
FILE *fIn = NULL; | |
FILE *fOut = NULL; | |
struct stat statb; | |
char *buf = NULL; | |
int port; | |
int cmd_stat; | |
size_t i, device_num; | |
char hostport[64]; | |
union {int i; char c[sizeof (int)]; } end_test; | |
if (sim_panel_error_buf == NULL) { /* Preallocate an error message buffer */ | |
sim_panel_error_bufsize = 2048; | |
sim_panel_error_buf = (char *) malloc (sim_panel_error_bufsize); | |
if (sim_panel_error_buf == NULL) { | |
sim_panel_error_buf = (char *)"sim_panel_set_error(): Out of Memory\n"; | |
sim_panel_error_bufsize = 0; | |
return NULL; | |
} | |
} | |
if (simulator_panel) { | |
for (device_num=0; device_num < simulator_panel->device_count; ++device_num) | |
if (simulator_panel->devices[device_num] == NULL) | |
break; | |
if (device_num == simulator_panel->device_count) { | |
sim_panel_set_error (NULL, "No free panel devices slots available %s simulator. All %d slots are used.", simulator_panel->path, (int)simulator_panel->device_count); | |
return NULL; | |
} | |
p = (PANEL *)_panel_malloc (sizeof(*p)); | |
if (p == NULL) | |
goto Error_Return; | |
memset (p, 0, sizeof(*p)); | |
_panel_register_panel (p); | |
p->device_name = (char *)_panel_malloc (1 + strlen (device_name)); | |
if (p->device_name == NULL) | |
goto Error_Return; | |
strcpy (p->device_name, device_name); | |
p->parent = simulator_panel; | |
p->Debug = p->parent->Debug; | |
strcpy (p->hostport, simulator_panel->hostport); | |
p->sock = INVALID_SOCKET; | |
} | |
else { | |
end_test.i = 1; /* test endian-ness */ | |
little_endian = (end_test.c[0] != 0); | |
sim_init_sock (); | |
for (port=1024; port < 2048; port++) { | |
SOCKET sock; | |
sprintf (hostport, "%d", port); | |
sock = sim_connect_sock_ex (NULL, hostport, NULL, NULL, SIM_SOCK_OPT_NODELAY | SIM_SOCK_OPT_BLOCKING); | |
if (sock != INVALID_SOCKET) { | |
int sta = 0; | |
while (!sta) { | |
msleep (10); | |
sta = sim_check_conn (sock, 1); | |
} | |
sim_close_sock (sock); | |
if (sta == -1) | |
break; | |
} | |
else | |
break; | |
} | |
if (stat (sim_config, &statb) < 0) { | |
sim_panel_set_error (NULL, "Can't stat simulator configuration '%s': %s", sim_config, strerror(errno)); | |
goto Error_Return; | |
} | |
buf = (char *)_panel_malloc (statb.st_size+1); | |
if (buf == NULL) | |
goto Error_Return; | |
buf[statb.st_size] = '\0'; | |
p = (PANEL *)_panel_malloc (sizeof(*p)); | |
if (p == NULL) | |
goto Error_Return; | |
memset (p, 0, sizeof(*p)); | |
_panel_register_panel (p); | |
p->sock = INVALID_SOCKET; | |
p->path = (char *)_panel_malloc (strlen (sim_path) + 1); | |
if (p->path == NULL) | |
goto Error_Return; | |
strcpy (p->path, sim_path); | |
p->config = (char *)_panel_malloc (strlen (sim_config) + 1); | |
if (p->config == NULL) | |
goto Error_Return; | |
strcpy (p->config, sim_config); | |
fIn = fopen (sim_config, "r"); | |
if (fIn == NULL) { | |
sim_panel_set_error (NULL, "Can't open configuration file '%s': %s", sim_config, strerror(errno)); | |
goto Error_Return; | |
} | |
p->temp_config = (char *)_panel_malloc (strlen (sim_config) + 40); | |
if (p->temp_config == NULL) | |
goto Error_Return; | |
sprintf (p->temp_config, "%s-Panel-%d", sim_config, getpid()); | |
fOut = fopen (p->temp_config, "w"); | |
if (fOut == NULL) { | |
sim_panel_set_error (NULL, "Can't create temporary configuration file '%s': %s", p->temp_config, strerror(errno)); | |
goto Error_Return; | |
} | |
fprintf (fOut, "# Temporary FrontPanel generated simh configuration file\n"); | |
fprintf (fOut, "# Original Configuration File: %s\n", p->config); | |
fprintf (fOut, "# Simulator Path: %s\n", sim_path); | |
while (fgets (buf, statb.st_size, fIn)) | |
fputs (buf, fOut); | |
free (buf); | |
buf = NULL; | |
fclose (fIn); | |
fIn = NULL; | |
fprintf (fOut, "set remote notelnet\n"); | |
if (device_panel_count) | |
fprintf (fOut, "set remote connections=%d\n", (int)device_panel_count+1); | |
fprintf (fOut, "set remote -u telnet=%s\n", hostport); | |
fprintf (fOut, "set remote master\n"); | |
fprintf (fOut, "exit\n"); | |
fclose (fOut); | |
fOut = NULL; | |
} | |
if (debug_file) { | |
_set_debug_file (p, debug_file); | |
sim_panel_set_debug_mode (p, DBG_XMT|DBG_RCV); | |
_panel_debug (p, DBG_XMT|DBG_RCV, "Creating Simulator Process %s\n", NULL, 0, sim_path); | |
if (stat (p->temp_config, &statb) < 0) { | |
sim_panel_set_error (NULL, "Can't stat temporary simulator configuration '%s': %s", p->temp_config, strerror(errno)); | |
goto Error_Return; | |
} | |
buf = (char *)_panel_malloc (statb.st_size+1); | |
if (buf == NULL) | |
goto Error_Return; | |
buf[statb.st_size] = '\0'; | |
fIn = fopen (p->temp_config, "r"); | |
if (fIn == NULL) { | |
sim_panel_set_error (NULL, "Can't open temporary configuration file '%s': %s", p->temp_config, strerror(errno)); | |
goto Error_Return; | |
} | |
_panel_debug (p, DBG_XMT|DBG_RCV, "Using Temporary Configuration File '%s' containing:", NULL, 0, p->temp_config); | |
i = 0; | |
while (fgets (buf, statb.st_size, fIn)) { | |
++i; | |
buf[strlen(buf) - 1] = '\0'; | |
_panel_debug (p, DBG_XMT|DBG_RCV, "Line %2d: %s", NULL, 0, (int)i, buf); | |
} | |
free (buf); | |
buf = NULL; | |
fclose (fIn); | |
fIn = NULL; | |
} | |
if (!simulator_panel) { | |
#if defined(_WIN32) | |
char cmd[2048]; | |
PROCESS_INFORMATION ProcessInfo; | |
STARTUPINFO StartupInfo; | |
sprintf(cmd, "%s%s%s %s%s%s", strchr (sim_path, ' ') ? "\"" : "", sim_path, strchr (sim_path, ' ') ? "\"" : "", strchr (p->temp_config, ' ') ? "\"" : "", p->temp_config, strchr (p->temp_config, ' ') ? "\"" : ""); | |
memset (&ProcessInfo, 0, sizeof(ProcessInfo)); | |
memset (&StartupInfo, 0, sizeof(StartupInfo)); | |
StartupInfo.dwFlags = STARTF_USESTDHANDLES; | |
StartupInfo.hStdInput = INVALID_HANDLE_VALUE; | |
StartupInfo.hStdOutput = INVALID_HANDLE_VALUE; | |
StartupInfo.hStdError = INVALID_HANDLE_VALUE; | |
if (CreateProcessA(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &StartupInfo, &ProcessInfo)) { | |
CloseHandle (ProcessInfo.hThread); | |
p->hProcess = ProcessInfo.hProcess; | |
p->dwProcessId = ProcessInfo.dwProcessId; | |
} | |
else { /* Creation Problem */ | |
sim_panel_set_error (NULL, "CreateProcess Error: %d", GetLastError()); | |
goto Error_Return; | |
} | |
#else | |
p->pidProcess = fork(); | |
if (p->pidProcess == 0) { | |
close (0); close (1); close (2); /* make sure not to pass the open standard handles */ | |
dup (dup (open ("/dev/null", O_RDWR))); /* open standard handles to /dev/null */ | |
if (execlp (sim_path, sim_path, p->temp_config, NULL, NULL)) { | |
perror ("execl"); | |
exit(errno); | |
} | |
} | |
if (p->pidProcess < 0) { | |
p->pidProcess = 0; | |
sim_panel_set_error (NULL, "fork() Error: %s", strerror(errno)); | |
goto Error_Return; | |
} | |
#endif | |
strcpy (p->hostport, hostport); | |
} | |
for (i=0; i<100; i++) { /* Allow up to 10 seconds waiting for simulator to start up */ | |
p->sock = sim_connect_sock_ex (NULL, p->hostport, NULL, NULL, SIM_SOCK_OPT_NODELAY | SIM_SOCK_OPT_BLOCKING); | |
if (p->sock == INVALID_SOCKET) | |
msleep (100); | |
else | |
break; | |
} | |
if (p->sock == INVALID_SOCKET) { | |
if (simulator_panel) { | |
sim_panel_set_error (NULL, "Can't connect to simulator Remote Console on port %s", p->hostport); | |
} | |
else { | |
if (stat (sim_path, &statb) < 0) | |
sim_panel_set_error (NULL, "Can't stat simulator '%s': %s", sim_path, strerror(errno)); | |
else | |
sim_panel_set_error (NULL, "Can't connect to the %s simulator Remote Console on port %s, the simulator process may not have started or the simulator binary can't be found", sim_path, p->hostport); | |
} | |
goto Error_Return; | |
} | |
_panel_debug (p, DBG_XMT|DBG_RCV, "Connected to simulator on %s after %dms", NULL, 0, p->hostport, (int)i*100); | |
pthread_mutex_init (&p->io_lock, NULL); | |
pthread_mutex_init (&p->io_send_lock, NULL); | |
pthread_mutex_init (&p->io_command_lock, NULL); | |
pthread_cond_init (&p->io_done, NULL); | |
pthread_cond_init (&p->startup_done, NULL); | |
if (sizeof(mantra) != _panel_send (p, (char *)mantra, sizeof(mantra))) { | |
sim_panel_set_error (NULL, "Error sending Telnet mantra (options): %s", sim_get_err_sock ("send")); | |
goto Error_Return; | |
} | |
if (1) { | |
pthread_attr_t attr; | |
pthread_attr_init(&attr); | |
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); | |
pthread_mutex_lock (&p->io_lock); | |
p->io_thread_running = 0; | |
pthread_create (&p->io_thread, &attr, _panel_reader, (void *)p); | |
while (!p->io_thread_running) | |
pthread_cond_wait (&p->startup_done, &p->io_lock); /* Wait for thread to stabilize */ | |
if ((p->Debug) && (p->parent == NULL)) { | |
p->debugflush_thread_running = 0; | |
pthread_create (&p->debugflush_thread, &attr, _panel_debugflusher, (void *)p); | |
while (!p->debugflush_thread_running) | |
pthread_cond_wait (&p->startup_done, &p->io_lock); /* Wait for thread to stabilize */ | |
} | |
pthread_mutex_unlock (&p->io_lock); | |
pthread_attr_destroy(&attr); | |
pthread_cond_destroy (&p->startup_done); | |
} | |
if (simulator_panel) { | |
simulator_panel->devices[device_num] = p; | |
} | |
else { | |
if (device_panel_count) { | |
p->devices = (PANEL **)_panel_malloc (device_panel_count*sizeof(*p->devices)); | |
if (p->devices == NULL) | |
goto Error_Return; | |
memset (p->devices, 0, device_panel_count*sizeof(*p->devices)); | |
p->device_count = device_panel_count; | |
} | |
if (p->State == Error) | |
goto Error_Return; | |
/* Validate sim_frontpanel API version */ | |
if (_panel_sendf (p, &cmd_stat, &p->simulator_version, "SHOW VERSION\r")) | |
goto Error_Return; | |
if (1) { | |
int api_version = 0; | |
char *c = strstr (p->simulator_version, "FrontPanel API Version"); | |
if ((!c) || | |
(1 != sscanf (c, "FrontPanel API Version %d", &api_version)) || | |
(api_version != SIM_FRONTPANEL_VERSION)) { | |
sim_panel_set_error (NULL, "Inconsistent sim_frontpanel API version %d in simulator. Version %d needed.-", api_version, SIM_FRONTPANEL_VERSION); | |
goto Error_Return; | |
} | |
} | |
if (1) { | |
char *radix = NULL; | |
if (_panel_sendf (p, &cmd_stat, &radix, "SHOW %s RADIX\r", p->device_name ? p->device_name : "")) { | |
free (radix); | |
goto Error_Return; | |
} | |
sscanf (radix, "Radix=%d", &p->radix); | |
free (radix); | |
if ((p->radix != 16) && (p->radix != 8)) { | |
sim_panel_set_error (NULL, "Unsupported Radix: %d%s%s.", p->radix, p->device_name ? " on device " : "", p->device_name ? p->device_name : ""); | |
goto Error_Return; | |
} | |
} | |
} | |
return p; | |
Error_Return: | |
if (fIn) | |
fclose (fIn); | |
if (fOut) { | |
fclose (fOut); | |
(void)remove (p->temp_config); | |
} | |
if (buf) | |
free (buf); | |
if (1) { | |
const char *err = sim_panel_get_error(); | |
char *errbuf = (char *)_panel_malloc (1 + strlen (err)); | |
strcpy (errbuf, err); /* preserve error info while closing */ | |
sim_panel_destroy (p); | |
sim_panel_set_error (NULL, "%s", errbuf); | |
free (errbuf); | |
} | |
if (!simulator_panel) | |
sim_cleanup_sock(); | |
return NULL; | |
} | |
PANEL * | |
sim_panel_start_simulator_debug (const char *sim_path, | |
const char *sim_config, | |
size_t device_panel_count, | |
const char *debug_file) | |
{ | |
return _sim_panel_create (sim_path, sim_config, device_panel_count, NULL, NULL, debug_file); | |
} | |
PANEL * | |
sim_panel_start_simulator (const char *sim_path, | |
const char *sim_config, | |
size_t device_panel_count) | |
{ | |
return sim_panel_start_simulator_debug (sim_path, sim_config, device_panel_count, NULL); | |
} | |
PANEL * | |
sim_panel_add_device_panel_debug (PANEL *simulator_panel, | |
const char *device_name, | |
const char *debug_file) | |
{ | |
return _sim_panel_create (NULL, NULL, 0, simulator_panel, device_name, debug_file); | |
} | |
PANEL * | |
sim_panel_add_device_panel (PANEL *simulator_panel, | |
const char *device_name) | |
{ | |
return sim_panel_add_device_panel_debug (simulator_panel, device_name, NULL); | |
} | |
int | |
sim_panel_destroy (PANEL *panel) | |
{ | |
REG *reg; | |
if (panel) { | |
_panel_debug (panel, DBG_XMT|DBG_RCV, "Closing Panel %s", NULL, 0, panel->device_name? panel->device_name : panel->path); | |
if (panel->devices) { | |
size_t i; | |
for (i=0; i<panel->device_count; i++) { | |
if (panel->devices[i]) { | |
sim_panel_destroy (panel->devices[i]); | |
panel->devices[i] = NULL; | |
} | |
} | |
free (panel->devices); | |
panel->devices = NULL; | |
} | |
if (panel->sock != INVALID_SOCKET) { | |
SOCKET sock = panel->sock; | |
int wait_count; | |
/* First, wind down the automatic register queries */ | |
sim_panel_set_display_callback_interval (panel, NULL, NULL, 0); | |
/* Next, attempt a simulator shutdown only with the master panel */ | |
if (panel->parent == NULL) { | |
if (panel->State == Run) | |
sim_panel_exec_halt (panel); | |
_panel_send (panel, "EXIT\r", 5); | |
} | |
/* Wait for up to 2 seconds for a graceful shutdown */ | |
panel->sock = INVALID_SOCKET; | |
for (wait_count=0; panel->io_thread_running && (wait_count<20); ++wait_count) | |
msleep (100); | |
/* Now close the socket which should stop a pending read that hasn't completed */ | |
sim_close_sock (sock); | |
pthread_join (panel->io_thread, NULL); | |
} | |
if ((panel->Debug) && (panel->parent == NULL)) | |
pthread_join (panel->debugflush_thread, NULL); | |
pthread_mutex_destroy (&panel->io_lock); | |
pthread_mutex_destroy (&panel->io_send_lock); | |
pthread_mutex_destroy (&panel->io_command_lock); | |
pthread_cond_destroy (&panel->io_done); | |
#if defined(_WIN32) | |
if (panel->hProcess) { | |
GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, panel->dwProcessId); | |
msleep (200); | |
TerminateProcess (panel->hProcess, 0); | |
WaitForSingleObject (panel->hProcess, INFINITE); | |
CloseHandle (panel->hProcess); | |
} | |
#else | |
if (panel->pidProcess) { | |
int status; | |
if (!kill (panel->pidProcess, 0)) { | |
kill (panel->pidProcess, SIGTERM); | |
msleep (200); | |
if (!kill (panel->pidProcess, 0)) | |
kill (panel->pidProcess, SIGKILL); | |
} | |
waitpid (panel->pidProcess, &status, 0); | |
} | |
#endif | |
free (panel->path); | |
free (panel->device_name); | |
free (panel->config); | |
if (panel->temp_config) | |
(void)remove (panel->temp_config); | |
free (panel->temp_config); | |
reg = panel->regs; | |
while (panel->reg_count--) { | |
free (reg->name); | |
free (reg->device_name); | |
reg++; | |
} | |
free (panel->regs); | |
free (panel->reg_query); | |
free (panel->io_response); | |
free (panel->halt_reason); | |
free (panel->simulator_version); | |
if ((panel->Debug) && (!panel->parent)) | |
fclose (panel->Debug); | |
if (!panel->parent) | |
sim_cleanup_sock (); | |
_panel_deregister_panel (panel); | |
free (panel); | |
} | |
return 0; | |
} | |
OperationalState | |
sim_panel_get_state (PANEL *panel) | |
{ | |
if (!panel) | |
return Halt; | |
return panel->State; | |
} | |
static int | |
_panel_add_register (PANEL *panel, | |
const char *name, | |
const char *device_name, | |
size_t size, | |
void *addr, | |
int indirect, | |
size_t element_count, | |
int *bits, | |
size_t bit_count) | |
{ | |
REG *regs, *reg; | |
char *response = NULL, *c; | |
unsigned long long data; | |
size_t i; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
if ((bit_count != 0) && (panel->sample_depth == 0)) { | |
sim_panel_set_error (NULL, "sim_panel_set_sampling_parameters() must be called first"); | |
return -1; | |
} | |
regs = (REG *)_panel_malloc ((1 + panel->reg_count)*sizeof(*regs)); | |
if (regs == NULL) | |
return sim_panel_set_error (panel, "_panel_add_register(): Out of Memory\n"); | |
pthread_mutex_lock (&panel->io_lock); | |
memcpy (regs, panel->regs, panel->reg_count*sizeof(*regs)); | |
reg = ®s[panel->reg_count]; | |
memset (reg, 0, sizeof(*regs)); | |
reg->name = (char *)_panel_malloc (1 + strlen (name)); | |
if (reg->name == NULL) { | |
panel->State = Error; | |
pthread_mutex_unlock (&panel->io_lock); | |
sim_panel_set_error (NULL, "_panel_add_register(): Out of Memory\n"); | |
free (regs); | |
return -1; | |
} | |
strcpy (reg->name, name); | |
reg->indirect = indirect; | |
reg->addr = addr; | |
reg->size = size; | |
reg->element_count = element_count; | |
reg->bits = bits; | |
reg->bit_count = bit_count; | |
for (i=0; i<strlen (reg->name); i++) { | |
if (islower (reg->name[i])) | |
reg->name[i] = toupper (reg->name[i]); | |
} | |
if (device_name) { | |
reg->device_name = (char *)_panel_malloc (1 + strlen (device_name)); | |
if (reg->device_name == NULL) { | |
free (reg->name); | |
free (regs); | |
pthread_mutex_unlock (&panel->io_lock); | |
return sim_panel_set_error (panel, "_panel_add_register(): Out of Memory\n"); | |
} | |
strcpy (reg->device_name, device_name); | |
for (i=0; i<strlen (reg->device_name); i++) { | |
if (islower (reg->device_name[i])) | |
reg->device_name[i] = toupper (reg->device_name[i]); | |
} | |
} | |
for (i=0; i<panel->reg_count; i++) { | |
char *t1 = (char *)_panel_malloc (2 + strlen (regs[i].name) + (regs[i].device_name? strlen (regs[i].device_name) : 0)); | |
char *t2 = (char *)_panel_malloc (2 + strlen (reg->name) + (reg->device_name? strlen (reg->device_name) : 0)); | |
if ((t1 == NULL) || (t2 == NULL)) { | |
free (t1); | |
free (t2); | |
free (reg->name); | |
free (reg->device_name); | |
panel->State = Error; | |
free (regs); | |
pthread_mutex_unlock (&panel->io_lock); | |
return sim_panel_set_error (NULL, "_panel_add_register(): Out of Memory\n"); | |
} | |
sprintf (t1, "%s %s", regs[i].device_name ? regs[i].device_name : "", regs[i].name); | |
sprintf (t2, "%s %s", reg->device_name ? reg->device_name : "", reg->name); | |
if ((!strcmp (t1, t2)) && | |
(reg->indirect == regs[i].indirect) && | |
((reg->bits == NULL) == (regs[i].bits == NULL))) { | |
pthread_mutex_unlock (&panel->io_lock); | |
sim_panel_set_error (NULL, "Duplicate Register Declaration"); | |
free (t1); | |
free (t2); | |
free (reg->name); | |
free (reg->device_name); | |
free (regs); | |
return -1; | |
} | |
free (t1); | |
free (t2); | |
} | |
pthread_mutex_unlock (&panel->io_lock); | |
/* Validate existence of requested register/array */ | |
if (_panel_sendf (panel, &cmd_stat, &response, "EXAMINE -H %s %s%s\r", device_name? device_name : "", name, (element_count > 0) ? "[0]" : "")) { | |
free (reg->name); | |
free (reg->device_name); | |
free (regs); | |
return -1; | |
} | |
c = strchr (response, ':'); | |
if ((!strcmp ("Invalid argument\r\n", response)) || (!c)) { | |
sim_panel_set_error (NULL, "Invalid Register: %s %s", device_name? device_name : "", name); | |
free (response); | |
free (reg->name); | |
free (reg->device_name); | |
free (regs); | |
return -1; | |
} | |
data = strtoull (c + 1, NULL, 16); | |
free (response); | |
if (element_count > 0) { | |
if (_panel_sendf (panel, &cmd_stat, &response, "EXAMINE %s %s[%d]\r", device_name? device_name : "", name, element_count-1)) { | |
free (reg->name); | |
free (reg->device_name); | |
free (regs); | |
return -1; | |
} | |
if (!strcmp ("Subscript out of range\r\n", response)) { | |
sim_panel_set_error (NULL, "Invalid Register Array Dimension: %s %s[%d]", device_name? device_name : "", name, element_count-1); | |
free (response); | |
free (reg->name); | |
free (reg->device_name); | |
free (regs); | |
return -1; | |
} | |
free (response); | |
} | |
pthread_mutex_lock (&panel->io_lock); | |
++panel->reg_count; | |
free (panel->regs); | |
panel->regs = regs; | |
panel->new_register = 1; | |
pthread_mutex_unlock (&panel->io_lock); | |
/* Now build the register query string for the whole register list */ | |
if (_panel_register_query_string (panel, &panel->reg_query, &panel->reg_query_size)) | |
return -1; | |
if (bits) { | |
for (i=0; i<bit_count; i++) | |
bits[i] = (data & (1LL<<i)) ? panel->sample_depth : 0; | |
if (_panel_establish_register_bits_collection (panel)) | |
return -1; | |
} | |
return 0; | |
} | |
int | |
sim_panel_add_register (PANEL *panel, | |
const char *name, | |
const char *device_name, | |
size_t size, | |
void *addr) | |
{ | |
return _panel_add_register (panel, name, device_name, size, addr, 0, 0, NULL, 0); | |
} | |
int | |
sim_panel_add_register_bits (PANEL *panel, | |
const char *name, | |
const char *device_name, | |
size_t bit_width, | |
int *bits) | |
{ | |
return _panel_add_register (panel, name, device_name, 0, NULL, 0, 0, bits, bit_width); | |
} | |
int | |
sim_panel_add_register_array (PANEL *panel, | |
const char *name, | |
const char *device_name, | |
size_t element_count, | |
size_t size, | |
void *addr) | |
{ | |
return _panel_add_register (panel, name, device_name, size, addr, 0, element_count, NULL, 0); | |
} | |
int | |
sim_panel_add_register_indirect (PANEL *panel, | |
const char *name, | |
const char *device_name, | |
size_t size, | |
void *addr) | |
{ | |
return _panel_add_register (panel, name, device_name, size, addr, 1, 0, NULL, 0); | |
} | |
int | |
sim_panel_add_register_indirect_bits (PANEL *panel, | |
const char *name, | |
const char *device_name, | |
size_t bit_width, | |
int *bits) | |
{ | |
return _panel_add_register (panel, name, device_name, 0, NULL, 1, 0, bits, bit_width); | |
} | |
static int | |
_panel_get_registers (PANEL *panel, int calledback, unsigned long long *simulation_time) | |
{ | |
if ((!panel) || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if ((!calledback) && (panel->callback)) { | |
sim_panel_set_error (NULL, "Callback provides register data"); | |
return -1; | |
} | |
if (!panel->reg_count) { | |
sim_panel_set_error (NULL, "No registers specified"); | |
return -1; | |
} | |
pthread_mutex_lock (&panel->io_command_lock); | |
pthread_mutex_lock (&panel->io_lock); | |
if (panel->reg_query_size != _panel_send (panel, panel->reg_query, panel->reg_query_size)) { | |
pthread_mutex_unlock (&panel->io_lock); | |
pthread_mutex_unlock (&panel->io_command_lock); | |
return -1; | |
} | |
if (panel->io_response_data) | |
_panel_debug (panel, DBG_RCV, "Receive Data Discarded: ", panel->io_response, panel->io_response_data); | |
panel->io_response_data = 0; | |
panel->io_waiting = 1; | |
while (panel->io_waiting) | |
pthread_cond_wait (&panel->io_done, &panel->io_lock); | |
if (simulation_time) | |
*simulation_time = panel->simulation_time; | |
pthread_mutex_unlock (&panel->io_lock); | |
pthread_mutex_unlock (&panel->io_command_lock); | |
return 0; | |
} | |
int | |
sim_panel_get_registers (PANEL *panel, unsigned long long *simulation_time) | |
{ | |
return _panel_get_registers (panel, (panel->State == Halt), simulation_time); | |
} | |
int | |
sim_panel_set_display_callback_interval (PANEL *panel, | |
PANEL_DISPLAY_PCALLBACK callback, | |
void *context, | |
int usecs_between_callbacks) | |
{ | |
if (!panel) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
pthread_mutex_lock (&panel->io_lock); /* acquire access */ | |
panel->callback = callback; | |
panel->callback_context = context; | |
if (usecs_between_callbacks && (0 == panel->usecs_between_callbacks)) { /* Need to start/enable callbacks */ | |
pthread_attr_t attr; | |
_panel_debug (panel, DBG_THR, "Starting callback thread, Interval: %d usecs", NULL, 0, usecs_between_callbacks); | |
panel->usecs_between_callbacks = usecs_between_callbacks; | |
pthread_cond_init (&panel->startup_done, NULL); | |
pthread_attr_init(&attr); | |
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); | |
pthread_create (&panel->callback_thread, &attr, _panel_callback, (void *)panel); | |
pthread_attr_destroy(&attr); | |
while (!panel->callback_thread_running) | |
pthread_cond_wait (&panel->startup_done, &panel->io_lock); /* Wait for thread to stabilize */ | |
pthread_cond_destroy (&panel->startup_done); | |
} | |
if ((usecs_between_callbacks == 0) && panel->usecs_between_callbacks) { /* Need to stop callbacks */ | |
_panel_debug (panel, DBG_THR, "Shutting down callback thread", NULL, 0); | |
panel->usecs_between_callbacks = 0; /* flag disabled */ | |
pthread_mutex_unlock (&panel->io_lock); /* allow access */ | |
pthread_join (panel->callback_thread, NULL); /* synchronize with thread rundown */ | |
pthread_mutex_lock (&panel->io_lock); /* reacquire access */ | |
} | |
pthread_mutex_unlock (&panel->io_lock); | |
return 0; | |
} | |
int | |
sim_panel_set_sampling_parameters_ex (PANEL *panel, | |
unsigned int sample_frequency, | |
unsigned int sample_dither_pct, | |
unsigned int sample_depth) | |
{ | |
if (sample_frequency == 0) { | |
sim_panel_set_error (NULL, "Invalid sample frequency value: %u", sample_frequency); | |
return -1; | |
} | |
if (sample_dither_pct > 25) { | |
sim_panel_set_error (NULL, "Invalid sample dither percentage value: %u", sample_dither_pct); | |
return -1; | |
} | |
if (sample_depth == 0) { | |
sim_panel_set_error (NULL, "Invalid sample depth value: %u", sample_depth); | |
return -1; | |
} | |
panel->sample_frequency = sample_frequency; | |
panel->sample_dither_pct = sample_dither_pct; | |
panel->sample_depth = sample_depth; | |
return 0; | |
} | |
int | |
sim_panel_set_sampling_parameters (PANEL *panel, | |
unsigned int sample_frequency, | |
unsigned int sample_depth) | |
{ | |
return sim_panel_set_sampling_parameters_ex (panel, | |
sample_frequency, | |
5, | |
sample_depth); | |
} | |
int | |
sim_panel_exec_halt (PANEL *panel) | |
{ | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->parent) { | |
sim_panel_set_error (NULL, "Can't HALT simulator from device front panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
if (_panel_sendf_completion (panel, NULL, sim_prompt, "\005")) { | |
_panel_debug (panel, DBG_THR, "Error trying to HALT running simulator: %s", NULL, 0, sim_panel_get_error ()); | |
return -1; | |
} | |
if (panel->State == Run) { | |
_panel_debug (panel, DBG_THR, "Unable to HALT running simulator", NULL, 0); | |
return -1; | |
} | |
} | |
return 0; | |
} | |
const char * | |
sim_panel_halt_text (PANEL *panel) | |
{ | |
if (!panel || !panel->halt_reason) | |
return ""; | |
return panel->halt_reason; | |
} | |
int | |
sim_panel_exec_boot (PANEL *panel, const char *device) | |
{ | |
int cmd_stat; | |
char *response, *simtime; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->parent) { | |
sim_panel_set_error (NULL, "Can't BOOT simulator from device front panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
/* A BOOT or RUN command will restart the simulator's time base. */ | |
/* We account for that so that the frontpanel application sees ever */ | |
/* increasing time values when register data is delivered. */ | |
if (_panel_sendf (panel, &cmd_stat, &response, "SHOW TIME\r")) | |
return -1; | |
if ((simtime = strstr (response, "Time:"))) { | |
panel->simulation_time = strtoull (simtime + 5, NULL, 10); | |
panel->simulation_time_base += panel->simulation_time; | |
} | |
free (response); | |
if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "BOOT %s\r", device)) { | |
_panel_debug (panel, DBG_THR, "Unable to BOOT simulator: %s", NULL, 0, sim_panel_get_error()); | |
return -1; | |
} | |
return 0; | |
} | |
int | |
sim_panel_exec_start (PANEL *panel) | |
{ | |
int cmd_stat; | |
char *response, *simtime; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->parent) { | |
sim_panel_set_error (NULL, "Can't RUN simulator from device front panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
/* A BOOT or RUN command will restart the simulator's time base. */ | |
/* We account for that so that the frontpanel application sees ever */ | |
/* increasing time values when register data is delivered. */ | |
if (_panel_sendf (panel, &cmd_stat, &response, "SHOW TIME\r")) { | |
_panel_debug (panel, DBG_THR, "Unable to send SHOW TIME command while starting simulator: %s", NULL, 0, sim_panel_get_error()); | |
return -1; | |
} | |
if ((simtime = strstr (response, "Time:"))) { | |
panel->simulation_time = strtoull (simtime + 5, NULL, 10); | |
panel->simulation_time_base += panel->simulation_time; | |
} | |
free (response); | |
panel->simulation_time_base += panel->simulation_time; | |
if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "RUN\r", 5)) { | |
_panel_debug (panel, DBG_THR, "Unable to start simulator: %s", NULL, 0, sim_panel_get_error()); | |
return -1; | |
} | |
return 0; | |
} | |
int | |
sim_panel_exec_run (PANEL *panel) | |
{ | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->parent) { | |
sim_panel_set_error (NULL, "Can't CONT simulator from device front panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "CONT\r")) | |
return -1; | |
return 0; | |
} | |
int | |
sim_panel_exec_step (PANEL *panel) | |
{ | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->parent) { | |
sim_panel_set_error (NULL, "Can't STEP simulator from device front panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
if (_panel_sendf_completion (panel, NULL, sim_prompt, "STEP")) { | |
_panel_debug (panel, DBG_THR, "Error trying to STEP running simulator: %s", NULL, 0, sim_panel_get_error ()); | |
return -1; | |
} | |
return 0; | |
} | |
int | |
sim_panel_break_set (PANEL *panel, const char *condition) | |
{ | |
char *response = NULL; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->parent) { | |
sim_panel_set_error (NULL, "Can't establish a breakpoint from device front panel"); | |
return -1; | |
} | |
if ((_panel_sendf (panel, &cmd_stat, &response, "BREAK %s\r", condition)) || | |
(*response)) { | |
sim_panel_set_error (NULL, "Error establishing breakpoint at '%s': %s", condition, response ? response : ""); | |
free (response); | |
return -1; | |
} | |
free (response); | |
return 0; | |
} | |
int | |
sim_panel_break_clear (PANEL *panel, const char *condition) | |
{ | |
char *response = NULL; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->parent) { | |
sim_panel_set_error (NULL, "Can't clear a breakpoint from device front panel"); | |
return -1; | |
} | |
if ((_panel_sendf (panel, &cmd_stat, &response, "NOBREAK %s\r", condition)) || | |
(*response)) { | |
sim_panel_set_error (NULL, "Error clearing breakpoint at '%s': %s", condition, response ? response : ""); | |
free (response); | |
return -1; | |
} | |
free (response); | |
return 0; | |
} | |
int | |
sim_panel_break_output_set (PANEL *panel, const char *condition) | |
{ | |
char *response = NULL; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->parent) { | |
sim_panel_set_error (NULL, "Can't establish an output breakpoint from device front panel"); | |
return -1; | |
} | |
if ((_panel_sendf (panel, &cmd_stat, &response, "EXPECT %s\r", condition)) || | |
(*response)) { | |
sim_panel_set_error (NULL, "Error establishing output breakpoint for '%s': %s", condition, response ? response : ""); | |
free (response); | |
return -1; | |
} | |
free (response); | |
return 0; | |
} | |
int | |
sim_panel_break_output_clear (PANEL *panel, const char *condition) | |
{ | |
char *response = NULL; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->parent) { | |
sim_panel_set_error (NULL, "Can't clear an output breakpoint from device front panel"); | |
return -1; | |
} | |
if ((_panel_sendf (panel, &cmd_stat, &response, "NOEXPECT %s\r", condition)) || | |
(*response)) { | |
sim_panel_set_error (NULL, "Error clearing output breakpoint for '%s': %s", condition, response ? response : ""); | |
free (response); | |
return -1; | |
} | |
free (response); | |
return 0; | |
} | |
/** | |
sim_panel_gen_examine | |
name_or_addr the name the simulator knows this register by | |
size the size (in local storage) of the buffer which will | |
receive the data returned when examining the simulator | |
value a pointer to the buffer which will be loaded with the | |
data returned when examining the simulator | |
*/ | |
int | |
sim_panel_gen_examine (PANEL *panel, | |
const char *name_or_addr, | |
size_t size, | |
void *value) | |
{ | |
char *response = NULL, *c; | |
unsigned long long data = 0; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
if (_panel_sendf (panel, &cmd_stat, &response, "EXAMINE -H %s", name_or_addr)) { | |
free (response); | |
return -1; | |
} | |
c = strchr (response, ':'); | |
if (!c) { | |
sim_panel_set_error (NULL, "response: %s", response); | |
free (response); | |
return -1; | |
} | |
data = strtoull (c + 1, NULL, 16); | |
if (little_endian) | |
memcpy (value, &data, size); | |
else | |
memcpy (value, ((char *)&data) + sizeof(data)-size, size); | |
free (response); | |
return 0; | |
} | |
/** | |
sim_panel_get_history | |
count the number of instructions to return | |
size the size (in local storage) of the buffer which will | |
receive the data returned when examining the simulator | |
buffer a pointer to the buffer which will be loaded with the | |
instruction history returned from the simulator | |
*/ | |
int | |
sim_panel_get_history (PANEL *panel, | |
int count, | |
size_t size, | |
char *buffer) | |
{ | |
char *response = NULL; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
if (_panel_sendf (panel, &cmd_stat, &response, "SHOW HISTORY=%d", count)) { | |
free (response); | |
return -1; | |
} | |
strncpy (buffer, response, size); | |
free (response); | |
return 0; | |
} | |
int | |
sim_panel_device_debug_mode (PANEL *panel, | |
const char *device, | |
int set_unset, | |
const char *mode_bits) | |
{ | |
char *response = NULL; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if ((device != NULL) && | |
((_panel_sendf (panel, &cmd_stat, &response, "SHOW %s", device) || | |
(cmd_stat)))) { | |
sim_panel_set_error (NULL, "Can't %s Debug Mode: '%s' on Device '%s': %s", | |
set_unset ? "Enable" : "Disable", mode_bits ? mode_bits : "", device, response); | |
free (response); | |
return -1; | |
} | |
free (response); | |
response = NULL; | |
if (_panel_sendf (panel, &cmd_stat, &response, "%sDEBUG %s %s", | |
set_unset ? "" : "NO", device ? device : "", mode_bits ? mode_bits : "") || | |
(cmd_stat)) { | |
sim_panel_set_error (NULL, "Can't %s Debug Mode: '%s' on Device '%s': %s", | |
set_unset ? "Enable" : "Disable", mode_bits ? mode_bits : "", device, response); | |
free (response); | |
return -1; | |
} | |
free (response); | |
return 0; | |
} | |
/** | |
sim_panel_gen_deposit | |
name_or_addr the name the simulator knows this register by | |
size the size (in local storage) of the buffer which | |
contains the data to be deposited into the simulator | |
value a pointer to the buffer which contains the data to | |
be deposited into the simulator | |
*/ | |
int | |
sim_panel_gen_deposit (PANEL *panel, | |
const char *name_or_addr, | |
size_t size, | |
const void *value) | |
{ | |
unsigned long long data = 0; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
if (little_endian) | |
memcpy (&data, value, size); | |
else | |
memcpy (((char *)&data) + sizeof(data)-size, value, size); | |
if (_panel_sendf (panel, &cmd_stat, NULL, "DEPOSIT -H %s %llx", name_or_addr, data)) | |
return -1; | |
return 0; | |
} | |
/** | |
sim_panel_mem_examine | |
addr_size the size (in local storage) of the buffer which | |
contains the memory address of the data to be examined | |
in the simulator | |
addr a pointer to the buffer containing the memory address | |
of the data to be examined in the simulator | |
value_size the size (in local storage) of the buffer which will | |
receive the data returned when examining the simulator | |
value a pointer to the buffer which will be loaded with the | |
data returned when examining the simulator | |
*/ | |
int | |
sim_panel_mem_examine (PANEL *panel, | |
size_t addr_size, | |
const void *addr, | |
size_t value_size, | |
void *value) | |
{ | |
char *response = NULL, *c; | |
unsigned long long data = 0, address = 0; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
if (little_endian) | |
memcpy (&address, addr, addr_size); | |
else | |
memcpy (((char *)&address) + sizeof(address)-addr_size, addr, addr_size); | |
if (_panel_sendf (panel, &cmd_stat, &response, (panel->radix == 16) ? "EXAMINE -H %llx" : "EXAMINE -H %llo", address)) { | |
free (response); | |
return -1; | |
} | |
c = strchr (response, ':'); | |
if (!c) { | |
sim_panel_set_error (NULL, "%s", response); | |
free (response); | |
return -1; | |
} | |
data = strtoull (c + 1, NULL, 16); | |
if (little_endian) | |
memcpy (value, &data, value_size); | |
else | |
memcpy (value, ((char *)&data) + sizeof(data)-value_size, value_size); | |
free (response); | |
return 0; | |
} | |
/** | |
sim_panel_mem_deposit | |
addr_size the size (in local storage) of the buffer which | |
contains the memory address of the data to be deposited | |
into the simulator | |
addr a pointer to the buffer containing the memory address | |
of the data to be deposited into the simulator | |
value_size the size (in local storage) of the buffer which will | |
contains the data to be deposited into the simulator | |
value a pointer to the buffer which contains the data to be | |
deposited into the simulator | |
*/ | |
int | |
sim_panel_mem_deposit (PANEL *panel, | |
size_t addr_size, | |
const void *addr, | |
size_t value_size, | |
const void *value) | |
{ | |
unsigned long long data = 0, address = 0; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
if (little_endian) { | |
memcpy (&data, value, value_size); | |
memcpy (&address, addr, addr_size); | |
} | |
else { | |
memcpy (((char *)&data) + sizeof(data)-value_size, value, value_size); | |
memcpy (((char *)&address) + sizeof(address)-addr_size, addr, addr_size); | |
} | |
if (_panel_sendf (panel, &cmd_stat, NULL, (panel->radix == 16) ? "DEPOSIT -H %llx %llx" : "DEPOSIT -H %llo %llx", address, data)) | |
return -1; | |
return 0; | |
} | |
/** | |
sim_panel_mem_deposit_instruction | |
addr_size the size (in local storage) of the buffer which | |
contains the memory address of the data to be deposited | |
into the simulator | |
addr a pointer to the buffer containing the memory address | |
of the data to be deposited into the simulator | |
instruction a pointer to the buffer that contains the mnemonic | |
instruction to be deposited at the indicated address | |
*/ | |
int | |
sim_panel_mem_deposit_instruction (PANEL *panel, | |
size_t addr_size, | |
const void *addr, | |
const char *instruction) | |
{ | |
unsigned long long address = 0; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
if (little_endian) | |
memcpy (&address, addr, addr_size); | |
else | |
memcpy (((char *)&address) + sizeof(address)-addr_size, addr, addr_size); | |
if (_panel_sendf (panel, &cmd_stat, NULL, (panel->radix == 16) ? "DEPOSIT -H %llx %s" : "DEPOSIT -H %llo %s", address, instruction)) | |
return -1; | |
return 0; | |
} | |
/** | |
sim_panel_set_register_value | |
name the name of a simulator register or a memory address | |
which is to receive a new value | |
value the new value in character string form. The string | |
must be in the native/natural radix that the simulator | |
uses when referencing that register | |
*/ | |
int | |
sim_panel_set_register_value (PANEL *panel, | |
const char *name, | |
const char *value) | |
{ | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
if (panel->State == Run) { | |
sim_panel_set_error (NULL, "Not Halted"); | |
return -1; | |
} | |
if (_panel_sendf (panel, &cmd_stat, NULL, "DEPOSIT %s %s", name, value)) | |
return -1; | |
return 0; | |
} | |
/** | |
sim_panel_mount | |
device the name of a simulator device/unit | |
switches any switches appropriate for the desire attach | |
path the path on the local system to be attached | |
*/ | |
int | |
sim_panel_mount (PANEL *panel, | |
const char *device, | |
const char *switches, | |
const char *path) | |
{ | |
char *response = NULL; | |
OperationalState OrigState; | |
int stat = 0; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
OrigState = panel->State; | |
if (OrigState == Run) | |
sim_panel_exec_halt (panel); | |
do { | |
if (_panel_sendf (panel, &cmd_stat, &response, "ATTACH %s %s %s", switches, device, path)) { | |
stat = -1; | |
break; | |
} | |
if (cmd_stat) { | |
sim_panel_set_error (NULL, response); | |
stat = -1; | |
} | |
} while (0); | |
if (OrigState == Run) | |
sim_panel_exec_run (panel); | |
free (response); | |
return stat; | |
} | |
/** | |
sim_panel_dismount | |
device the name of a simulator device/unit | |
*/ | |
int | |
sim_panel_dismount (PANEL *panel, | |
const char *device) | |
{ | |
char *response = NULL; | |
OperationalState OrigState; | |
int stat = 0; | |
int cmd_stat; | |
if (!panel || (panel->State == Error)) { | |
sim_panel_set_error (NULL, "Invalid Panel"); | |
return -1; | |
} | |
OrigState = panel->State; | |
if (OrigState == Run) | |
sim_panel_exec_halt (panel); | |
do { | |
if (_panel_sendf (panel, &cmd_stat, &response, "DETACH %s", device)) { | |
stat = -1; | |
break; | |
} | |
if (cmd_stat) { | |
sim_panel_set_error (NULL, "%s", response); | |
stat = -1; | |
} | |
} while (0); | |
if (OrigState == Run) | |
sim_panel_exec_run (panel); | |
free (response); | |
return stat; | |
} | |
static void * | |
_panel_reader(void *arg) | |
{ | |
PANEL *p = (PANEL*)arg; | |
REG *r = NULL; | |
int sched_policy; | |
struct sched_param sched_priority; | |
char buf[4096]; | |
int buf_data = 0; | |
int processing_register_output = 0; | |
int io_wait_done = 0; | |
/* | |
Boost Priority for this response processing thread to quickly digest | |
arriving data. | |
*/ | |
pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); | |
++sched_priority.sched_priority; | |
pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); | |
pthread_setspecific (panel_thread_id, "reader"); | |
_panel_debug (p, DBG_THR, "Starting", NULL, 0); | |
buf[buf_data] = '\0'; | |
pthread_mutex_lock (&p->io_lock); | |
if (!p->parent) { | |
while (1) { | |
int new_data = sim_read_sock (p->sock, &buf[buf_data], sizeof(buf)-(buf_data+1)); | |
if (new_data <= 0) { | |
sim_panel_set_error (NULL, "%s after reading %d bytes: %s", sim_get_err_sock("Unexpected socket read"), buf_data, buf); | |
_panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); | |
p->State = Error; | |
break; | |
} | |
_panel_debug (p, DBG_RCV, "Startup receive of %d bytes: ", &buf[buf_data], new_data, new_data); | |
buf_data += new_data; | |
buf[buf_data] = '\0'; | |
if (!memcmp (mantra, buf, sizeof (mantra))) { /* strip initial telnet mantra from input stream */ | |
memmove (buf, buf + sizeof (mantra), 1 + buf_data - sizeof (mantra)); | |
buf_data -= sizeof (mantra); | |
} | |
if ((size_t)buf_data < strlen (sim_prompt)) | |
continue; | |
if (!strcmp (sim_prompt, &buf[buf_data - strlen (sim_prompt)])) { | |
memmove (buf, &buf[buf_data - strlen (sim_prompt)], strlen (sim_prompt) + 1); | |
buf_data = strlen (sim_prompt); | |
break; | |
} | |
} | |
} | |
p->io_thread_running = 1; | |
pthread_mutex_unlock (&p->io_lock); | |
pthread_cond_signal (&p->startup_done); /* Signal we're ready to go */ | |
msleep (100); | |
pthread_mutex_lock (&p->io_lock); | |
while ((p->sock != INVALID_SOCKET) && | |
(p->State != Error)) { | |
int new_data; | |
char *s, *e, *eol; | |
if (NULL == strchr (buf, '\n')) { | |
pthread_mutex_unlock (&p->io_lock); | |
new_data = sim_read_sock (p->sock, &buf[buf_data], sizeof(buf)-(buf_data+1)); | |
pthread_mutex_lock (&p->io_lock); | |
if (new_data <= 0) { | |
sim_panel_set_error (NULL, "%s", sim_get_err_sock("Unexpected socket read")); | |
_panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); | |
p->State = Error; | |
break; | |
} | |
_panel_debug (p, DBG_RCV, "Received %d bytes: ", &buf[buf_data], new_data, new_data); | |
buf_data += new_data; | |
buf[buf_data] = '\0'; | |
} | |
s = buf; | |
while ((eol = strchr (s, '\n'))) { | |
/* Line to process */ | |
*eol++ = '\0'; | |
while ((*s) && (s[strlen(s)-1] == '\r')) | |
s[strlen(s)-1] = '\0'; | |
if (processing_register_output) { | |
e = strchr (s, ':'); | |
if (e) { | |
size_t i; | |
char smp_dev[32], smp_reg[32], smp_ind[32]; | |
unsigned int bit; | |
*e++ = '\0'; | |
if (!strcmp("Time", s)) { | |
p->simulation_time = strtoull (e, NULL, 10); | |
s = eol; | |
while (isspace(0xFF & (*s))) | |
++s; | |
continue; /* process next line */ | |
} | |
if ((*s == '}') && | |
(3 == sscanf (s, "}%s %s %s", smp_dev, smp_reg, smp_ind))) { /* Register bit Sample Data? */ | |
r = NULL; | |
for (i=0; i<p->reg_count; i++) { | |
if (p->regs[i].bits == NULL) | |
continue; | |
if ((!strcmp (smp_reg, p->regs[i].name)) && | |
((!p->device_name) || (!strcmp (smp_dev, p->device_name)))) { | |
r = &p->regs[i]; | |
break; | |
} | |
} | |
if (r) { | |
for (bit = 0; bit < r->bit_count; bit++) { | |
int val = (int)strtol (e, &e, 10); | |
r->bits[bit] = val; | |
if (*e == ',') | |
++e; | |
else | |
break; | |
} | |
s = eol; | |
} | |
while (isspace(0xFF & (*s))) | |
++s; | |
r = NULL; | |
continue; /* process next line */ | |
} | |
if (!strncmp (s + strlen (sim_prompt), register_ind_echo, strlen (register_ind_echo) - 1)) { | |
e = s + strlen (sim_prompt) + strlen (register_ind_echo); | |
r = NULL; | |
for (i=0; i<p->reg_count; i++) { | |
if (p->regs[i].indirect && (!strcmp(p->regs[i].name, e))) { | |
r = &p->regs[i]; | |
break; | |
} | |
} | |
s = eol; | |
while (isspace(0xFF & (*s))) | |
++s; | |
if (r) | |
continue; /* process next line */ | |
} | |
if (r) { | |
if (strcmp (s, r->name)) { | |
unsigned long long data; | |
data = strtoull (e, NULL, 16); | |
if (little_endian) | |
memcpy (r->addr, &data, r->size); | |
else | |
memcpy (r->addr, ((char *)&data) + sizeof(data)-r->size, r->size); | |
r = NULL; | |
} | |
s = eol; | |
while (isspace(0xFF & (*s))) | |
++s; | |
continue; /* process next line */ | |
} | |
for (i=0; i<p->reg_count; i++) { | |
if (p->regs[i].element_count == 0) { | |
if (!strcmp(p->regs[i].name, s)) { | |
unsigned long long data; | |
data = strtoull (e, NULL, 16); | |
if (little_endian) | |
memcpy (p->regs[i].addr, &data, p->regs[i].size); | |
else | |
memcpy (p->regs[i].addr, ((char *)&data) + sizeof(data)-p->regs[i].size, p->regs[i].size); | |
break; | |
} | |
} | |
else { | |
size_t name_len = strlen (p->regs[i].name); | |
if ((0 == memcmp (p->regs[i].name, s, name_len)) && (s[name_len] == '[')) { | |
size_t array_index = (size_t)atoi (s + name_len + 1); | |
size_t end_index = array_index; | |
char *end = strchr (s + name_len + 1, '['); | |
if (end) | |
end_index = (size_t)atoi (end + 1); | |
if (strcmp (e, " same as above")) | |
p->array_element_data = strtoull (e, NULL, 16); | |
while (array_index <= end_index) { | |
if (little_endian) | |
memcpy ((char *)(p->regs[i].addr) + (array_index * p->regs[i].size), &p->array_element_data, p->regs[i].size); | |
else | |
memcpy ((char *)(p->regs[i].addr) + (array_index * p->regs[i].size), ((char *)&p->array_element_data) + sizeof(p->array_element_data)-p->regs[i].size, p->regs[i].size); | |
++array_index; | |
} | |
break; | |
} | |
} | |
} | |
if (i != p->reg_count) { | |
s = eol; | |
while (isspace(0xFF & (*s))) | |
++s; | |
continue; | |
} | |
--e; | |
*e = ':'; | |
/* Unexpected Register Data Found (or other output containing a : character) */ | |
} | |
} | |
if ((strlen (s) > strlen (sim_prompt)) && (!strcmp (s + strlen (sim_prompt), register_repeat_end))) { | |
_panel_debug (p, DBG_RCV, "*Repeat Block Complete (Accumulated Data = %d)", NULL, 0, (int)p->io_response_data); | |
if (p->callback) { | |
pthread_mutex_unlock (&p->io_lock); | |
p->callback (p, p->simulation_time_base + p->simulation_time, p->callback_context); | |
pthread_mutex_lock (&p->io_lock); | |
} | |
processing_register_output = 0; | |
p->io_response_data = 0; | |
p->io_response[p->io_response_data] = '\0'; | |
goto Start_Next_Line; | |
} | |
if ((strlen (s) > strlen (sim_prompt)) && | |
((!strcmp (s + strlen (sim_prompt), register_repeat_start)) || | |
(!strcmp (s + strlen (sim_prompt), register_get_start)))) { | |
_panel_debug (p, DBG_RCV, "*Repeat/Register Block Starting", NULL, 0); | |
processing_register_output = 1; | |
goto Start_Next_Line; | |
} | |
if ((strlen (s) > strlen (sim_prompt)) && | |
(!strcmp (s + strlen (sim_prompt), register_get_end))) { | |
_panel_debug (p, DBG_RCV, "*Register Block Complete", NULL, 0); | |
p->io_waiting = 0; | |
processing_register_output = 0; | |
pthread_cond_signal (&p->io_done); | |
goto Start_Next_Line; | |
} | |
if ((strlen (s) > strlen (sim_prompt)) && (!strcmp (s + strlen (sim_prompt), command_done_echo))) { | |
_panel_debug (p, DBG_RCV, "*Received Command Complete", NULL, 0); | |
p->io_waiting = 0; | |
pthread_cond_signal (&p->io_done); | |
goto Start_Next_Line; | |
} | |
/* Non Register Data Found (echo of EXAMINE or other commands and/or command output) */ | |
if (p->io_response_data + strlen (s) + 3 > p->io_response_size) { | |
char *t = (char *)_panel_malloc (p->io_response_data + strlen (s) + 3); | |
if (t == NULL) { | |
_panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); | |
p->State = Error; | |
break; | |
} | |
memcpy (t, p->io_response, p->io_response_data); | |
free (p->io_response); | |
p->io_response = t; | |
p->io_response_size = p->io_response_data + strlen (s) + 3; | |
} | |
_panel_debug (p, DBG_RCV, "Receive Data Accumulated: '%s'", NULL, 0, s); | |
strcpy (p->io_response + p->io_response_data, s); | |
p->io_response_data += strlen(s); | |
strcpy (p->io_response + p->io_response_data, "\r\n"); | |
p->io_response_data += 2; | |
if ((!p->parent) && | |
(p->completion_string) && | |
(!memcmp (s, p->completion_string, strlen (p->completion_string)))) { | |
_panel_debug (p, DBG_RCV, "Match with potentially coalesced additional data: '%s'", NULL, 0, p->completion_string); | |
if (eol < &buf[buf_data]) | |
memset (s + strlen (s), ' ', eol - (s + strlen (s))); | |
break; | |
} | |
Start_Next_Line: | |
s = eol; | |
while (isspace(0xFF & (*s))) | |
++s; | |
} | |
memmove (buf, s, buf_data - (s - buf) + 1); | |
buf_data = strlen (buf); | |
if (buf_data) | |
_panel_debug (p, DBG_RSP, "Remnant Buffer Contents: '%s'", NULL, 0, buf); | |
if ((!p->parent) && | |
(p->completion_string) && | |
(!memcmp (buf, p->completion_string, strlen (p->completion_string)))) { | |
_panel_debug (p, DBG_RCV, "*Received Command Complete - Match: '%s'", NULL, 0, p->completion_string); | |
io_wait_done = 1; | |
} | |
if (!memcmp ("Simulator Running...", buf, 20)) { | |
_panel_debug (p, DBG_RSP, "State transitioning to Run", NULL, 0); | |
p->State = Run; | |
buf_data -= 20; | |
if (buf_data) { | |
memmove (buf, buf + 20, buf_data + 1); | |
_panel_debug (p, DBG_RSP, "Remnant Buffer Contents: '%s'", NULL, 0, buf); | |
} | |
else | |
buf[buf_data] = '\0'; | |
if (io_wait_done) { /* someone waiting for this? */ | |
_panel_debug (p, DBG_RCV, "*Match Command Complete - Match signaling waiting thread", NULL, 0); | |
io_wait_done = 0; | |
p->io_waiting = 0; | |
p->completion_string = NULL; | |
pthread_cond_signal (&p->io_done); | |
/* Let this state transition propagate to the interested thread(s) */ | |
/* before processing remaining buffered data */ | |
pthread_mutex_unlock (&p->io_lock); | |
msleep (100); | |
pthread_mutex_lock (&p->io_lock); | |
} | |
} | |
if ((p->State == Run) && (!strcmp (buf, sim_prompt))) { | |
_panel_debug (p, DBG_RSP, "State transitioning to Halt: io_wait_done: %d", NULL, 0, io_wait_done); | |
p->State = Halt; | |
free (p->halt_reason); | |
p->halt_reason = (char *)_panel_malloc (1 + strlen (p->io_response)); | |
if (p->halt_reason == NULL) { | |
_panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); | |
p->State = Error; | |
break; | |
} | |
strcpy (p->halt_reason, p->io_response); | |
} | |
if (io_wait_done) { | |
_panel_debug (p, DBG_RCV, "*Match Command Complete - Match signaling waiting thread", NULL, 0); | |
io_wait_done = 0; | |
p->io_waiting = 0; | |
p->completion_string = NULL; | |
pthread_cond_signal (&p->io_done); | |
} | |
} | |
if (p->io_waiting) { | |
_panel_debug (p, DBG_THR, "Receive: restarting waiting thread while exiting", NULL, 0); | |
p->io_waiting = 0; | |
pthread_cond_signal (&p->io_done); | |
} | |
_panel_debug (p, DBG_THR, "Exiting", NULL, 0); | |
pthread_setspecific (panel_thread_id, NULL); | |
p->io_thread_running = 0; | |
pthread_mutex_unlock (&p->io_lock); | |
return NULL; | |
} | |
static void * | |
_panel_callback(void *arg) | |
{ | |
PANEL *p = (PANEL*)arg; | |
int sched_policy; | |
struct sched_param sched_priority; | |
char *buf = NULL; | |
size_t buf_data = 0; | |
unsigned int callback_count = 0; | |
int cmd_stat; | |
/* | |
Boost Priority for timer thread so it doesn't compete | |
with compute bound activities. | |
*/ | |
pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); | |
++sched_priority.sched_priority; | |
pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); | |
pthread_setspecific (panel_thread_id, "callback"); | |
_panel_debug (p, DBG_THR, "Starting", NULL, 0); | |
pthread_mutex_lock (&p->io_lock); | |
p->callback_thread_running = 1; | |
pthread_mutex_unlock (&p->io_lock); | |
pthread_cond_signal (&p->startup_done); /* Signal we're ready to go */ | |
msleep (100); | |
pthread_mutex_lock (&p->io_lock); | |
while ((p->sock != INVALID_SOCKET) && | |
(p->usecs_between_callbacks) && | |
(p->State != Error)) { | |
int interval = p->usecs_between_callbacks; | |
int new_register = p->new_register; | |
p->new_register = 0; | |
pthread_mutex_unlock (&p->io_lock); | |
if (new_register) /* need to get and send updated register info */ | |
_panel_register_query_string (p, &buf, &buf_data); | |
/* twice a second activities: */ | |
/* 1) update the query string if it has changed */ | |
/* (only really happens at startup) */ | |
/* 2) update register state by polling if the simulator is halted */ | |
msleep (500); | |
pthread_mutex_lock (&p->io_lock); | |
if (new_register) { | |
size_t repeat_data = strlen (register_repeat_prefix) + /* prefix */ | |
20 + /* max int width */ | |
strlen (register_repeat_units) + /* units and spacing */ | |
buf_data + /* command contents */ | |
1 + /* ; */ | |
strlen (register_repeat_start) + /* auto repeat begin */ | |
1 + /* ; */ | |
strlen (register_repeat_end) + /* auto repeat completion */ | |
1 + /* carriage return */ | |
1; /* NUL */ | |
char *repeat = (char *)malloc (repeat_data); | |
char *c; | |
c = strstr (buf, register_get_start); /* remove register_get_start string and anything before it */ | |
if (c) { /* always true */ | |
buf_data -= (c - buf) + strlen (register_get_start); | |
c += strlen (register_get_start); | |
} | |
sprintf (repeat, "%s%d%s%s%*.*s", register_repeat_prefix, | |
p->usecs_between_callbacks, | |
register_repeat_units, | |
register_repeat_start, | |
(int)buf_data, (int)buf_data, c); | |
pthread_mutex_unlock (&p->io_lock); | |
c = strstr (repeat, register_get_end); /* remove register_done_echo string and */ | |
if (c) /* always true */ | |
strcpy (c, register_repeat_end); /* replace it with the register_repeat_end string */ | |
if (_panel_sendf (p, &cmd_stat, NULL, "%s", repeat)) { | |
pthread_mutex_lock (&p->io_lock); | |
free (repeat); | |
break; | |
} | |
pthread_mutex_lock (&p->io_lock); | |
free (repeat); | |
} | |
/* when halted, we directly poll the halted system to get updated */ | |
/* register state which may have changed due to panel activities */ | |
if (p->State == Halt) { | |
pthread_mutex_unlock (&p->io_lock); | |
if (_panel_get_registers (p, 1, NULL)) { | |
pthread_mutex_lock (&p->io_lock); | |
break; | |
} | |
if (p->callback) | |
p->callback (p, p->simulation_time_base + p->simulation_time, p->callback_context); | |
pthread_mutex_lock (&p->io_lock); | |
} | |
} | |
pthread_mutex_unlock (&p->io_lock); | |
/* stop any established repeating activity in the simulator */ | |
if (p->parent == NULL) { /* Top level panel? */ | |
_panel_debug (p, DBG_THR, "Stopping All Repeats before exiting", NULL, 0); | |
_panel_sendf (p, &cmd_stat, NULL, "%s", register_repeat_stop_all); | |
} | |
else { | |
_panel_debug (p, DBG_THR, "Stopping Repeats before exiting", NULL, 0); | |
_panel_sendf (p, &cmd_stat, NULL, "%s", register_repeat_stop); | |
} | |
pthread_mutex_lock (&p->io_lock); | |
_panel_debug (p, DBG_THR, "Exiting", NULL, 0); | |
pthread_setspecific (panel_thread_id, NULL); | |
p->callback_thread_running = 0; | |
pthread_mutex_unlock (&p->io_lock); | |
free (buf); | |
return NULL; | |
} | |
const char *sim_panel_get_error (void) | |
{ | |
return (sim_panel_error_buf ? sim_panel_error_buf : ""); | |
} | |
void sim_panel_clear_error (void) | |
{ | |
if (sim_panel_error_bufsize) | |
free (sim_panel_error_buf); | |
sim_panel_error_buf = NULL; | |
sim_panel_error_bufsize = 0; | |
} | |
#if defined (_WIN32) | |
#define vsnprintf _vsnprintf | |
#endif | |
static int sim_panel_set_error (PANEL *p, const char *fmt, ...) | |
{ | |
va_list arglist; | |
int len; | |
if (p) { | |
pthread_mutex_lock (&p->io_lock); | |
p->State = Error; | |
pthread_mutex_unlock (&p->io_lock); | |
} | |
if (sim_panel_error_bufsize == 0) { | |
sim_panel_error_bufsize = 2048; | |
sim_panel_error_buf = (char *) malloc (sim_panel_error_bufsize); | |
if (sim_panel_error_buf == NULL) { | |
sim_panel_error_buf = (char *)"sim_panel_set_error(): Out of Memory\n"; | |
sim_panel_error_bufsize = 0; | |
return -1; | |
} | |
} | |
sim_panel_error_buf[sim_panel_error_bufsize-1] = '\0'; | |
while (1) { /* format passed string, args */ | |
va_start (arglist, fmt); | |
len = vsnprintf (sim_panel_error_buf, sim_panel_error_bufsize-1, fmt, arglist); | |
va_end (arglist); | |
if (len < 0) /* Format encoding error? */ | |
break; | |
/* If the formatted result didn't fit into the buffer, then grow the buffer and try again */ | |
if (len >= (int)(sim_panel_error_bufsize-1)) { | |
free (sim_panel_error_buf); | |
sim_panel_error_bufsize = sim_panel_error_bufsize * 2; | |
while ((int)sim_panel_error_bufsize < len + 2) | |
sim_panel_error_bufsize = sim_panel_error_bufsize * 2; | |
sim_panel_error_buf = (char *) malloc (sim_panel_error_bufsize); | |
if (sim_panel_error_buf == NULL) { | |
sim_panel_error_buf = (char *)"sim_panel_set_error(): Out of Memory\n"; | |
sim_panel_error_bufsize = 0; | |
return -1; | |
} | |
sim_panel_error_buf[sim_panel_error_bufsize-1] = '\0'; | |
continue; | |
} | |
break; | |
} | |
return -1; | |
} | |
static int | |
_panel_vsendf_completion (PANEL *p, int *completion_status, char **response, const char *completion_string, const char *fmt, va_list arglist) | |
{ | |
char stackbuf[1024]; | |
int bufsize = sizeof(stackbuf); | |
char *buf = stackbuf; | |
int len, status_echo_len = 0, sent_len; | |
int post_fix_len = completion_status ? 7 + sizeof (command_done_echo) + sizeof (command_status) : 1; | |
int ret; | |
if (completion_status && completion_string) /* one or the other, but */ | |
return -1; /* not both */ | |
while (1) { /* format passed string, args */ | |
len = vsnprintf (buf, bufsize-1, fmt, arglist); | |
if (len < 0) | |
return sim_panel_set_error (NULL, "Format encoding error while processing '%s'", fmt); | |
/* If the formatted result didn't fit into the buffer, then grow the buffer and try again */ | |
if ((len + post_fix_len) >= bufsize-1) { | |
if (buf != stackbuf) | |
free (buf); | |
bufsize = bufsize * 2; | |
if (bufsize < (len + post_fix_len + 2)) | |
bufsize = len + post_fix_len + 2; | |
buf = (char *) _panel_malloc (bufsize); | |
if (buf == NULL) | |
return -1; | |
buf[bufsize-1] = '\0'; | |
continue; | |
} | |
break; | |
} | |
if (len && (buf[len-1] != '\r')) { | |
strcpy (&buf[len], "\r"); /* Make sure command line is terminated */ | |
++len; | |
} | |
pthread_mutex_lock (&p->io_command_lock); | |
++p->command_count; | |
if (completion_status || completion_string) { | |
if (completion_status) { | |
sprintf (&buf[len], "%s\r%s\r", command_status, command_done_echo); | |
status_echo_len = strlen (&buf[len]); | |
} | |
pthread_mutex_lock (&p->io_lock); | |
p->completion_string = completion_string; | |
if (p->io_response_data) | |
_panel_debug (p, DBG_RCV, "Receive Data Discarded: ", p->io_response, p->io_response_data); | |
p->io_response_data = 0; | |
p->io_waiting = 1; | |
} | |
_panel_debug (p, DBG_REQ, "Command %d Request%s: %*.*s", NULL, 0, p->command_count, completion_status ? " (with response)" : "", len, len, buf); | |
ret = ((len + status_echo_len) == (sent_len = _panel_send (p, buf, len + status_echo_len))) ? 0 : -1; | |
if (completion_status || completion_string) { | |
if (ret) { /* Send failed? */ | |
p->completion_string = NULL; | |
p->io_waiting = 0; | |
} | |
else { /* Sent OK? */ | |
char *tresponse = NULL; | |
while (p->io_waiting) | |
pthread_cond_wait (&p->io_done, &p->io_lock); /* Wait for completion */ | |
tresponse = (char *)_panel_malloc (p->io_response_data + 1); | |
if (0 == memcmp (buf, p->io_response + strlen (sim_prompt), len)) { | |
char *eol, *status; | |
memcpy (tresponse, p->io_response + strlen (sim_prompt) + len + 1, p->io_response_data + 1 - (strlen (sim_prompt) + len + 1)); | |
if (completion_status) { | |
*completion_status = -1; | |
status = strstr (tresponse, command_status); | |
if (status) { | |
*(status - strlen (sim_prompt)) = '\0'; | |
status += strlen (command_status) + 2; | |
eol = strchr (status, '\r'); | |
if (eol) | |
*eol = '\0'; | |
sscanf (status, "Status:%08X-", completion_status); | |
} | |
} | |
} | |
else | |
memcpy (tresponse, p->io_response, p->io_response_data + 1); | |
if (response) { | |
*response = tresponse; | |
if (completion_status) | |
_panel_debug (p, DBG_RSP, "Command %d Response(Status=%d): '%s'", NULL, 0, p->command_count, *completion_status, *response); | |
else | |
_panel_debug (p, DBG_RSP, "Command %d Response - Match '%s': '%s'", NULL, 0, p->command_count, completion_string, *response); | |
} | |
else { | |
free (tresponse); | |
if (p->io_response_data) { | |
if (completion_status) | |
_panel_debug (p, DBG_RSP, "Discarded Unwanted Command %d Response Data(Status=%d):", p->io_response, p->io_response_data, p->command_count, *completion_status); | |
else | |
_panel_debug (p, DBG_RSP, "Discarded Unwanted Command %d Response Data - Match '%s':", p->io_response, p->io_response_data, p->command_count, completion_string); | |
} | |
} | |
} | |
p->completion_string = NULL; | |
p->io_response_data = 0; | |
p->io_response[0] = '\0'; | |
pthread_mutex_unlock (&p->io_lock); | |
} | |
else { | |
if (ret) | |
sim_panel_set_error (p, "Unexpected send length: %d, expected: %d", sent_len, len + status_echo_len); | |
} | |
pthread_mutex_unlock (&p->io_command_lock); | |
if (buf != stackbuf) | |
free (buf); | |
return ret; | |
} | |
static int | |
_panel_sendf (PANEL *p, int *completion_status, char **response, const char *fmt, ...) | |
{ | |
va_list arglist; | |
int status; | |
va_start (arglist, fmt); | |
status = _panel_vsendf_completion (p, completion_status, response, NULL, fmt, arglist); | |
va_end (arglist); | |
return status; | |
} | |
static int | |
_panel_sendf_completion (PANEL *p, char **response, const char *completion, const char *fmt, ...) | |
{ | |
va_list arglist; | |
int status; | |
va_start (arglist, fmt); | |
status = _panel_vsendf_completion (p, NULL, response, completion, fmt, arglist); | |
va_end (arglist); | |
return status; | |
} | |
#ifdef __cplusplus | |
} | |
#endif |