6 changed files with 300 additions and 18 deletions
@ -0,0 +1,211 @@ |
|||
#include <arpa/inet.h> |
|||
#include <errno.h> |
|||
#include <fcntl.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <unistd.h> |
|||
|
|||
#include <algorithm> |
|||
#include <cassert> |
|||
#include <cstdio> |
|||
|
|||
#include "remote_bitbang.h" |
|||
|
|||
#define DEBUG |
|||
#ifdef DEBUG |
|||
# define D(x) x |
|||
#else |
|||
# define D(x) |
|||
#endif // DEBUG
|
|||
|
|||
/////////// Circular buffer
|
|||
|
|||
template <typename T> |
|||
unsigned int circular_buffer_t<T>::size() const |
|||
{ |
|||
if (end >= start) |
|||
return end - start; |
|||
else |
|||
return end + capacity - start; |
|||
} |
|||
|
|||
template <typename T> |
|||
void circular_buffer_t<T>::consume(unsigned int bytes) |
|||
{ |
|||
start = (start + bytes) % capacity; |
|||
} |
|||
|
|||
template <typename T> |
|||
unsigned int circular_buffer_t<T>::contiguous_empty_size() const |
|||
{ |
|||
if (end >= start) |
|||
if (start == 0) |
|||
return capacity - end - 1; |
|||
else |
|||
return capacity - end; |
|||
else |
|||
return start - end - 1; |
|||
} |
|||
|
|||
template <typename T> |
|||
unsigned int circular_buffer_t<T>::contiguous_data_size() const |
|||
{ |
|||
if (end >= start) |
|||
return end - start; |
|||
else |
|||
return capacity - start; |
|||
} |
|||
|
|||
template <typename T> |
|||
void circular_buffer_t<T>::data_added(unsigned int bytes) |
|||
{ |
|||
end += bytes; |
|||
assert(end <= capacity); |
|||
if (end == capacity) |
|||
end = 0; |
|||
} |
|||
|
|||
template <typename T> |
|||
void circular_buffer_t<T>::reset() |
|||
{ |
|||
start = 0; |
|||
end = 0; |
|||
} |
|||
|
|||
template <typename T> |
|||
void circular_buffer_t<T>::append(const T *src, unsigned int count) |
|||
{ |
|||
unsigned int copy = std::min(count, contiguous_empty_size()); |
|||
memcpy(contiguous_empty(), src, copy * sizeof(T)); |
|||
data_added(copy); |
|||
count -= copy; |
|||
if (count > 0) { |
|||
assert(count < contiguous_empty_size()); |
|||
memcpy(contiguous_empty(), src+copy, count * sizeof(T)); |
|||
data_added(count); |
|||
} |
|||
} |
|||
|
|||
/////////// remote_bitbang_t
|
|||
|
|||
remote_bitbang_t::remote_bitbang_t(uint16_t port, sim_t *sim) : |
|||
socket_fd(0), |
|||
client_fd(0), |
|||
recv_buf(64 * 1024), |
|||
send_buf(64 * 1024) |
|||
{ |
|||
socket_fd = socket(AF_INET, SOCK_STREAM, 0); |
|||
if (socket_fd == -1) { |
|||
fprintf(stderr, "remote_bitbang failed to make socket: %s (%d)\n", |
|||
strerror(errno), errno); |
|||
abort(); |
|||
} |
|||
|
|||
fcntl(socket_fd, F_SETFL, O_NONBLOCK); |
|||
int reuseaddr = 1; |
|||
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, |
|||
sizeof(int)) == -1) { |
|||
fprintf(stderr, "remote_bitbang failed setsockopt: %s (%d)\n", |
|||
strerror(errno), errno); |
|||
abort(); |
|||
} |
|||
|
|||
struct sockaddr_in addr; |
|||
memset(&addr, 0, sizeof(addr)); |
|||
addr.sin_family = AF_INET; |
|||
addr.sin_addr.s_addr = INADDR_ANY; |
|||
addr.sin_port = htons(port); |
|||
|
|||
if (bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { |
|||
fprintf(stderr, "remote_bitbang failed to bind socket: %s (%d)\n", |
|||
strerror(errno), errno); |
|||
abort(); |
|||
} |
|||
|
|||
if (listen(socket_fd, 1) == -1) { |
|||
fprintf(stderr, "remote_bitbang failed to listen on socket: %s (%d)\n", |
|||
strerror(errno), errno); |
|||
abort(); |
|||
} |
|||
} |
|||
|
|||
void remote_bitbang_t::accept() |
|||
{ |
|||
client_fd = ::accept(socket_fd, NULL, NULL); |
|||
if (client_fd == -1) { |
|||
if (errno == EAGAIN) { |
|||
// No client waiting to connect right now.
|
|||
} else { |
|||
fprintf(stderr, "failed to accept on socket: %s (%d)\n", strerror(errno), |
|||
errno); |
|||
abort(); |
|||
} |
|||
} else { |
|||
fcntl(client_fd, F_SETFL, O_NONBLOCK); |
|||
} |
|||
} |
|||
|
|||
void remote_bitbang_t::read() |
|||
{ |
|||
// Reading from a non-blocking socket still blocks if there is no data
|
|||
// available.
|
|||
|
|||
size_t count = recv_buf.contiguous_empty_size(); |
|||
ssize_t bytes = ::read(client_fd, recv_buf.contiguous_empty(), count); |
|||
if (bytes == -1) { |
|||
if (errno == EAGAIN) { |
|||
// We'll try again the next call.
|
|||
} else { |
|||
fprintf(stderr, "failed to read on socket: %s (%d)\n", strerror(errno), errno); |
|||
abort(); |
|||
} |
|||
} else if (bytes == 0) { |
|||
// The remote disconnected.
|
|||
client_fd = 0; |
|||
recv_buf.reset(); |
|||
send_buf.reset(); |
|||
} else { |
|||
recv_buf.data_added(bytes); |
|||
D(fprintf(stderr, "receive buffer: ")); |
|||
for (unsigned i = 0; i < recv_buf.size(); i++) { |
|||
D(fprintf(stderr, "%c", recv_buf[i])); |
|||
} |
|||
D(fprintf(stderr, "\n")); |
|||
} |
|||
} |
|||
|
|||
void remote_bitbang_t::write() |
|||
{ |
|||
if (send_buf.empty()) |
|||
return; |
|||
|
|||
while (!send_buf.empty()) { |
|||
unsigned int count = send_buf.contiguous_data_size(); |
|||
assert(count > 0); |
|||
ssize_t bytes = ::write(client_fd, send_buf.contiguous_data(), count); |
|||
if (bytes == -1) { |
|||
fprintf(stderr, "failed to write to socket: %s (%d)\n", strerror(errno), errno); |
|||
abort(); |
|||
} else if (bytes == 0) { |
|||
// Client can't take any more data right now.
|
|||
break; |
|||
} else { |
|||
D(fprintf(stderr, "wrote %zd bytes: ", bytes)); |
|||
for (int i = 0; i < bytes; i++) { |
|||
D(fprintf(stderr, "%c", send_buf[i])); |
|||
} |
|||
D(fprintf(stderr, "\n")); |
|||
send_buf.consume(bytes); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void remote_bitbang_t::tick() |
|||
{ |
|||
if (client_fd > 0) { |
|||
this->read(); |
|||
this->write(); |
|||
} else { |
|||
this->accept(); |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
#ifndef REMOTE_BITBANG_H |
|||
#define REMOTE_BITBANG_H |
|||
|
|||
#include <stdint.h> |
|||
|
|||
class sim_t; |
|||
|
|||
template <typename T> |
|||
class circular_buffer_t |
|||
{ |
|||
public: |
|||
// The buffer can store capacity-1 data elements.
|
|||
circular_buffer_t(unsigned int capacity) : data(new T[capacity]), |
|||
start(0), end(0), capacity(capacity) {} |
|||
circular_buffer_t() : start(0), end(0), capacity(0) {} |
|||
~circular_buffer_t() { delete[] data; } |
|||
|
|||
T *data; |
|||
unsigned int start; // Data start, inclusive.
|
|||
unsigned int end; // Data end, exclusive.
|
|||
unsigned int capacity; // Size of the buffer.
|
|||
unsigned int size() const; |
|||
bool empty() const { return start == end; } |
|||
bool full() const { return ((end+1) % capacity) == start; } |
|||
T entry(unsigned index) { return data[(start + index) % capacity]; } |
|||
|
|||
// Return size and address of the block of RAM where more data can be copied
|
|||
// to be added to the buffer.
|
|||
unsigned int contiguous_empty_size() const; |
|||
T *contiguous_empty() { return data + end; } |
|||
void data_added(unsigned int bytes); |
|||
|
|||
unsigned int contiguous_data_size() const; |
|||
T *contiguous_data() { return data + start; } |
|||
// Tell the buffer that some bytes were consumed from the start of the
|
|||
// buffer.
|
|||
void consume(unsigned int bytes); |
|||
|
|||
void reset(); |
|||
|
|||
T operator[](unsigned int i) const { return data[(start + i) % capacity]; } |
|||
|
|||
void append(const T *src, unsigned int count); |
|||
}; |
|||
|
|||
class remote_bitbang_t |
|||
{ |
|||
public: |
|||
// Create a new server, listening for connections from localhost on the given
|
|||
// port.
|
|||
remote_bitbang_t(uint16_t port, sim_t *sim); |
|||
|
|||
// Do a bit of work.
|
|||
void tick(); |
|||
|
|||
private: |
|||
int socket_fd; |
|||
int client_fd; |
|||
circular_buffer_t<uint8_t> recv_buf; |
|||
circular_buffer_t<uint8_t> send_buf; |
|||
|
|||
// Check for a client connecting, and accept if there is one.
|
|||
void accept(); |
|||
// Read as much into recv_buf as possible.
|
|||
void read(); |
|||
// Write as much of send_buf as possible.
|
|||
void write(); |
|||
}; |
|||
|
|||
#endif |
|||
Loading…
Reference in new issue