Refactor remote bitbang code.
[riscv-isa-sim.git] / riscv / remote_bitbang.cc
1 #include <arpa/inet.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7
8 #include <algorithm>
9 #include <cassert>
10 #include <cstdio>
11
12 #include "remote_bitbang.h"
13
14 #if 1
15 # define D(x) x
16 #else
17 # define D(x)
18 #endif
19
20 /////////// Circular buffer
21
22 template <typename T>
23 unsigned int circular_buffer_t<T>::size() const
24 {
25 if (end >= start)
26 return end - start;
27 else
28 return end + capacity - start;
29 }
30
31 template <typename T>
32 void circular_buffer_t<T>::consume(unsigned int bytes)
33 {
34 start = (start + bytes) % capacity;
35 }
36
37 template <typename T>
38 unsigned int circular_buffer_t<T>::contiguous_empty_size() const
39 {
40 if (end >= start)
41 if (start == 0)
42 return capacity - end - 1;
43 else
44 return capacity - end;
45 else
46 return start - end - 1;
47 }
48
49 template <typename T>
50 unsigned int circular_buffer_t<T>::contiguous_data_size() const
51 {
52 if (end >= start)
53 return end - start;
54 else
55 return capacity - start;
56 }
57
58 template <typename T>
59 void circular_buffer_t<T>::data_added(unsigned int bytes)
60 {
61 end += bytes;
62 assert(end <= capacity);
63 if (end == capacity)
64 end = 0;
65 }
66
67 template <typename T>
68 void circular_buffer_t<T>::reset()
69 {
70 start = 0;
71 end = 0;
72 }
73
74 template <typename T>
75 void circular_buffer_t<T>::append(const T *src, unsigned int count)
76 {
77 unsigned int copy = std::min(count, contiguous_empty_size());
78 memcpy(contiguous_empty(), src, copy * sizeof(T));
79 data_added(copy);
80 count -= copy;
81 if (count > 0) {
82 assert(count < contiguous_empty_size());
83 memcpy(contiguous_empty(), src+copy, count * sizeof(T));
84 data_added(count);
85 }
86 }
87
88 template <typename T>
89 void circular_buffer_t<T>::append(T value)
90 {
91 append(&value, 1);
92 }
93
94 /////////// remote_bitbang_t
95
96 remote_bitbang_t::remote_bitbang_t(uint16_t port, jtag_dtm_t *tap) :
97 tap(tap),
98 socket_fd(0),
99 client_fd(0),
100 recv_buf(64 * 1024),
101 send_buf(64 * 1024)
102 {
103 socket_fd = socket(AF_INET, SOCK_STREAM, 0);
104 if (socket_fd == -1) {
105 fprintf(stderr, "remote_bitbang failed to make socket: %s (%d)\n",
106 strerror(errno), errno);
107 abort();
108 }
109
110 fcntl(socket_fd, F_SETFL, O_NONBLOCK);
111 int reuseaddr = 1;
112 if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
113 sizeof(int)) == -1) {
114 fprintf(stderr, "remote_bitbang failed setsockopt: %s (%d)\n",
115 strerror(errno), errno);
116 abort();
117 }
118
119 struct sockaddr_in addr;
120 memset(&addr, 0, sizeof(addr));
121 addr.sin_family = AF_INET;
122 addr.sin_addr.s_addr = INADDR_ANY;
123 addr.sin_port = htons(port);
124
125 if (bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
126 fprintf(stderr, "remote_bitbang failed to bind socket: %s (%d)\n",
127 strerror(errno), errno);
128 abort();
129 }
130
131 if (listen(socket_fd, 1) == -1) {
132 fprintf(stderr, "remote_bitbang failed to listen on socket: %s (%d)\n",
133 strerror(errno), errno);
134 abort();
135 }
136 }
137
138 void remote_bitbang_t::accept()
139 {
140 client_fd = ::accept(socket_fd, NULL, NULL);
141 if (client_fd == -1) {
142 if (errno == EAGAIN) {
143 // No client waiting to connect right now.
144 } else {
145 fprintf(stderr, "failed to accept on socket: %s (%d)\n", strerror(errno),
146 errno);
147 abort();
148 }
149 } else {
150 fcntl(client_fd, F_SETFL, O_NONBLOCK);
151 }
152 }
153
154 void remote_bitbang_t::read()
155 {
156 // Reading from a non-blocking socket still blocks if there is no data
157 // available.
158
159 size_t count = recv_buf.contiguous_empty_size();
160 ssize_t bytes = ::read(client_fd, recv_buf.contiguous_empty(), count);
161 if (bytes == -1) {
162 if (errno == EAGAIN) {
163 // We'll try again the next call.
164 } else {
165 fprintf(stderr, "failed to read on socket: %s (%d)\n", strerror(errno), errno);
166 abort();
167 }
168 } else if (bytes == 0) {
169 // The remote disconnected.
170 client_fd = 0;
171 recv_buf.reset();
172 send_buf.reset();
173 } else {
174 recv_buf.data_added(bytes);
175 D(fprintf(stderr, "receive buffer: "));
176 for (unsigned i = 0; i < recv_buf.size(); i++) {
177 D(fprintf(stderr, "%c", recv_buf[i]));
178 }
179 D(fprintf(stderr, "\n"));
180 }
181 }
182
183 void remote_bitbang_t::write()
184 {
185 if (send_buf.empty())
186 return;
187
188 while (!send_buf.empty()) {
189 unsigned int count = send_buf.contiguous_data_size();
190 assert(count > 0);
191 ssize_t bytes = ::write(client_fd, send_buf.contiguous_data(), count);
192 if (bytes == -1) {
193 fprintf(stderr, "failed to write to socket: %s (%d)\n", strerror(errno), errno);
194 abort();
195 } else if (bytes == 0) {
196 // Client can't take any more data right now.
197 break;
198 } else {
199 D(fprintf(stderr, "wrote %zd bytes: ", bytes));
200 for (int i = 0; i < bytes; i++) {
201 D(fprintf(stderr, "%c", send_buf[i]));
202 }
203 D(fprintf(stderr, "\n"));
204 send_buf.consume(bytes);
205 }
206 }
207 }
208
209 void remote_bitbang_t::tick()
210 {
211 if (client_fd > 0) {
212 this->read();
213 process_input();
214 this->write();
215 } else {
216 this->accept();
217 }
218 }
219
220 void remote_bitbang_t::process_input()
221 {
222 // TODO: get rid of the circular buffers, and just read/write here with
223 // simple local buffers.
224 // Each message is a single character, so there's never any need to keep a
225 // partially transmitted message around.
226
227 for (unsigned i = 0; i < recv_buf.size(); i++) {
228 uint8_t command = recv_buf[i];
229
230 switch (command) {
231 case 'B': fprintf(stderr, "*BLINK*\n"); break;
232 case 'b': fprintf(stderr, "_______\n"); break;
233 case 'r': tap->reset(); break;
234 case '0': tap->set_pins(0, 0, 0); break;
235 case '1': tap->set_pins(0, 0, 1); break;
236 case '2': tap->set_pins(0, 1, 0); break;
237 case '3': tap->set_pins(0, 1, 1); break;
238 case '4': tap->set_pins(1, 0, 0); break;
239 case '5': tap->set_pins(1, 0, 1); break;
240 case '6': tap->set_pins(1, 1, 0); break;
241 case '7': tap->set_pins(1, 1, 1); break;
242 case 'R': send_buf.append(tap->tdo() ? '1' : '0'); break;
243 default:
244 fprintf(stderr, "remote_bitbang got unsupported command '%c'\n", command);
245 }
246 }
247 recv_buf.reset();
248 }