2005-04-06 Mark Mitchell * configure.ac: On MinGW, define USE_WIN32API and link with -lws2_32. * ser-tcp.c (): Include, for Windows. (ETIMEDOUT): Define, for Windows. (ioctl): Likewise. (closesocket): Define, for POSIX. (net_open): Adjust for differences in socket functions between Windows and UNIX. (net_close): Likweise. (net_read_prim): New function. (net_write_prim): Likewise. (_initialize_ser_tcp): Initialize winsock. Fill in read_prim and write_prim. * ser-unix.h (ser_unix_readcchar): Remove. (ser_unix_read_prim): Declare. (ser_unix_write_prim): Likewise. * ser-unix.c (generic_readchar): Move to ser-base.c. (ser_unix_wait_for): Likewise. (do_unix_readchar): Likewise. (ser_unix_readchar): Likewise. (_initialize_ser_hardwire): Initialize read_prim and write_prim. (ser_unix_read_prim): New function. (ser_unix_write_prim): Likewise. * ser-base.h (generic_readchar): Declare. (ser_base_readchar): Likewise. * ser-base.c (): Include, for windows. (fd_event): Use the read primitive specified by the serial interface. (ser_base_wait_for): Moved from ser-unix.c (do_ser_base_read_char): Likewise. (generic_readchar): Likewise. (ser_base_readchar): Likewise. (ser_base_write): Use the write primitive specified by the serial interface. * ser-pipe.c (_initialize_ser_pipe): Use ser_base_readchar, not ser_unix_readchar. Initialize read_prim and write_prim. * serial.c (struct serial_ops): Add read_prim and write_prim. *** /net/sethra/scratch/mitchell/src/gdb/gdb/configure.ac Wed Apr 6 09:56:04 2005 --- configure.ac Wed Apr 6 09:08:52 2005 *************** if test x$gdb_cv_os_cygwin = xyes; then *** 1187,1192 **** --- 1187,1204 ---- ;; esac fi + + # The ser-tcp.c module requires sockets. + case ${host} in + *mingw32*) + AC_DEFINE(USE_WIN32API, 1, + [Define if we should use the Windows API, instead of the + POSIX API. On Windows, we use the Windows API when + building for MinGW, but the POSIX API when building + for Cygwin.]) + WIN32LIBS="$WIN32LIBS -lws2_32" + ;; + esac AC_SUBST(WIN32LIBS) LIBGUI="../libgui/src/libgui.a" *** /net/sethra/scratch/mitchell/src/gdb/gdb/ser-base.c Wed Apr 6 09:56:04 2005 --- ser-base.c Wed Apr 6 09:59:39 2005 *************** *** 24,29 **** --- 24,32 ---- #include "serial.h" #include "ser-unix.h" #include "event-loop.h" + #ifdef USE_WIN32API + #include + #endif static timer_handler_func push_event; static handler_func fd_event; *************** fd_event (int error, void *context) *** 136,146 **** pull characters out of the buffer. See also generic_readchar(). */ int nr; ! do ! { ! nr = read (scb->fd, scb->buf, BUFSIZ); ! } ! while (nr == -1 && errno == EINTR); if (nr == 0) { scb->bufcnt = SERIAL_EOF; --- 139,145 ---- pull characters out of the buffer. See also generic_readchar(). */ int nr; ! nr = scb->ops->read_prim (scb, BUFSIZ); if (nr == 0) { scb->bufcnt = SERIAL_EOF; *************** push_event (void *context) *** 174,179 **** --- 173,357 ---- reschedule (scb); } + /* Wait for input on scb, with timeout seconds. Returns 0 on success, + otherwise SERIAL_TIMEOUT or SERIAL_ERROR. */ + + static int + ser_base_wait_for (struct serial *scb, int timeout) + { + while (1) + { + int numfds; + struct timeval tv; + fd_set readfds, exceptfds; + + /* NOTE: Some OS's can scramble the READFDS when the select() + call fails (ex the kernel with Red Hat 5.2). Initialize all + arguments before each call. */ + + 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; /* Got an error from select or poll */ + } + + return 0; + } + } + + /* Read a character with user-specified timeout. TIMEOUT is number of seconds + to wait, or -1 to wait forever. Use timeout of 0 to effect a poll. Returns + char if successful. Returns -2 if timeout expired, EOF if line dropped + dead, or -3 for any other error (see errno in that case). */ + + static int + do_ser_base_readchar (struct serial *scb, int timeout) + { + int status; + int delta; + + /* We have to be able to keep the GUI alive here, so we break the + original timeout into steps of 1 second, running the "keep the + GUI alive" hook each time through the loop. + + Also, timeout = 0 means to poll, so we just set the delta to 0, + so we will only go through the loop once. */ + + delta = (timeout == 0 ? 0 : 1); + while (1) + { + + /* N.B. The UI may destroy our world (for instance by calling + remote_stop,) in which case we want to get out of here as + quickly as possible. It is not safe to touch scb, since + someone else might have freed it. The + deprecated_ui_loop_hook signals that we should exit by + returning 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 we got a character or an error back from wait_for, then we can + break from the loop before the timeout is completed. */ + + if (status != SERIAL_TIMEOUT) + { + break; + } + + /* If we have exhausted the original timeout, then generate + a SERIAL_TIMEOUT, and pass it out of the loop. */ + + 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; /* 0 chars means timeout [may need to + distinguish between EOF & timeouts + someday] */ + else + return SERIAL_ERROR; /* Got an error from read */ + } + + scb->bufcnt = status; + scb->bufcnt--; + scb->bufp = scb->buf; + return *scb->bufp++; + } + + /* Perform operations common to both old and new readchar. */ + + /* Return the next character from the input FIFO. If the FIFO is + empty, call the SERIAL specific routine to try and read in more + characters. + + Initially data from the input FIFO is returned (fd_event() + pre-reads the input into that FIFO. Once that has been emptied, + further data is obtained by polling the input FD using the device + specific readchar() function. Note: reschedule() is called after + every read. This is because there is no guarentee that the lower + level fd_event() poll_event() code (which also calls reschedule()) + will be called. */ + + 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) + { + /* Some errors/eof are are sticky. */ + ch = scb->bufcnt; + } + else + { + ch = do_readchar (scb, timeout); + if (ch < 0) + { + switch ((enum serial_rc) ch) + { + case SERIAL_EOF: + case SERIAL_ERROR: + /* Make the error/eof stick. */ + 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) { *************** ser_base_write (struct serial *scb, cons *** 181,187 **** while (len > 0) { ! cc = write (scb->fd, str, len); if (cc < 0) return 1; --- 359,365 ---- while (len > 0) { ! cc = scb->ops->write_prim (scb, str, len); if (cc < 0) return 1; *** /net/sethra/scratch/mitchell/src/gdb/gdb/ser-pipe.c Wed Apr 6 09:56:04 2005 --- ser-pipe.c Mon Mar 28 10:41:42 2005 *************** _initialize_ser_pipe (void) *** 144,150 **** ops->next = 0; ops->open = pipe_open; ops->close = pipe_close; ! ops->readchar = ser_unix_readchar; ops->write = ser_base_write; ops->flush_output = ser_base_flush_output; ops->flush_input = ser_base_flush_input; --- 144,150 ---- ops->next = 0; ops->open = pipe_open; ops->close = pipe_close; ! ops->readchar = ser_base_readchar; ops->write = ser_base_write; ops->flush_output = ser_base_flush_output; ops->flush_input = ser_base_flush_input; *************** _initialize_ser_pipe (void) *** 158,162 **** --- 158,164 ---- ops->setstopbits = ser_base_setstopbits; ops->drain_output = ser_base_drain_output; ops->async = ser_base_async; + ops->read_prim = ser_unix_read_prim; + ops->write_prim = ser_unix_write_prim; serial_add_interface (ops); } *** /net/sethra/scratch/mitchell/src/gdb/gdb/ser-tcp.c Wed Apr 6 09:55:25 2005 --- ser-tcp.c Wed Apr 6 09:16:51 2005 *************** *** 34,44 **** --- 34,54 ---- #endif #include + + #ifdef USE_WIN32API + #include + #define ETIMEDOUT WSAETIMEDOUT + #define ioctl ioctlsocket + #else #include #include #include #include #include + /* We cannot define "close" to "closesocket" above because the + Windows API has "close", but it does not work on sockets. */ + #define closesocket close + #endif #include #include "gdb_string.h" *************** net_open (struct serial *scb, const char *** 62,67 **** --- 72,82 ---- int use_udp; struct hostent *hostent; struct sockaddr_in sockaddr; + #ifdef USE_WIN32API + u_long ioarg; + #else + int ioarg; + #endif use_udp = 0; if (strncmp (name, "udp:", 4) == 0) *************** net_open (struct serial *scb, const char *** 108,121 **** sizeof (struct in_addr)); /* set socket nonblocking */ ! tmp = 1; ! ioctl (scb->fd, FIONBIO, &tmp); /* Use Non-blocking connect. connect() will return 0 if connected already. */ n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)); ! if (n < 0 && errno != EINPROGRESS) { net_close (scb); return -1; } --- 123,147 ---- sizeof (struct in_addr)); /* set socket nonblocking */ ! ioarg = 1; ! ioctl (scb->fd, FIONBIO, &ioarg); /* Use Non-blocking connect. connect() will return 0 if connected already. */ n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)); ! if (n < 0 ! #ifdef USE_WIN32API ! /* Under Windows, calling "connect" with a non-blocking socket ! results in WSAEWOULDBLOCK, not WSAEINPROGRESS. */ ! && WSAGetLastError() != WSAEWOULDBLOCK ! #else ! && errno != EINPROGRESS ! #endif ! ) { + #ifdef USE_WIN32API + errno = WSAGetLastError(); + #endif net_close (scb); return -1; } *************** net_open (struct serial *scb, const char *** 165,171 **** { int res, err, len; len = sizeof(err); ! res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, &err, &len); if (res < 0 || err) { if (err) --- 191,201 ---- { int res, err, len; len = sizeof(err); ! /* On Windows, the fourth parameter to getsockopt is a "char *"; ! on UNIX systems it is generally "void *". The cast to "void *" ! is OK everywhere, since in C "void *" can be implicitly ! converted to any pointer type. */ ! res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len); if (res < 0 || err) { if (err) *************** net_open (struct serial *scb, const char *** 176,183 **** } /* turn off nonblocking */ ! tmp = 0; ! ioctl (scb->fd, FIONBIO, &tmp); if (use_udp == 0) { --- 206,213 ---- } /* turn off nonblocking */ ! ioarg = 0; ! ioctl (scb->fd, FIONBIO, &ioarg); if (use_udp == 0) { *************** net_close (struct serial *scb) *** 202,221 **** if (scb->fd < 0) return; ! close (scb->fd); scb->fd = -1; } void _initialize_ser_tcp (void) { ! struct serial_ops *ops = XMALLOC (struct serial_ops); memset (ops, 0, sizeof (struct serial_ops)); ops->name = "tcp"; ops->next = 0; ops->open = net_open; ops->close = net_close; ! ops->readchar = ser_unix_readchar; ops->write = ser_base_write; ops->flush_output = ser_base_flush_output; ops->flush_input = ser_base_flush_input; --- 232,270 ---- if (scb->fd < 0) return; ! closesocket (scb->fd); scb->fd = -1; } + static int + net_read_prim (struct serial *scb, size_t count) + { + return recv (scb->fd, scb->buf, count, 0); + } + + static int + net_write_prim (struct serial *scb, const void *buf, size_t count) + { + return send (scb->fd, buf, count, 0); + } + void _initialize_ser_tcp (void) { ! struct serial_ops *ops; ! #ifdef USE_WIN32API ! WSADATA wsa_data; ! if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0) ! /* WinSock is unavailable. */ ! return; ! #endif ! ops = XMALLOC (struct serial_ops); memset (ops, 0, sizeof (struct serial_ops)); ops->name = "tcp"; ops->next = 0; ops->open = net_open; ops->close = net_close; ! ops->readchar = ser_base_readchar; ops->write = ser_base_write; ops->flush_output = ser_base_flush_output; ops->flush_input = ser_base_flush_input; *************** _initialize_ser_tcp (void) *** 229,233 **** --- 278,284 ---- ops->setstopbits = ser_base_setstopbits; ops->drain_output = ser_base_drain_output; ops->async = ser_base_async; + ops->read_prim = net_read_prim; + ops->write_prim = net_write_prim; serial_add_interface (ops); } *** /net/sethra/scratch/mitchell/src/gdb/gdb/ser-unix.c Wed Apr 6 09:56:04 2005 --- ser-unix.c Mon Mar 28 10:41:42 2005 *************** static void hardwire_raw (struct serial *** 70,78 **** static int wait_for (struct serial *scb, int timeout); static int hardwire_readchar (struct serial *scb, int timeout); static int do_hardwire_readchar (struct serial *scb, int timeout); - static int generic_readchar (struct serial *scb, int timeout, - int (*do_readchar) (struct serial *scb, - int timeout)); static int rate_to_code (int rate); static int hardwire_setbaudrate (struct serial *scb, int rate); static void hardwire_close (struct serial *scb); --- 70,75 ---- *************** hardwire_raw (struct serial *scb) *** 422,428 **** */ /* FIXME: cagney/1999-09-16: Don't replace this with the equivalent ! ser_unix*() until the old TERMIOS/SGTTY/... timer code has been flushed. . */ /* NOTE: cagney/1999-09-30: Much of the code below is dead. The only --- 419,425 ---- */ /* FIXME: cagney/1999-09-16: Don't replace this with the equivalent ! ser_base*() until the old TERMIOS/SGTTY/... timer code has been flushed. . */ /* NOTE: cagney/1999-09-30: Much of the code below is dead. The only *************** wait_for (struct serial *scb, int timeou *** 542,554 **** dropped dead, or SERIAL_ERROR for any other error (see errno in that case). */ /* FIXME: cagney/1999-09-16: Don't replace this with the equivalent ! ser_unix*() until the old TERMIOS/SGTTY/... timer code has been flushed. */ /* NOTE: cagney/1999-09-16: This function is not identical to ! ser_unix_readchar() as part of replacing it with ser_unix*() merging will be required - this code handles the case where read() ! times out due to no data while ser_unix_readchar() doesn't expect that. */ static int --- 539,551 ---- dropped dead, or SERIAL_ERROR for any other error (see errno in that case). */ /* FIXME: cagney/1999-09-16: Don't replace this with the equivalent ! ser_base*() until the old TERMIOS/SGTTY/... timer code has been flushed. */ /* NOTE: cagney/1999-09-16: This function is not identical to ! ser_base_readchar() as part of replacing it with ser_base*() merging will be required - this code handles the case where read() ! times out due to no data while ser_base_readchar() doesn't expect that. */ static int *************** hardwire_close (struct serial *scb) *** 863,1053 **** close (scb->fd); scb->fd = -1; } - - /* Wait for input on scb, with timeout seconds. Returns 0 on success, - otherwise SERIAL_TIMEOUT or SERIAL_ERROR. */ - - static int - ser_unix_wait_for (struct serial *scb, int timeout) - { - while (1) - { - int numfds; - struct timeval tv; - fd_set readfds, exceptfds; - - /* NOTE: Some OS's can scramble the READFDS when the select() - call fails (ex the kernel with Red Hat 5.2). Initialize all - arguments before each call. */ - - 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; /* Got an error from select or poll */ - } - - return 0; - } - } - - /* Read a character with user-specified timeout. TIMEOUT is number of seconds - to wait, or -1 to wait forever. Use timeout of 0 to effect a poll. Returns - char if successful. Returns -2 if timeout expired, EOF if line dropped - dead, or -3 for any other error (see errno in that case). */ - - static int - do_unix_readchar (struct serial *scb, int timeout) - { - int status; - int delta; - - /* We have to be able to keep the GUI alive here, so we break the - original timeout into steps of 1 second, running the "keep the - GUI alive" hook each time through the loop. - - Also, timeout = 0 means to poll, so we just set the delta to 0, - so we will only go through the loop once. */ - - delta = (timeout == 0 ? 0 : 1); - while (1) - { - - /* N.B. The UI may destroy our world (for instance by calling - remote_stop,) in which case we want to get out of here as - quickly as possible. It is not safe to touch scb, since - someone else might have freed it. The - deprecated_ui_loop_hook signals that we should exit by - returning 1. */ - - if (deprecated_ui_loop_hook) - { - if (deprecated_ui_loop_hook (0)) - return SERIAL_TIMEOUT; - } - - status = ser_unix_wait_for (scb, delta); - if (timeout > 0) - timeout -= delta; - - /* If we got a character or an error back from wait_for, then we can - break from the loop before the timeout is completed. */ - - if (status != SERIAL_TIMEOUT) - { - break; - } - - /* If we have exhausted the original timeout, then generate - a SERIAL_TIMEOUT, and pass it out of the loop. */ - - else if (timeout == 0) - { - status = SERIAL_TIMEOUT; - break; - } - } - - if (status < 0) - return status; - - while (1) - { - status = read (scb->fd, scb->buf, BUFSIZ); - if (status != -1 || errno != EINTR) - break; - } - - if (status <= 0) - { - if (status == 0) - return SERIAL_TIMEOUT; /* 0 chars means timeout [may need to - distinguish between EOF & timeouts - someday] */ - else - return SERIAL_ERROR; /* Got an error from read */ - } - - scb->bufcnt = status; - scb->bufcnt--; - scb->bufp = scb->buf; - return *scb->bufp++; - } - - /* Perform operations common to both old and new readchar. */ - - /* Return the next character from the input FIFO. If the FIFO is - empty, call the SERIAL specific routine to try and read in more - characters. - - Initially data from the input FIFO is returned (fd_event() - pre-reads the input into that FIFO. Once that has been emptied, - further data is obtained by polling the input FD using the device - specific readchar() function. Note: reschedule() is called after - every read. This is because there is no guarentee that the lower - level fd_event() poll_event() code (which also calls reschedule()) - will be called. */ - - static 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) - { - /* Some errors/eof are are sticky. */ - ch = scb->bufcnt; - } - else - { - ch = do_readchar (scb, timeout); - if (ch < 0) - { - switch ((enum serial_rc) ch) - { - case SERIAL_EOF: - case SERIAL_ERROR: - /* Make the error/eof stick. */ - scb->bufcnt = ch; - break; - case SERIAL_TIMEOUT: - scb->bufcnt = 0; - break; - } - } - } - reschedule (scb); - return ch; - } - - int - ser_unix_readchar (struct serial *scb, int timeout) - { - return generic_readchar (scb, timeout, do_unix_readchar); - } void _initialize_ser_hardwire (void) --- 860,866 ---- *************** _initialize_ser_hardwire (void) *** 1058,1064 **** ops->next = 0; ops->open = hardwire_open; ops->close = hardwire_close; ! /* FIXME: Don't replace this with the equivalent ser_unix*() until the old TERMIOS/SGTTY/... timer code has been flushed. cagney 1999-09-16. */ ops->readchar = hardwire_readchar; --- 871,877 ---- ops->next = 0; ops->open = hardwire_open; ops->close = hardwire_close; ! /* FIXME: Don't replace this with the equivalent ser_base*() until the old TERMIOS/SGTTY/... timer code has been flushed. cagney 1999-09-16. */ ops->readchar = hardwire_readchar; *************** _initialize_ser_hardwire (void) *** 1075,1079 **** --- 888,916 ---- ops->setstopbits = hardwire_setstopbits; ops->drain_output = hardwire_drain_output; ops->async = ser_base_async; + ops->read_prim = ser_unix_read_prim; + ops->write_prim = ser_unix_write_prim; serial_add_interface (ops); } + + int + ser_unix_read_prim (struct serial *scb, size_t count) + { + int status; + + while (1) + { + status = read (scb->fd, scb->buf, count); + if (status != -1 || errno != EINTR) + break; + } + return status; + } + + int + ser_unix_write_prim (struct serial *scb, const void *buf, size_t len) + { + /* ??? Historically, GDB has not retried calls to "write" that + result in EINTR. */ + return write (scb->fd, buf, len); + } *** /net/sethra/scratch/mitchell/src/gdb/gdb/ser-base.h Wed Apr 6 09:56:04 2005 --- ser-base.h Mon Mar 28 10:41:42 2005 *************** *** 25,30 **** --- 25,33 ---- struct serial; struct ui_file; + extern int generic_readchar (struct serial *scb, int timeout, + int (*do_readchar) (struct serial *scb, + int timeout)); extern void reschedule (struct serial *scb); extern int ser_base_flush_output (struct serial *scb); extern int ser_base_flush_input (struct serial *scb); *************** extern int ser_base_drain_output (struct *** 46,50 **** --- 49,54 ---- extern int ser_base_write (struct serial *scb, const char *str, int len); extern void ser_base_async (struct serial *scb, int async_p); + extern int ser_base_readchar (struct serial *scb, int timeout); #endif *** /net/sethra/scratch/mitchell/src/gdb/gdb/ser-unix.h Wed Apr 6 09:56:04 2005 --- ser-unix.h Mon Mar 28 10:41:42 2005 *************** *** 22,27 **** #ifndef SER_UNIX_H #define SER_UNIX_H ! extern int ser_unix_readchar (struct serial *scb, int timeout); #endif --- 22,29 ---- #ifndef SER_UNIX_H #define SER_UNIX_H ! extern int ser_unix_read_prim (struct serial *scb, size_t count); ! extern int ser_unix_write_prim (struct serial *scb, const void *buf, ! size_t count); #endif *** /net/sethra/scratch/mitchell/src/gdb/gdb/serial.h Wed Apr 6 09:56:04 2005 --- serial.h Mon Mar 28 10:41:42 2005 *************** struct serial_ops *** 232,237 **** --- 232,243 ---- the specified function when ever there is something interesting. */ void (*async) (struct serial *scb, int async_p); + /* Perform a low-level read operation, reading (at most) COUNT + bytes into SCB->BUF. */ + int (*read_prim)(struct serial *scb, size_t count); + /* Perform a low-level write operation, writing (at most) COUNT + bytes from BUF. */ + int (*write_prim)(struct serial *scb, const void *buf, size_t count); }; /* Add a new serial interface to the interface list */