#include "defs.h"
#include "serial.h"
#include "ser-base.h"
#include "event-loop.h"
#include "gdb_string.h"
#include <sys/time.h>
#ifdef USE_WIN32API
#include <winsock2.h>
#endif
static timer_handler_func push_event;
static handler_func fd_event;
enum {
FD_SCHEDULED = -1,
NOTHING_SCHEDULED = -2
};
void
reschedule (struct serial *scb)
{
if (serial_is_async_p (scb))
{
int next_state;
switch (scb->async_state)
{
case FD_SCHEDULED:
if (scb->bufcnt == 0)
next_state = FD_SCHEDULED;
else
{
delete_file_handler (scb->fd);
next_state = create_timer (0, push_event, scb);
}
break;
case NOTHING_SCHEDULED:
if (scb->bufcnt == 0)
{
add_file_handler (scb->fd, fd_event, scb);
next_state = FD_SCHEDULED;
}
else
{
next_state = create_timer (0, push_event, scb);
}
break;
default:
if (scb->bufcnt == 0)
{
delete_timer (scb->async_state);
add_file_handler (scb->fd, fd_event, scb);
next_state = FD_SCHEDULED;
}
else
next_state = scb->async_state;
break;
}
if (serial_debug_p (scb))
{
switch (next_state)
{
case FD_SCHEDULED:
if (scb->async_state != FD_SCHEDULED)
fprintf_unfiltered (gdb_stdlog, "[fd%d->fd-scheduled]\n",
scb->fd);
break;
default:
if (scb->async_state == FD_SCHEDULED)
fprintf_unfiltered (gdb_stdlog, "[fd%d->timer-scheduled]\n",
scb->fd);
break;
}
}
scb->async_state = next_state;
}
}
static void
fd_event (int error, void *context)
{
struct serial *scb = context;
if (error != 0)
{
scb->bufcnt = SERIAL_ERROR;
}
else if (scb->bufcnt == 0)
{
int nr;
nr = scb->ops->read_prim (scb, BUFSIZ);
if (nr == 0)
{
scb->bufcnt = SERIAL_EOF;
}
else if (nr > 0)
{
scb->bufcnt = nr;
scb->bufp = scb->buf;
}
else
{
scb->bufcnt = SERIAL_ERROR;
}
}
scb->async_handler (scb, scb->async_context);
reschedule (scb);
}
static void
push_event (void *context)
{
struct serial *scb = context;
scb->async_state = NOTHING_SCHEDULED;
scb->async_handler (scb, scb->async_context);
reschedule (scb);
}
static int
ser_base_wait_for (struct serial *scb, int timeout)
{
while (1)
{
int numfds;
struct timeval tv;
fd_set readfds, exceptfds;
tv.tv_sec = timeout;
tv.tv_usec = 0;
FD_ZERO (&readfds);
FD_ZERO (&exceptfds);
FD_SET (scb->fd, &readfds);
FD_SET (scb->fd, &exceptfds);
if (timeout >= 0)
numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, &tv);
else
numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, 0);
if (numfds <= 0)
{
if (numfds == 0)
return SERIAL_TIMEOUT;
else if (errno == EINTR)
continue;
else
return SERIAL_ERROR;
}
return 0;
}
}
static int
do_ser_base_readchar (struct serial *scb, int timeout)
{
int status;
int delta;
delta = (timeout == 0 ? 0 : 1);
while (1)
{
if (deprecated_ui_loop_hook)
{
if (deprecated_ui_loop_hook (0))
return SERIAL_TIMEOUT;
}
status = ser_base_wait_for (scb, delta);
if (timeout > 0)
timeout -= delta;
if (status != SERIAL_TIMEOUT)
break;
else if (timeout == 0)
{
status = SERIAL_TIMEOUT;
break;
}
}
if (status < 0)
return status;
status = scb->ops->read_prim (scb, BUFSIZ);
if (status <= 0)
{
if (status == 0)
return SERIAL_TIMEOUT;
else
return SERIAL_ERROR;
}
scb->bufcnt = status;
scb->bufcnt--;
scb->bufp = scb->buf;
return *scb->bufp++;
}
int
generic_readchar (struct serial *scb, int timeout,
int (do_readchar) (struct serial *scb, int timeout))
{
int ch;
if (scb->bufcnt > 0)
{
ch = *scb->bufp;
scb->bufcnt--;
scb->bufp++;
}
else if (scb->bufcnt < 0)
{
ch = scb->bufcnt;
}
else
{
ch = do_readchar (scb, timeout);
if (ch < 0)
{
switch ((enum serial_rc) ch)
{
case SERIAL_EOF:
case SERIAL_ERROR:
scb->bufcnt = ch;
break;
case SERIAL_TIMEOUT:
scb->bufcnt = 0;
break;
}
}
}
reschedule (scb);
return ch;
}
int
ser_base_readchar (struct serial *scb, int timeout)
{
return generic_readchar (scb, timeout, do_ser_base_readchar);
}
int
ser_base_write (struct serial *scb, const char *str, int len)
{
int cc;
while (len > 0)
{
cc = scb->ops->write_prim (scb, str, len);
if (cc < 0)
return 1;
len -= cc;
str += cc;
}
return 0;
}
int
ser_base_flush_output (struct serial *scb)
{
return 0;
}
int
ser_base_flush_input (struct serial *scb)
{
if (scb->bufcnt >= 0)
{
scb->bufcnt = 0;
scb->bufp = scb->buf;
return 0;
}
else
return SERIAL_ERROR;
}
int
ser_base_send_break (struct serial *scb)
{
return 0;
}
int
ser_base_drain_output (struct serial *scb)
{
return 0;
}
void
ser_base_raw (struct serial *scb)
{
return;
}
serial_ttystate
ser_base_get_tty_state (struct serial *scb)
{
return (serial_ttystate) XMALLOC (int);
}
int
ser_base_set_tty_state (struct serial *scb, serial_ttystate ttystate)
{
return 0;
}
int
ser_base_noflush_set_tty_state (struct serial *scb,
serial_ttystate new_ttystate,
serial_ttystate old_ttystate)
{
return 0;
}
void
ser_base_print_tty_state (struct serial *scb,
serial_ttystate ttystate,
struct ui_file *stream)
{
return;
}
int
ser_base_setbaudrate (struct serial *scb, int rate)
{
return 0;
}
int
ser_base_setstopbits (struct serial *scb, int num)
{
return 0;
}
void
ser_base_async (struct serial *scb,
int async_p)
{
if (async_p)
{
scb->async_state = NOTHING_SCHEDULED;
if (serial_debug_p (scb))
fprintf_unfiltered (gdb_stdlog, "[fd%d->asynchronous]\n",
scb->fd);
reschedule (scb);
}
else
{
if (serial_debug_p (scb))
fprintf_unfiltered (gdb_stdlog, "[fd%d->synchronous]\n",
scb->fd);
switch (scb->async_state)
{
case FD_SCHEDULED:
delete_file_handler (scb->fd);
break;
case NOTHING_SCHEDULED:
break;
default:
delete_timer (scb->async_state);
break;
}
}
}