-static void
-tcp_close(struct vconn *vconn)
-{
- struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
- poll_cancel(tcp->tx_waiter);
- close(tcp->fd);
- free(tcp);
-}
-
-static int
-tcp_connect(struct vconn *vconn)
-{
- struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
- return check_connection_completion(tcp->fd);
-}
-
-static int
-tcp_recv(struct vconn *vconn, struct buffer **bufferp)
-{
- struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
- struct buffer *rx;
- size_t want_bytes;
- ssize_t retval;
-
- if (tcp->rxbuf == NULL) {
- tcp->rxbuf = buffer_new(1564);
- }
- rx = tcp->rxbuf;
-
-again:
- if (sizeof(struct ofp_header) > rx->size) {
- want_bytes = sizeof(struct ofp_header) - rx->size;
- } else {
- struct ofp_header *oh = rx->data;
- size_t length = ntohs(oh->length);
- if (length < sizeof(struct ofp_header)) {
- VLOG_ERR("received too-short ofp_header (%zu bytes)", length);
- return EPROTO;
- }
- want_bytes = length - rx->size;
- if (!want_bytes) {
- *bufferp = rx;
- tcp->rxbuf = NULL;
- return 0;
- }
- }
- buffer_prealloc_tailroom(rx, want_bytes);
-
- retval = read(tcp->fd, buffer_tail(rx), want_bytes);
- if (retval > 0) {
- rx->size += retval;
- if (retval == want_bytes) {
- if (rx->size > sizeof(struct ofp_header)) {
- *bufferp = rx;
- tcp->rxbuf = NULL;
- return 0;
- } else {
- goto again;
- }
- }
- return EAGAIN;
- } else if (retval == 0) {
- if (rx->size) {
- VLOG_ERR("connection dropped mid-packet");
- return EPROTO;
- } else {
- return EOF;
- }
- } else {
- return retval ? errno : EAGAIN;
- }
-}
-
-static void
-tcp_clear_txbuf(struct tcp_vconn *tcp)
-{
- buffer_delete(tcp->txbuf);
- tcp->txbuf = NULL;
- tcp->tx_waiter = NULL;
-}
-
-static void
-tcp_do_tx(int fd UNUSED, short int revents UNUSED, void *vconn_)
-{
- struct vconn *vconn = vconn_;
- struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
- ssize_t n = write(tcp->fd, tcp->txbuf->data, tcp->txbuf->size);
- if (n < 0) {
- if (errno != EAGAIN) {
- VLOG_ERR("send: %s", strerror(errno));
- tcp_clear_txbuf(tcp);
- return;
- }
- } else if (n > 0) {
- buffer_pull(tcp->txbuf, n);
- if (!tcp->txbuf->size) {
- tcp_clear_txbuf(tcp);
- return;
- }
- }
- tcp->tx_waiter = poll_fd_callback(tcp->fd, POLLOUT, tcp_do_tx, vconn);
-}
-
-static int
-tcp_send(struct vconn *vconn, struct buffer *buffer)
-{
- struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
- ssize_t retval;
-
- if (tcp->txbuf) {
- return EAGAIN;
- }
-
- retval = write(tcp->fd, buffer->data, buffer->size);
- if (retval == buffer->size) {
- buffer_delete(buffer);
- return 0;
- } else if (retval >= 0 || errno == EAGAIN) {
- tcp->txbuf = buffer;
- if (retval > 0) {
- buffer_pull(buffer, retval);
- }
- tcp->tx_waiter = poll_fd_callback(tcp->fd, POLLOUT, tcp_do_tx, vconn);
- return 0;
- } else {
- return errno;
- }
-}
-
-static void
-tcp_wait(struct vconn *vconn, enum vconn_wait_type wait)
-{
- struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
- switch (wait) {
- case WAIT_CONNECT:
- poll_fd_wait(tcp->fd, POLLOUT);
- break;
-
- case WAIT_SEND:
- if (!tcp->txbuf) {
- poll_fd_wait(tcp->fd, POLLOUT);
- } else {
- /* Nothing to do: need to drain txbuf first. */
- }
- break;
-
- case WAIT_RECV:
- poll_fd_wait(tcp->fd, POLLIN);
- break;
-
- default:
- NOT_REACHED();
- }
-}
-