summaryrefslogtreecommitdiff
path: root/utils/hwstub/include/hwstub_net.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/hwstub/include/hwstub_net.hpp')
-rw-r--r--utils/hwstub/include/hwstub_net.hpp334
1 files changed, 334 insertions, 0 deletions
diff --git a/utils/hwstub/include/hwstub_net.hpp b/utils/hwstub/include/hwstub_net.hpp
new file mode 100644
index 0000000000..2cf6e07ccb
--- /dev/null
+++ b/utils/hwstub/include/hwstub_net.hpp
@@ -0,0 +1,334 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2016 by Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef __HWSTUB_NET_HPP__
22#define __HWSTUB_NET_HPP__
23
24#include "hwstub.hpp"
25#include <map>
26#include <thread>
27#include <future>
28#include <list>
29
30namespace hwstub {
31namespace net {
32
33/** Net context
34 *
35 * A socket context provides access to another context through a network. This
36 * is particularly useful to have another program create a USB context and provide
37 * access to it via some network. The two most useful types of network are TCP
38 * and Unix domains */
39class context : public hwstub::context
40{
41 friend class device;
42 friend class handle;
43protected:
44 context();
45public:
46 virtual ~context();
47 /** Create a socket context with an existing file descriptor. Note that the
48 * file descriptor will be closed when the context will be destroyed. */
49 static std::shared_ptr<context> create_socket(int socket_fd);
50 /** Create a TCP socket context with a domain name and a port. If port is empty,
51 * a default port is used. */
52 static std::shared_ptr<context> create_tcp(const std::string& domain,
53 const std::string& port, std::string *error = nullptr);
54 /** Create a UNIX socket context with a file system path (see man for details) */
55 static std::shared_ptr<context> create_unix(const std::string& path,
56 std::string *error = nullptr);
57 /** Create a UNIX socket context with an abstract name (see man for details) */
58 static std::shared_ptr<context> create_unix_abstract(const std::string& path,
59 std::string *error = nullptr);
60 /** Useful functions for network byte order conversion */
61 uint32_t to_net_order(uint32_t u);
62 uint32_t from_net_order(uint32_t u);
63
64 /** Default parameters */
65 static std::string default_unix_path();
66 static std::string default_tcp_domain();
67 static std::string default_tcp_port();
68
69protected:
70 /** Send a message to the server. Context will always serialize calls to send()
71 * so there is no need to worry about concurrency issues. */
72 virtual error send(void *buffer, size_t& sz) = 0;
73 /** Receive a message from the server, sz is updated with the received size.
74 * Context will always serialize calls to recv() so there is no need to
75 * worry about concurrency issues. */
76 virtual error recv(void *buffer, size_t& sz) = 0;
77 /** Perform a standard command: send a header with optional data and wait for
78 * an answer. In case of an underlying network error, the corresponding error
79 * code will be reported. If the server responds correctly, the argument array
80 * is overwritten with the servers's response. If the requests has been NACK'ed
81 * the error code will be parsed and returned as a standard error code (see details below)
82 * (note that the original error code can still be found in args[0]). No data
83 * is transmitted in case of NACK.
84 * If the server ACKs the request, this function will also perform reception of
85 * the data. In recv_data is not NULL, the receive data will be put there and the
86 * size will be written in in_size. There are two cases: either *recv_data is NULL
87 * and the function will allocate the memory based on much data is sent by the server.
88 * Or *recv_data is not NULL, in which case the function NOT allocate memory
89 * and put the data at *recv_data; in this case, *recv_size should be the set
90 * to the size of the buffer and will be updated to the received size. If the
91 * server sents more data than the buffer size, OVERFLOW will be returned.
92 * If no data was received but recv_data is not null, *recv_size will be set to
93 * zero. It is the caller's responsability to delete *recv_data. Note that if
94 * server sends data but recv_data is null, the data will still be received and
95 * thrown away.
96 * This function takes care of network byte order for cmd and arguments
97 * but not for data. */
98 error send_cmd(uint32_t cmd, uint32_t args[HWSTUB_NET_ARGS], uint8_t *send_data,
99 size_t send_size, uint8_t **recv_data, size_t *recv_size);
100 /** Ask the context to stop any communication with the server and do a clean
101 * shutdown if possible. This is a blocking call. When this function returns,
102 * there will no more calls to the underlying communication functions.
103 * This function should be called in the destructor to prevent the context from
104 * calling children functions after the object has been deconstructed. */
105 void stop_context();
106
107 /** Perform delayed init (aka HELLO stage), do nothing is not needed */
108 void delayed_init();
109 /* NOTE ctx_dev_t = uint32_t (device id) */
110 uint32_t from_ctx_dev(ctx_dev_t dev);
111 ctx_dev_t to_ctx_dev(uint32_t dev);
112 virtual error fetch_device_list(std::vector<ctx_dev_t>& list, void*& ptr);
113 virtual void destroy_device_list(void *ptr);
114 virtual error create_device(ctx_dev_t dev, std::shared_ptr<hwstub::device>& hwdev);
115 virtual bool match_device(ctx_dev_t dev, std::shared_ptr<hwstub::device> hwdev);
116
117 enum class state
118 {
119 HELLO, /* client is initialising, server has not been contacted yet */
120 IDLE, /* not doing anything */
121 DEAD, /* died on unrecoverable error */
122 };
123
124 state m_state; /* client state */
125 error m_error; /* error state for DEAD */
126};
127
128/** Socket based net context
129 *
130 * Don't use this class directly, use context::create_* calls. This class
131 * provides send()/recv() for any socket based network. */
132class socket_context : public context
133{
134 friend class context;
135protected:
136 socket_context(int socket_fd);
137public:
138 virtual ~socket_context();
139 /** set operation timeout */
140 void set_timeout(std::chrono::milliseconds ms);
141
142protected:
143 virtual error send(void *buffer, size_t& sz);
144 virtual error recv(void *buffer, size_t& sz);
145
146 int m_socketfd; /* socket file descriptor */
147};
148
149
150/** Net device
151 *
152 * Device accessed through a network */
153class device : public hwstub::device
154{
155 friend class context; /* for ctor */
156protected:
157 device(std::shared_ptr<hwstub::context> ctx, uint32_t devid);
158public:
159 virtual ~device();
160
161protected:
162 /** Return device ID */
163 uint32_t device_id();
164 virtual error open_dev(std::shared_ptr<hwstub::handle>& handle);
165 virtual bool has_multiple_open() const;
166
167 int32_t m_device_id; /* device id */
168};
169
170/** Net handle
171 *
172 * Handle used to talk to a distant device. */
173class handle : public hwstub::handle
174{
175 friend class device;
176protected:
177 handle(std::shared_ptr<hwstub::device> dev, uint32_t hid);
178public:
179 virtual ~handle();
180
181protected:
182 virtual error read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic);
183 virtual error write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic);
184 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
185 virtual error get_dev_log(void *buf, size_t& buf_sz);
186 virtual error exec_dev(uint32_t addr, uint16_t flags);
187 virtual error status() const;
188 virtual size_t get_buffer_size();
189
190 uint32_t m_handle_id; /* handle id */
191};
192
193/** Net server
194 *
195 * A server that forwards requests from net clients to a context */
196class server
197{
198protected:
199 server(std::shared_ptr<hwstub::context> contex);
200public:
201 virtual ~server();
202
203 /** Create a socket server with an existing file descriptor. Note that the
204 * file descriptor will be closed when the context will be destroyed. */
205 static std::shared_ptr<server> create_socket(std::shared_ptr<hwstub::context> contex,
206 int socket_fd);
207 /** Create a TCP socket server with a domain name and a port. If port is empty,
208 * a default port is used. */
209 static std::shared_ptr<server> create_tcp(std::shared_ptr<hwstub::context> contex,
210 const std::string& domain, const std::string& port, std::string *error = nullptr);
211 /** Create a UNIX socket server with a file system path (see man for details) */
212 static std::shared_ptr<server> create_unix(std::shared_ptr<hwstub::context> contex,
213 const std::string& path, std::string *error = nullptr);
214 /** Create a UNIX socket server with an abstract name (see man for details) */
215 static std::shared_ptr<server> create_unix_abstract(
216 std::shared_ptr<hwstub::context> contex, const std::string& path,
217 std::string *error = nullptr);
218 /** Useful functions for network byte order conversion */
219 uint32_t to_net_order(uint32_t u);
220 uint32_t from_net_order(uint32_t u);
221
222 /** Set/clear debug output for this context */
223 void set_debug(std::ostream& os);
224 inline void clear_debug() { set_debug(cnull); }
225 /** Get debug output for this context */
226 std::ostream& debug();
227protected:
228 struct client_state;
229 /** Opaque client type */
230 typedef void* srv_client_t;
231 /** The client discovery implementation must call this function when a new
232 * client wants to talk to the server. If the server is unhappy with the
233 * request, it will immediately call terminate_client() */
234 void client_arrived(srv_client_t client);
235 /** The client discovery implementation can notify asychronously about a client
236 * that left. Note that the implementation does not need to provide a mechanism,
237 * but should in this case return CLIENT_DISCONNECTED when the server performs
238 * a send() or recv() on a disconnected client. The server will always call
239 * after receiving client_left() but since this call is asychronous, the
240 * implementation must be prepared to deal with extra send()/recv() in the mean
241 * time. */
242 void client_left(srv_client_t client);
243 /** The client discovery implementation can ask the server to stop all client
244 * threads. This is a blocking call. When this function returns, there will no
245 * more calls to the underlying communication functions. Note that the server
246 * will normally call terminate_client() on each active client at this point.
247 * This function should be called in the destructor to prevent the server from
248 * calling children functions after the object has been deconstructed. */
249 void stop_server();
250 /** Notify that the connection to a client is now finished. After this call, no
251 * more send()/recv() will be made to the client and the associated data will
252 * be freed. After this call, the implementation is not allowed to call client_left()
253 * for this client (assuming it did not previously). The implementation should close
254 * the communication channel at this point and free any associated data. */
255 virtual void terminate_client(srv_client_t client) = 0;
256 /** Send a message to the client. Server will always serialize calls to send()
257 * for a given client so there is no need to worry about concurrency issues. */
258 virtual error send(srv_client_t client, void *buffer, size_t& sz) = 0;
259 /** Receive a message from the client, sz is updated with the received size.
260 * Server will always serialize calls to recv() for a given client so there
261 * is no need to worry about concurrency issues. See comment about client_left(). */
262 virtual error recv(srv_client_t client, void *buffer, size_t& sz) = 0;
263 /** handle command: cmd and arguments are in host order, the function should
264 * either return an error (command will be NACKed) or must fill the arguments
265 * and data for the answer. Note that the data is still in network byte order.
266 * If the funtion wants to send data back, it must set *send_data to a valid
267 * pointer, this pointer will be freed after the data is sent back. */
268 error handle_cmd(client_state *state, uint32_t cmd, uint32_t args[HWSTUB_NET_ARGS],
269 uint8_t *recv_data, size_t recv_size, uint8_t*& send_data, size_t& send_size);
270
271 /* complete state of a client */
272 struct client_state
273 {
274 client_state(srv_client_t cl, std::future<void>&& f);
275 srv_client_t client; /* client */
276 std::future<void> future; /* thread (see .cpp for explaination) */
277 volatile bool exit; /* exit flag */
278 uint32_t next_dev_id; /* next device ID */
279 uint32_t next_handle_id; /* next handle ID */
280 /* dev ID <-> hwstub dev map */
281 std::map<uint32_t, std::shared_ptr<hwstub::device>> dev_map;
282 /* handle ID -> hwstub handle map */
283 std::map<uint32_t, std::shared_ptr<hwstub::handle>> handle_map;
284 };
285
286 /** Client thread */
287 static void client_thread2(server *s, client_state *cs);
288 void client_thread(client_state *cs);
289
290 std::shared_ptr<hwstub::context> m_context; /* context to perform operation */
291 std::list<client_state> m_client; /* client list */
292 std::recursive_mutex m_mutex; /* server mutex */
293 std::ostream *m_debug; /* debug stream */
294};
295
296/** Socket based net server
297 *
298 */
299class socket_server : public server
300{
301protected:
302 socket_server(std::shared_ptr<hwstub::context> contex, int socket_fd);
303public:
304 virtual ~socket_server();
305 /** create a server */
306 static std::shared_ptr<server> create(std::shared_ptr<hwstub::context> contex,
307 int socket_fd);
308 /** set operation timeout */
309 void set_timeout(std::chrono::milliseconds ms);
310
311protected:
312 virtual void terminate_client(srv_client_t client);
313 virtual error send(srv_client_t client, void *buffer, size_t& sz);
314 virtual error recv(srv_client_t, void *buffer, size_t& sz);
315
316 /* NOTE srv_client_t = int (client file descriptor) */
317 int from_srv_client(srv_client_t cli);
318 srv_client_t to_srv_client(int fd);
319
320 /** Discovery thread */
321 static void discovery_thread1(socket_server *s);
322 void discovery_thread();
323
324 static const int LISTEN_QUEUE_SIZE = 5;
325 struct timeval m_timeout; /* operations timeout */
326 int m_socketfd; /* socket file descriptor */
327 std::thread m_discovery_thread; /* thread handling client discovery */
328 volatile bool m_discovery_exit; /* exit flag */
329};
330
331} // namespace net
332} // namespace hwstub
333
334#endif /* __HWSTUB_NET_HPP__ */