quic/qbox
Loading...
Searching...
No Matches
char_backend_socket.h
1/*
2 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
12#ifndef _GS_UART_BACKEND_SOCKET_H_
13#define _GS_UART_BACKEND_SOCKET_H_
14
15#include <unistd.h>
16#include <poll.h>
17#include <errno.h>
18#include <cstring>
19#include <stdio.h>
20#include <sys/stat.h>
21#include <fcntl.h>
22#include <sys/socket.h>
23#include <arpa/inet.h>
24#include <netinet/in.h>
25#include <netinet/tcp.h>
26#include <sys/ioctl.h>
27#include <signal.h>
28#include <netdb.h>
29
30#include <scp/report.h>
31
32#include <async_event.h>
33#include <uutils.h>
34#include <ports/biflow-socket.h>
35#include <module_factory_registery.h>
36
37class char_backend_socket : public sc_core::sc_module
38{
39protected:
40 cci::cci_param<std::string> p_address;
41 cci::cci_param<bool> p_server;
42 cci::cci_param<bool> p_nowait;
43 cci::cci_param<bool> p_sigquit;
44
45private:
46 SCP_LOGGER();
47 std::string ip;
48 std::string port;
49
50public:
52
53#ifdef WIN32
54#pragma message("char_backend_socket not yet implemented for WIN32")
55#endif
56
57 int m_srv_socket;
58 int m_socket = -1;
59 uint8_t m_buf[256];
60
61 void sock_setup()
62 {
63 int flags = fcntl(m_socket, F_GETFL, 0);
64 flags |= O_NONBLOCK;
65 if (::fcntl(m_socket, F_SETFL, flags) != 0) {
66 SCP_ERR(()) << "setting socket in non-blocking mode failed: " << std::strerror(errno);
67 }
68
69 flags = 1;
70 if (::setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags))) {
71 SCP_WARN(()) << "setting up TCP_NODELAY option failed: " << std::strerror(errno);
72 }
73 }
74
75 void start_of_simulation()
76 {
77 size_t count = std::count(p_address.get_value().begin(), p_address.get_value().end(), ':');
78 if (count == 1) {
79 size_t first = p_address.get_value().find_first_of(':');
80 ip = p_address.get_value().substr(0, first);
81 port = p_address.get_value().substr(first + 1);
82 } else {
83 SCP_ERR(()) << "malformed address, expecting IP:PORT (e.g. 127.0.0.1:4001)";
84 return;
85 }
86
87 SCP_DEBUG(()) << "IP: " << ip << ", PORT: " << port;
88
89 if (p_server) {
90 setup_tcp_server(ip, port);
91 } else {
92 setup_tcp_client(ip, port);
93 }
94
95 new std::thread(&char_backend_socket::rcv_thread, this);
96 }
97
98 char_backend_socket(sc_core::sc_module_name name)
99 : sc_core::sc_module(name)
100 , p_address("address", "127.0.0.1:4001", "socket address IP:Port")
101 , p_server("server", true, "type of socket: true if server - false if client")
102 , p_nowait("nowait", true, "setting socket in non-blocking mode")
103 , p_sigquit("sigquit", false, "Interpret 0x1c in the data stream as a sigquit")
104 , socket("biflow_socket")
105 {
106 SCP_TRACE(()) << "char_backend_socket constructor";
107 socket.register_b_transport(this, &char_backend_socket::writefn);
108 }
109
110 void end_of_elaboration() { socket.can_receive_any(); }
111
112 void* rcv_thread()
113 {
114 for (;;) {
115 if (p_server && m_socket < 0) {
116 socklen_t addr_len = sizeof(struct sockaddr_in);
118 int flag;
119
120 m_socket = ::accept(m_srv_socket, (struct sockaddr*)&client_addr, &addr_len);
121 if (m_socket < 0) {
122 continue;
123 }
124 sock_setup();
125
126 char str[INET_ADDRSTRLEN];
128 int cport = ntohs(client_addr.sin_port);
129
130 SCP_DEBUG(()) << "incoming connection from " << str << ":" << cport;
131 } else if (!p_server && m_socket < 0) {
132 SCP_DEBUG(())("Waiting for connection");
133 sleep(1);
134 setup_tcp_client(ip, port);
135 continue;
136 }
137
138 struct pollfd fd;
139 int ret;
140 fd.fd = m_socket;
141 fd.events = POLLIN;
142 if (poll(&fd, 1, 1000) == 0) {
143 continue;
144 }
145 if (fd.revents > 0x4) {
146 close(m_socket);
147 m_socket = -1;
148 if (!p_nowait) {
149 SCP_FATAL(())("Non waiting Socket closed");
150 } else {
151 SCP_WARN(())("Socket closed, will wait for new connection");
152 }
153 continue;
154 }
155 ret = ::read(m_socket, m_buf, 1); // We can only guarantee 1 read will work
156 if (ret > 0) {
157 for (int i = 0; i < ret; i++) {
158 unsigned char c = m_buf[i];
159 if (p_sigquit && c == 0x1c) {
160 sc_core::sc_stop();
161 }
162 socket.enqueue(c);
163 }
164 }
165 }
166 }
167
168 void writefn(tlm::tlm_generic_payload& txn, sc_core::sc_time& t)
169 {
170 while (m_socket < 0) {
171 if (p_nowait) {
172 return;
173 }
174 SCP_WARN(()) << "waiting for socket connection on IP address: " << p_address.get_value();
175 sleep(1);
176 }
177
178 uint8_t* data = txn.get_data_ptr();
179 for (int i = 0; i < txn.get_streaming_width(); i++) {
180 do {
181 if ((::write(m_socket, &data[i], 1)) != 1) {
182 if (errno == EAGAIN) {
183 SCP_WARN(())("(Blocking) write did not complete (EAGAIN)");
184 sleep(1);
185 } else {
186 if (p_nowait) {
187 SCP_WARN(())("(Non blocking) socket closed");
188 return;
189 } else {
190 SCP_FATAL(())("(Blocking) socket closed.");
191 }
192 }
193 } else {
194 break; // Write completed normally
195 }
196 } while (true);
197 }
198 }
199
200 void setup_tcp_server(std::string ip, std::string port)
201 {
202 int ret;
203 struct sockaddr_in addr_in;
204 int iport;
205 int flag;
206
207 SCP_DEBUG(()) << "setting up TCP server on " << ip << ":" << port;
208
209 socklen_t addr_len = sizeof(struct sockaddr_in);
210
211 m_srv_socket = ::socket(AF_INET, SOCK_STREAM, 0);
212 if (m_srv_socket < 0) {
213 SCP_ERR(()) << "socket failed: " << std::strerror(errno);
214 return;
215 }
216
217 flag = 1;
218 ::setsockopt(m_srv_socket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
219
220 iport = std::stoi(port);
221
222 ::memset((char*)&addr_in, 0, sizeof(addr_in));
223 addr_in.sin_family = AF_INET;
224 addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
225 addr_in.sin_port = htons(iport);
226
227 ret = ::bind(m_srv_socket, (struct sockaddr*)&addr_in, sizeof(addr_in));
228 if (ret < 0) {
229 ::close(m_srv_socket);
230 m_srv_socket = -1;
231 SCP_ERR(()) << "bind to port '" << iport << "' failed: " << std::strerror(errno);
232 return;
233 }
234
235 ret = listen(m_srv_socket, 1);
236 if (ret < 0) {
237 ::close(m_srv_socket);
238 m_srv_socket = -1;
239 SCP_ERR(()) << "listen failed: " << std::strerror(errno);
240 return;
241 }
242 // the connection will be done in the rcv_thread
243 }
244
245 void setup_tcp_client(std::string ip, std::string port)
246 {
247 int flag = 1;
248 int status;
249 struct addrinfo hints;
250 struct addrinfo* servinfo;
251
252 SCP_INFO(()) << "setting up TCP client connection to " << ip << ":" << port;
253
254 m_socket = ::socket(AF_INET, SOCK_STREAM, 0);
255
256 if (m_socket == -1) {
257 SCP_ERR(()) << "socket failed: " << std::strerror(errno);
258 return;
259 }
260
261 memset(&hints, 0, sizeof(hints));
262 hints.ai_family = AF_UNSPEC;
263 hints.ai_socktype = SOCK_STREAM;
264
265 status = getaddrinfo(ip.c_str(), port.c_str(), &hints, &servinfo);
266 if (status == -1) {
267 SCP_ERR(()) << "getaddrinfo failed: " << std::strerror(errno);
268 close_sock();
269 }
270
271 if (::connect(m_socket, servinfo->ai_addr, servinfo->ai_addrlen) == -1) {
272 SCP_ERR(()) << "connect failed: " << std::strerror(errno);
274 close_sock();
275 }
277
278 sock_setup();
279
280 return;
281 }
282
283 void close_sock()
284 {
285 ::close(m_socket);
286 m_socket = -1;
287 }
288};
289extern "C" void module_register();
290#endif
Definition target.h:160
Definition char_backend_socket.h:38
Definition biflow-socket.h:73
void can_receive_any()
can_receive_any Allow unlimited items to arrive.
Definition biflow-socket.h:264
void enqueue(T data)
enqueue Enqueue data to be sent (unlimited queue size) NOTE: Thread safe.
Definition biflow-socket.h:277
void register_b_transport(MODULE *mod, void(MODULE::*cb)(tlm::tlm_generic_payload &, sc_core::sc_time &))
Register b_transport to be called whenever data is received from the socket.
Definition biflow-socket.h:227