+
+/* add/remove log callback handler */
+int log_add_callback(log_callback_fn fn, void *priv)
+{
+ log_callback_t *cb;
+
+ /* prevent the same callback to be registered more than once, just for sure */
+ for (cb = log_callbacks; cb; cb = cb->next)
+ {
+ if (cb->fn == fn && cb->priv == priv)
+ return ERROR_INVALID_ARGUMENTS;
+ }
+
+ /* alloc memory, it is safe just to return in case of an error, no need for the caller to check this */
+ if ((cb = malloc(sizeof(log_callback_t))) == NULL)
+ return ERROR_BUF_TOO_SMALL;
+
+ /* add item to the beginning of the linked list */
+ cb->fn = fn;
+ cb->priv = priv;
+ cb->next = log_callbacks;
+ log_callbacks = cb;
+
+ return ERROR_OK;
+}
+
+int log_remove_callback(log_callback_fn fn, void *priv)
+{
+ log_callback_t *cb, **p;
+
+ for (p = &log_callbacks; (cb = *p); p = &(*p)->next)
+ {
+ if (cb->fn == fn && cb->priv == priv)
+ {
+ *p = cb->next;
+ free(cb);
+ return ERROR_OK;
+ }
+ }
+
+ /* no such item */
+ return ERROR_INVALID_ARGUMENTS;
+}
+
+/* return allocated string w/printf() result */
+char *alloc_vprintf(const char *fmt, va_list ap)
+{
+ /* no buffer at the beginning, force realloc to do the job */
+ char *string = NULL;
+
+ /* start with buffer size suitable for typical messages */
+ int size = 128;
+
+ for (;;)
+ {
+ char *t = string;
+ va_list ap_copy;
+ int ret;
+ string = realloc(string, size);
+ if (string == NULL)
+ {
+ if (t != NULL)
+ free(t);
+ return NULL;
+ }
+
+ va_copy(ap_copy, ap);
+
+ ret = vsnprintf(string, size, fmt, ap_copy);
+ /* NB! The result of the vsnprintf() might be an *EMPTY* string! */
+ if ((ret >= 0) && ((ret + 1) < size))
+ break;
+
+ /* there was just enough or not enough space, allocate more in the next round */
+ size *= 2; /* double the buffer size */
+ }
+
+ /* the returned buffer is by principle guaranteed to be at least one character longer */
+ return string;
+}
+
+char *alloc_printf(const char *format, ...)
+{
+ char *string;
+ va_list ap;
+ va_start(ap, format);
+ string = alloc_vprintf(format, ap);
+ va_end(ap);
+ return string;
+}
+
+/* Code must return to the server loop before 1000ms has returned or invoke
+ * this function.
+ *
+ * The GDB connection will time out if it spends >2000ms and you'll get nasty
+ * error messages from GDB:
+ *
+ * Ignoring packet error, continuing...
+ * Reply contains invalid hex digit 116
+ *
+ * While it is possible use "set remotetimeout" to more than the default 2000ms
+ * in GDB, OpenOCD guarantees that it sends keep-alive packages on the
+ * GDB protocol and it is a bug in OpenOCD not to either return to the server
+ * loop or invoke keep_alive() every 1000ms.
+ *
+ * This function will send a keep alive packet if >500ms has passed since last time
+ * it was invoked.
+ *
+ */
+void keep_alive()
+{
+ current_time=timeval_ms();
+ if (current_time-last_time>1000)
+ {
+ LOG_WARNING("BUG: keep_alive() was not invoked in the 1000ms timelimit. GDB alive packet not sent! (%lld)", current_time-last_time);
+ last_time=current_time;
+ } else if (current_time-last_time>500)
+ {
+ /* this will keep the GDB connection alive */
+ LOG_USER_N("%s", "");
+ last_time=current_time;
+ }
+
+ /* also process TCL events (we have to do this from 'log.c' since its
+ * keep_alive() is the only routine guaranteed to be called at least
+ * once per second :( */
+ process_jim_events ();
+}
+
+/* reset keep alive timer without sending message */
+void kept_alive()
+{
+ current_time=timeval_ms();
+ last_time=current_time;
+}
+
+/* if we sleep for extended periods of time, we must invoke keep_alive() intermittantly */
+void alive_sleep(int ms)
+{
+ int i;
+ for (i=0; i<ms; i+=500)
+ {
+ int sleep_a_bit=ms-i;
+ if (sleep_a_bit>500)
+ {
+ sleep_a_bit=500;
+ }
+ keep_alive();
+ usleep(sleep_a_bit*1000);
+ keep_alive();
+ }
+}