summaryrefslogtreecommitdiff
path: root/utils/hwstub/include
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2016-02-07 21:42:15 +0000
committerAmaury Pouly <amaury.pouly@gmail.com>2016-04-08 19:37:30 +0200
commit3d8a08ca25c3041ac677335e51341d966a9b370b (patch)
tree1bf06dea354e3ae95c1ec91b6ee259d0ac21659c /utils/hwstub/include
parent56dc54d38ac6c1d47ea6dbae88b1e5f7fee9f3ec (diff)
downloadrockbox-3d8a08ca25c3041ac677335e51341d966a9b370b.tar.gz
rockbox-3d8a08ca25c3041ac677335e51341d966a9b370b.zip
hwstub: rewrite and expand library
Rewrite the hwstub library in C++, with a clean and modular design. The library was designed from the ground up to be aware of multithreading issues and to handle memory allocation nicely with shared pointers. Compared to the original library, it brings the following major features: - support for JZ boot devices, it is very easy to add support for others - support for network transparent operations (through sockets): both tcp and unix domains are support Change-Id: I75899cb9c7aa938c17ede2bb3f468e7a55d625b4
Diffstat (limited to 'utils/hwstub/include')
-rw-r--r--utils/hwstub/include/hwstub.h70
-rw-r--r--utils/hwstub/include/hwstub.hpp351
-rw-r--r--utils/hwstub/include/hwstub_net.hpp334
-rw-r--r--utils/hwstub/include/hwstub_protocol.h318
-rw-r--r--utils/hwstub/include/hwstub_uri.hpp131
-rw-r--r--utils/hwstub/include/hwstub_usb.hpp194
-rw-r--r--utils/hwstub/include/hwstub_virtual.hpp159
7 files changed, 1557 insertions, 0 deletions
diff --git a/utils/hwstub/include/hwstub.h b/utils/hwstub/include/hwstub.h
new file mode 100644
index 0000000000..4d12de8eda
--- /dev/null
+++ b/utils/hwstub/include/hwstub.h
@@ -0,0 +1,70 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2012 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__
22#define __HWSTUB__
23
24#include <libusb.h>
25#include "hwstub_protocol.h"
26
27#ifdef __cplusplus
28extern "C" {
29#endif
30
31/**
32 *
33 * Low-Level interface
34 *
35 */
36
37struct hwstub_device_t;
38
39/* Returns hwstub interface, or -1 if none was found */
40int hwstub_probe(libusb_device *dev);
41/* Helper function which returns a list of all hwstub devices found. The caller
42 * must unref all of them when done, possibly using libusb_free_device_list().
43 * Return number of devices or <0 on error */
44ssize_t hwstub_get_device_list(libusb_context *ctx, libusb_device ***list);
45/* Returns NULL on error */
46struct hwstub_device_t *hwstub_open(libusb_device_handle *handle);
47/* Returns 0 on success. Does *NOT* close the usb handle */
48int hwstub_release(struct hwstub_device_t *dev);
49
50/* Returns number of bytes filled */
51int hwstub_get_desc(struct hwstub_device_t *dev, uint16_t desc, void *info, size_t sz);
52/* Returns number of bytes filled */
53int hwstub_get_log(struct hwstub_device_t *dev, void *buf, size_t sz);
54/* Returns number of bytes written/read or <0 on error */
55int hwstub_read(struct hwstub_device_t *dev, uint32_t addr, void *buf, size_t sz);
56int hwstub_read_atomic(struct hwstub_device_t *dev, uint32_t addr, void *buf, size_t sz);
57int hwstub_write(struct hwstub_device_t *dev, uint32_t addr, const void *buf, size_t sz);
58int hwstub_write_atomic(struct hwstub_device_t *dev, uint32_t addr, const void *buf, size_t sz);
59int hwstub_rw_mem(struct hwstub_device_t *dev, int read, uint32_t addr, void *buf, size_t sz);
60int hwstub_rw_mem_atomic(struct hwstub_device_t *dev, int read, uint32_t addr, void *buf, size_t sz);
61/* Returns <0 on error */
62int hwstub_exec(struct hwstub_device_t *dev, uint32_t addr, uint16_t flags);
63int hwstub_call(struct hwstub_device_t *dev, uint32_t addr);
64int hwstub_jump(struct hwstub_device_t *dev, uint32_t addr);
65
66#ifdef __cplusplus
67} // extern "C"
68#endif
69
70#endif /* __HWSTUB__ */ \ No newline at end of file
diff --git a/utils/hwstub/include/hwstub.hpp b/utils/hwstub/include/hwstub.hpp
new file mode 100644
index 0000000000..deac976240
--- /dev/null
+++ b/utils/hwstub/include/hwstub.hpp
@@ -0,0 +1,351 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2015 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_HPP__
22#define __HWSTUB_HPP__
23
24#include "hwstub_protocol.h"
25#include <string>
26#include <mutex>
27#include <vector>
28#include <cstdint>
29#include <atomic>
30#include <memory>
31#include <chrono>
32#include <thread>
33#include <mutex>
34#include <condition_variable>
35#include <ostream>
36
37namespace hwstub {
38
39class context;
40class device;
41class handle;
42class context_poller;
43
44/** C++ equivalent of /dev/null for streams */
45extern std::ostream cnull;
46extern std::wostream wcnull;
47
48/** Errors */
49enum class error
50{
51 SUCCESS, /** Success */
52 ERROR, /** Unspecified error */
53 DISCONNECTED, /** Device has been disconnected */
54 PROBE_FAILURE, /** Device did not pass probing */
55 NO_CONTEXT, /** The context has been destroyed */
56 USB_ERROR, /** Unspecified USB error */
57 DUMMY, /** Call on dummy device/handle */
58 NO_SERVER, /** The server could not be reached */
59 SERVER_DISCONNECTED, /** Context got disconnected from the server */
60 SERVER_MISMATCH, /** The server is not compatible with hwstub */
61 PROTOCOL_ERROR, /** Network protocol error */
62 NET_ERROR, /** Network error */
63 TIMEOUT, /** Operation timed out */
64 OVERFLW, /** Operation stopped to prevent buffer overflow */
65};
66
67/** Return a string explaining the error */
68std::string error_string(error err);
69
70/** NOTE Multithreading:
71 * Unless specified, all methods are thread-safe
72 */
73
74/** Context
75 *
76 * A context provides a list of available devices and may notify devices
77 * arrival and departure.
78 *
79 * A context provides a way to regularly poll for derive changes. There are two
80 * ways to manually force an update:
81 * - on call to get_device_list(), the list is already refetched
82 * - on call to update_list() to force list update
83 * Note that automatic polling is disabled by default.
84 */
85class context : public std::enable_shared_from_this<context>
86{
87protected:
88 context();
89public:
90 /** On destruction, the context will destroy all the devices. */
91 virtual ~context();
92 /** Get device list, clears the list in argument first. All devices in the list
93 * are still connected (or believe to be). This function will update the device
94 * list. */
95 error get_device_list(std::vector<std::shared_ptr<device>>& list);
96 /** Force the context to update its internal list of devices. */
97 error update_list();
98 /** Ask the context to automatically poll for device changes.
99 * Note that this might spawn a new thread to do so, in which case it will
100 * be destroyed/stop on deletetion or when stop_polling() is called. If
101 * polling is already enabled, this function will change the polling interval. */
102 void start_polling(std::chrono::milliseconds interval = std::chrono::milliseconds(250));
103 /** Stop polling. */
104 void stop_polling();
105 /** Register a notification callback with arguments (context,arrived,device)
106 * WARNING the callback may be called asynchronously ! */
107 typedef std::function<void(std::shared_ptr<context>, bool, std::shared_ptr<device>)> notification_callback_t;
108 typedef size_t callback_ref_t;
109 callback_ref_t register_callback(const notification_callback_t& fn);
110 void unregister_callback(callback_ref_t ref);
111 /** Return a dummy device that does nothing. A dummy device might be useful
112 * in cases where one still wants a valid pointer to no device. This dummy
113 * device does not appear in the list, it can be opened and will fail all requests. */
114 error get_dummy_device(std::shared_ptr<device>& dev);
115 /** Set/clear debug output for this context */
116 void set_debug(std::ostream& os);
117 inline void clear_debug() { set_debug(cnull); }
118 /** Get debug output for this context */
119 std::ostream& debug();
120
121protected:
122 /** Notify the context about a device. If arrived is true, the device is
123 * added to the list and a reference will be added to it. If arrived is false,
124 * the device is marked as disconnected(), removed from the list and a
125 * reference will be removed from it. Adding a device that matches an
126 * existing one will do nothing. */
127 void change_device(bool arrived, std::shared_ptr<device> dev);
128 /** Do device notification */
129 void notify_device(bool arrived, std::shared_ptr<device> dev);
130 /** Opaque device type */
131 typedef void* ctx_dev_t;
132 /** Fetch the device list. Each item in the list is an opaque pointer. The function
133 * can also provide a pointer that will be used to free the list resources
134 * if necessary. Return <0 on error. */
135 virtual error fetch_device_list(std::vector<ctx_dev_t>& list, void*& ptr) = 0;
136 /** Destroy the resources created to get the list. */
137 virtual void destroy_device_list(void *ptr) = 0;
138 /** Create a new hwstub device from the opaque pointer. Return <0 on error.
139 * This function needs not add a reference to the newly created device. */
140 virtual error create_device(ctx_dev_t dev, std::shared_ptr<device>& hwdev) = 0;
141 /** Return true if the opaque pointer corresponds to the device. Only called
142 * from map_device(). */
143 virtual bool match_device(ctx_dev_t dev, std::shared_ptr<device> hwdev) = 0;
144 /** Check if a device matches another one in the list */
145 bool contains_dev(const std::vector<device*>& list, ctx_dev_t dev);
146
147 struct callback_t
148 {
149 notification_callback_t callback;
150 callback_ref_t ref;
151 };
152
153 std::shared_ptr<context_poller> m_poller; /* poller object */
154 std::recursive_mutex m_mutex; /* list mutex */
155 std::vector<std::shared_ptr<device>> m_devlist; /* list of devices */
156 std::vector<callback_t> m_callbacks; /* list of callbacks */
157 callback_ref_t m_next_cb_ref; /* next callback reference */
158 std::ostream *m_debug; /* debug stream */
159};
160
161/** Context Poller
162 *
163 * This class provides a way to regularly poll a context for device changes.
164 * NOTE this class is not meant to be used directly since context already
165 * provides access to it via start_polling() and stop_polling() */
166class context_poller
167{
168public:
169 context_poller(std::weak_ptr<context> ctx, std::chrono::milliseconds interval = std::chrono::milliseconds(250));
170 ~context_poller();
171 /** Set polling interval (in milliseconds) (works even if polling already enabled) */
172 void set_interval(std::chrono::milliseconds interval);
173 /** Start polling */
174 void start();
175 /** Stop polling. After return, no function will be made. */
176 void stop();
177
178protected:
179 static void thread(context_poller *poller);
180 void poll();
181
182 std::weak_ptr<context> m_ctx; /* context */
183 bool m_running; /* are we running ? */
184 bool m_exit; /* exit flag for the thread */
185 std::thread m_thread; /* polling thread */
186 std::mutex m_mutex; /* mutex lock */
187 std::condition_variable m_cond; /* signalling condition */
188 std::chrono::milliseconds m_interval; /* Interval */
189};
190
191/** Device
192 *
193 * A device belongs to a context.
194 * Note that a device only keeps a weak pointer to the context, so it is possible
195 * for the context to be destroyed during the life of the device, in which case
196 * all operations on it will fail. */
197class device : public std::enable_shared_from_this<device>
198{
199protected:
200 device(std::shared_ptr<context> ctx);
201public:
202 virtual ~device();
203 /** Open a handle to the device. Several handles may be opened concurrently. */
204 error open(std::shared_ptr<handle>& handle);
205 /** Disconnect the device. This will notify the context that the device is gone. */
206 void disconnect();
207 /** Returns true if the device is still connected. */
208 bool connected();
209 /** Get context (might be empty) */
210 std::shared_ptr<context> get_context();
211
212protected:
213 /** Some subsystems allow for hardware to be open several times and so do not.
214 * For example, libusb only allows one handle per device. To workaround this issue,
215 * open() will do some magic to allow for several open() even when the hardware
216 * supports only one. If the device does not support multiple
217 * handles (as reported by has_multiple_open()), open() will only call open_dev()
218 * the first time the device is opened and will redirect other open() calls to
219 * this handle using proxy handles. If the device supports multiple handles,
220 * open() will simply call open_dev() each time.
221 * The open_dev() does not need to care about this magic and only needs to
222 * open the device and returns the handle to it.
223 * NOTE this function is always called with the mutex locked already. */
224 virtual error open_dev(std::shared_ptr<handle>& handle) = 0;
225 /** Return true if device can be opened multiple times. In this case, each
226 * call to open() will generate a call to do_open(). Otherwise, proxy handles
227 * will be created for each open() and do_open() will only be called the first
228 * time. */
229 virtual bool has_multiple_open() const = 0;
230
231 std::weak_ptr<context> m_ctx; /* pointer to context */
232 std::recursive_mutex m_mutex; /* device state mutex: ref count, connection status */
233 bool m_connected; /* false once device is disconnected */
234 std::weak_ptr<handle> m_handle; /* weak pointer to the opened handle (if !has_multiple_open()) */
235};
236
237/** Handle
238 *
239 * A handle is tied to a device and provides access to the stub operation.
240 * The handle is reference counted and is destroyed
241 * when its reference count decreased to zero.
242 */
243class handle : public std::enable_shared_from_this<handle>
244{
245protected:
246 /** A handle will always hold a reference to the device */
247 handle(std::shared_ptr<device> dev);
248public:
249 /** When destroyed, the handle will release its reference to the device */
250 virtual ~handle();
251 /** Return associated device */
252 std::shared_ptr<device> get_device();
253 /** Fetch a descriptor, buf_sz is the size of the buffer and is updated to
254 * reflect the number of bytes written to the buffer. */
255 error get_desc(uint16_t desc, void *buf, size_t& buf_sz);
256 /** Fetch part of the log, buf_sz is the size of the buffer and is updated to
257 * reflect the number of bytes written to the buffer. */
258 error get_log(void *buf, size_t& buf_sz);
259 /** Ask the stub to execute some code.
260 * NOTE: this may kill the stub */
261 error exec(uint32_t addr, uint16_t flags);
262 /** Read/write some device memory. sz is the size of the buffer and is updated to
263 * reflect the number of bytes written to the buffer.
264 * NOTE: the stub may or may not recover from bad read/write, so this may kill it.
265 * NOTE: the default implemtentation of read() and write() will split transfers
266 * according to the buffer size and call read_dev() and write_dev() */
267 error read(uint32_t addr, void *buf, size_t& sz, bool atomic);
268 error write(uint32_t addr, const void *buf, size_t& sz, bool atomic);
269 /** Get device buffer size: any read() or write() greater than this size
270 * will be split into several transaction to avoid overflowing device
271 * buffer. */
272 virtual size_t get_buffer_size() = 0;
273 /** Check a handle status. A successful handle does not guarantee successful
274 * operations. An invalid handle will typically report probing failure (the
275 * device did not pass probing) or disconnection.
276 * The default implemtentation will test if context still exists and connection status. */
277 virtual error status() const;
278 /** Shorthand for status() == HWSTUB_SUCCESS */
279 inline bool valid() const { return status() == error::SUCCESS; }
280
281 /** Helper functions */
282 error get_version_desc(hwstub_version_desc_t& desc);
283 error get_layout_desc(hwstub_layout_desc_t& desc);
284 error get_stmp_desc(hwstub_stmp_desc_t& desc);
285 error get_pp_desc(hwstub_pp_desc_t& desc);
286 error get_jz_desc(hwstub_jz_desc_t& desc);
287 error get_target_desc(hwstub_target_desc_t& desc);
288
289protected:
290 /** The get_desc(), get_log(), exec(), read() and write() function
291 * take care of details so that each implementation can safely assume that
292 * the hwstub context exists and will not be destroyed during the execution
293 * of the function. It will also return early if the device has been disconnected.
294 *
295 * NOTE on read() and write():
296 * Since devices have a limited buffer, big transfers must be split into
297 * smaller ones. The high-level read() and write() functions perform this
298 * splitting in a generic way, based on get_buffer_size(), and calling read_dev()
299 * and write_dev() which do the actual operation.
300 * These function can safely assume that sz <= get_buffer_size(). */
301 virtual error read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic) = 0;
302 virtual error write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic) = 0;
303 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz) = 0;
304 virtual error get_dev_log(void *buf, size_t& buf_sz) = 0;
305 virtual error exec_dev(uint32_t addr, uint16_t flags) = 0;
306
307 std::shared_ptr<device> m_dev; /* pointer to device */
308 std::atomic<int> m_refcnt; /* reference count */
309 std::recursive_mutex m_mutex; /* operation mutex to serialise operations */
310};
311
312/** Dummy device */
313class dummy_device : public device
314{
315 friend class context; /* for ctor */
316protected:
317 dummy_device(std::shared_ptr<context> ctx);
318public:
319 virtual ~dummy_device();
320
321protected:
322 virtual error open_dev(std::shared_ptr<handle>& handle);
323 virtual bool has_multiple_open() const;
324};
325
326/** Dummy handle */
327class dummy_handle : public handle
328{
329 friend class dummy_device;
330protected:
331 dummy_handle(std::shared_ptr<device> dev);
332public:
333 virtual ~dummy_handle();
334
335protected:
336 virtual error read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic);
337 virtual error write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic);
338 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
339 virtual error get_dev_log(void *buf, size_t& buf_sz);
340 virtual error exec_dev(uint32_t addr, uint16_t flags);
341 virtual error status() const;
342 virtual size_t get_buffer_size();
343
344 struct hwstub_version_desc_t m_desc_version;
345 struct hwstub_layout_desc_t m_desc_layout;
346 struct hwstub_target_desc_t m_desc_target;
347};
348
349} // namespace hwstub
350
351#endif /* __HWSTUB_HPP__ */
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__ */
diff --git a/utils/hwstub/include/hwstub_protocol.h b/utils/hwstub/include/hwstub_protocol.h
new file mode 100644
index 0000000000..39d2f2ebfe
--- /dev/null
+++ b/utils/hwstub/include/hwstub_protocol.h
@@ -0,0 +1,318 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2012 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_PROTOCOL__
22#define __HWSTUB_PROTOCOL__
23
24#include <stdint.h>
25
26/**
27 * This file contains the data structures used in the USB and network protocol.
28 * All USB data uses the standard USB byte order which is little-endian.
29 */
30
31/**
32 * HWStub protocol version
33 */
34
35#define HWSTUB_VERSION_MAJOR 4
36#define HWSTUB_VERSION_MINOR 2
37
38#define HWSTUB_VERSION__(maj, min) #maj"."#min
39#define HWSTUB_VERSION_(maj, min) HWSTUB_VERSION__(maj, min)
40#define HWSTUB_VERSION HWSTUB_VERSION_(HWSTUB_VERSION_MAJOR, HWSTUB_VERSION_MINOR)
41
42/**
43 * A device can use any VID:PID but in case hwstub is in full control of the
44 * device, the preferred VID:PID is the following.
45 */
46
47#define HWSTUB_USB_VID 0xfee1
48#define HWSTUB_USB_PID 0xdead
49
50/**
51 * The device class should be per interface and the hwstub interface must use
52 * the following class, subclass and protocol.
53 */
54
55#define HWSTUB_CLASS 0xff
56#define HWSTUB_SUBCLASS 0xde
57#define HWSTUB_PROTOCOL 0xad
58
59/**********************************
60 * Descriptors
61 **********************************/
62
63/**
64 * Descriptors can be retrieved using configuration descriptor or individually
65 * using the standard GetDescriptor request on the interface.
66 */
67
68#define HWSTUB_DT_VERSION 0x41 /* mandatory */
69#define HWSTUB_DT_LAYOUT 0x42 /* mandatory */
70#define HWSTUB_DT_TARGET 0x43 /* mandatory */
71#define HWSTUB_DT_STMP 0x44 /* mandatory for STMP */
72#define HWSTUB_DT_PP 0x45 /* mandatory for PP */
73#define HWSTUB_DT_JZ 0x46 /* mandatory for JZ */
74
75struct hwstub_version_desc_t
76{
77 uint8_t bLength;
78 uint8_t bDescriptorType;
79 /* full version information */
80 uint8_t bMajor;
81 uint8_t bMinor;
82 uint8_t bRevision;
83} __attribute__((packed));
84
85struct hwstub_layout_desc_t
86{
87 uint8_t bLength;
88 uint8_t bDescriptorType;
89 /* describe the range of memory used by the running code */
90 uint32_t dCodeStart;
91 uint32_t dCodeSize;
92 /* describe the range of memory used by the stack */
93 uint32_t dStackStart;
94 uint32_t dStackSize;
95 /* describe the range of memory available as a buffer */
96 uint32_t dBufferStart;
97 uint32_t dBufferSize;
98} __attribute__((packed));
99
100struct hwstub_stmp_desc_t
101{
102 uint8_t bLength;
103 uint8_t bDescriptorType;
104 /* Chip ID and revision */
105 uint16_t wChipID; /* 0x3780 for STMP3780 for example */
106 uint8_t bRevision; /* 0=TA1 on STMP3780 for example */
107 uint8_t bPackage; /* 0=169BGA for example */
108} __attribute__((packed));
109
110struct hwstub_pp_desc_t
111{
112 uint8_t bLength;
113 uint8_t bDescriptorType;
114 /* Chip ID and revision */
115 uint16_t wChipID; /* 0x5002 for PP5002 for example */
116 uint8_t bRevision[2]; /* 'B1' for B1 for example */
117} __attribute__((packed));
118
119struct hwstub_jz_desc_t
120{
121 uint8_t bLength;
122 uint8_t bDescriptorType;
123 /* Chip ID and revision */
124 uint16_t wChipID; /* 0x4760 for Jz4760 for example */
125 uint8_t bRevision; /* 0 for Jz4760, 'B' for JZ4760B */
126} __attribute__((packed));
127
128#define HWSTUB_TARGET_UNK ('U' | 'N' << 8 | 'K' << 16 | ' ' << 24)
129#define HWSTUB_TARGET_STMP ('S' | 'T' << 8 | 'M' << 16 | 'P' << 24)
130#define HWSTUB_TARGET_RK27 ('R' | 'K' << 8 | '2' << 16 | '7' << 24)
131#define HWSTUB_TARGET_PP ('P' | 'P' << 8 | ' ' << 16 | ' ' << 24)
132#define HWSTUB_TARGET_ATJ ('A' | 'T' << 8 | 'J' << 16 | ' ' << 24)
133#define HWSTUB_TARGET_JZ ('J' | 'Z' << 8 | '4' << 16 | '7' << 24)
134
135struct hwstub_target_desc_t
136{
137 uint8_t bLength;
138 uint8_t bDescriptorType;
139 /* Target ID and name */
140 uint32_t dID;
141 char bName[58];
142} __attribute__((packed));
143
144/**
145 * Socket command packet header: any transfer (in both directions) start with this.
146 * All data is transmitted in network byte order.
147 */
148#define HWSTUB_NET_ARGS 4
149
150struct hwstub_net_hdr_t
151{
152 uint32_t magic; /* magic value (HWSERVER_MAGIC) */
153 uint32_t cmd; /* command (OR'ed with (N)ACK on response) */
154 uint32_t length; /* length of the data following this header */
155 uint32_t args[HWSTUB_NET_ARGS]; /* command arguments */
156} __attribute__((packed));
157
158/**
159 * Control commands
160 *
161 * These commands are sent to the interface, using the standard bRequest field
162 * of the SETUP packet. The wIndex contains the interface number. The wValue
163 * contains an ID which is used for requests requiring several transfers.
164 */
165#define HWSTUB_GET_LOG 0x40
166#define HWSTUB_READ 0x41
167#define HWSTUB_READ2 0x42
168#define HWSTUB_WRITE 0x43
169#define HWSTUB_EXEC 0x44
170#define HWSTUB_READ2_ATOMIC 0x45
171#define HWSTUB_WRITE_ATOMIC 0x46
172
173/* the following commands and the ACK/NACK mechanism are net only */
174#define HWSERVER_ACK(n) (0x100|(n))
175#define HWSERVER_ACK_MASK 0x100
176#define HWSERVER_NACK(n) (0x200|(n))
177#define HWSERVER_NACK_MASK 0x200
178
179#define HWSERVER_MAGIC ('h' << 24 | 'w' << 16 | 's' << 8 | 't')
180
181#define HWSERVER_HELLO 0x400
182#define HWSERVER_GET_DEV_LIST 0x401
183#define HWSERVER_DEV_OPEN 0x402
184#define HWSERVER_DEV_CLOSE 0x403
185#define HWSERVER_BYE 0x404
186#define HWSERVER_GET_DESC 0x405
187#define HWSERVER_GET_LOG 0x406
188#define HWSERVER_READ 0x407
189#define HWSERVER_WRITE 0x408
190#define HWSERVER_EXEC 0x409
191
192/* net errors (always in arg[0] if command is NACKed) */
193#define HWERR_OK 0 /* success */
194#define HWERR_FAIL 1 /* general error from hwstub */
195#define HWERR_INVALID_ID 2 /* invalid id of the device */
196#define HWERR_DISCONNECTED 3 /* device got disconnected */
197
198/* read/write flags */
199#define HWSERVER_RW_ATOMIC 0x1
200
201/**********************************
202 * Control Protocol
203 **********************************/
204
205/**
206 * HWSTUB_GET_LOG:
207 * The log is returned as part of the control transfer.
208 */
209
210/**
211 * HWSTUB_READ and HWSTUB_READ2(_ATOMIC):
212 * Read a range of memory. The request works in two steps: first the host
213 * sends HWSTUB_READ with the parameters (address, length) and then
214 * a HWSTUB_READ2 to retrieve the buffer. Both requests must use the same
215 * ID in wValue, otherwise the second request will be STALLed.
216 * HWSTUB_READ2_ATOMIC behaves the same as HWSTUB_READ2 except that the read
217 * is guaranteed to be atomic (ie performed as a single memory access) and
218 * will be STALLed if atomicity can not be ensured.
219 */
220
221struct hwstub_read_req_t
222{
223 uint32_t dAddress;
224} __attribute__((packed));
225
226/**
227 * HWSTUB_WRITE:
228 * Write a range of memory. The payload starts with the following header, everything
229 * which follows is data.
230 * HWSTUB_WRITE_ATOMIC behaves the same except it is atomic. See HWSTUB_READ2_ATOMIC.
231 */
232struct hwstub_write_req_t
233{
234 uint32_t dAddress;
235} __attribute__((packed));
236
237/**
238 * HWSTUB_EXEC:
239 * Execute code at an address. Several options are available regarding ARM vs Thumb,
240 * jump vs call.
241 */
242
243#define HWSTUB_EXEC_ARM (0 << 0) /* target code is ARM */
244#define HWSTUB_EXEC_THUMB (1 << 0) /* target code is Thumb */
245#define HWSTUB_EXEC_JUMP (0 << 1) /* branch, code will never turn */
246#define HWSTUB_EXEC_CALL (1 << 1) /* call and expect return */
247
248struct hwstub_exec_req_t
249{
250 uint32_t dAddress;
251 uint16_t bmFlags;
252} __attribute__((packed));
253
254/**
255 * HWSERVER_HELLO:
256 * Say hello to the server, give protocol version and get server version.
257 * Send: args[0] = major << 8 | minor, no data
258 * Receive: args[0] = major << 8 | minor, no data
259 */
260
261/**
262 * HWSERVER_GET_DEV_LIST:
263 * Get device list.
264 * Send: no argument, no data.
265 * Receive: no argument, data contains a list of device IDs, each ID is a uint32_t
266 * transmitted in network byte order.
267 */
268
269/**
270 * HWSERVER_DEV_OPEN:
271 * Open a device and return a handle.
272 * Send: args[0] = device ID, no data.
273 * Receive: args[0] = handle ID, no data.
274 */
275
276/**
277 * HWSERVER_DEV_CLOSE:
278 * Close a device handle.
279 * Send: args[0] = handle ID, no data.
280 * Receive: no argument, no data.
281 */
282
283/**
284 * HWSERVER_BYE:
285 * Say bye to the server, closing all devices and effectively stopping the communication.
286 * Send: no argument, no data
287 * Receive: no argument, no data
288 */
289
290/**
291 * HWSERVER_GET_DESC:
292 * Query a descriptor.
293 * Send: args[0] = handle ID, args[1] = desc ID, args[2] = requested length, no data
294 * Receive: no argument, data contains RAW descriptor (ie all fields are in little-endian)
295 */
296
297/**
298 * HWSERVER_GET_LOG:
299 * Query a descriptor.
300 * Send: args[0] = handle ID, args[1] = requested length, no data
301 * Receive: no argument, data contains log data
302 */
303
304/**
305 * HWSERVER_READ:
306 * Read data.
307 * Send: args[0] = handle ID, args[1] = addr, args[2] = length, args[3] = flags
308 * Receive: no argument, data read on device
309 */
310
311/**
312 * HWSERVER_WRITE:
313 * Read data.
314 * Send: args[0] = handle ID, args[1] = addr, args[2] = flags, data to write
315 * Receive: no data
316 */
317
318#endif /* __HWSTUB_PROTOCOL__ */
diff --git a/utils/hwstub/include/hwstub_uri.hpp b/utils/hwstub/include/hwstub_uri.hpp
new file mode 100644
index 0000000000..d461764cd9
--- /dev/null
+++ b/utils/hwstub/include/hwstub_uri.hpp
@@ -0,0 +1,131 @@
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_URI_HPP__
22#define __HWSTUB_URI_HPP__
23
24#include "hwstub.hpp"
25#include "hwstub_net.hpp"
26
27namespace hwstub {
28namespace uri {
29
30/** HWSTUB URIs
31 *
32 * They are of the form:
33 *
34 * scheme:[//domain[:port]][/][path[?query]]
35 *
36 * The scheme is mandatory and controls the type of context that is created.
37 * The following scheme are recognized:
38 * usb USB context
39 * tcp TCP context
40 * unix Unix domain context
41 * virt Virtual context (Testing and debugging)
42 * default Default context (This is the default)
43 *
44 * When creating a USB context, the domain and port must be empty:
45 * usb:
46 *
47 * When creating a TCP context, the domain and port are given as argument to
48 * the context:
49 * tcp://localhost:6666
50 *
51 * When creating a Unix context, the domain is given as argument to
52 * the context, it is invalid to specify a port. There are two types of
53 * unix contexts: the one specified by a filesystem path, or (Linux-only) by
54 * an abstract domain. Abstract names are specified as a domain starting with a '#',
55 * whereas standard path can be any path:
56 * unix:///path/to/socket
57 * unix://#hwstub
58 *
59 * When creating a virtual context, the domain will contain a specification of
60 * the device to create. The device list is of the type(param);type(param);...
61 * where the only supported type at the moment is 'dummy' with a single parameter
62 * which is the device name:
63 * virt://dummy(Device A);dummy(Device B);dummy(Super device C)
64 *
65 *
66 * HWSTUB SERVER URIs
67 *
68 * The same scheme can be used to spawn servers. Server URIs are a subset of
69 * context URIs and only support tcp and unix schemes.
70 */
71
72/** URI
73 *
74 * Represents an URI and allows queries on it */
75class uri
76{
77public:
78 uri(const std::string& uri);
79 /** Return whether the URI is syntactically correct */
80 bool valid() const;
81 /** Return error description if URI is invalid */
82 std::string error() const;
83 /** Return the original URI */
84 std::string full_uri() const;
85 /** Return the scheme */
86 std::string scheme() const;
87 /** Return the domain, or empty is none */
88 std::string domain() const;
89 /** Return the port, or empty is none */
90 std::string port() const;
91 /** Return the path, or empty is none */
92 std::string path() const;
93
94protected:
95 void parse();
96 bool validate_scheme();
97 bool validate_domain();
98 bool validate_port();
99
100 std::string m_uri; /* original uri */
101 bool m_valid; /* did it parse correctly ? */
102 std::string m_scheme; /* scheme (extracted from URI) */
103 std::string m_domain; /* domain (extracted from URI) */
104 std::string m_port; /* port (extracted from URI) */
105 std::string m_path; /* path (extracted from URI) */
106 std::string m_error; /* error string (for invalid URIs) */
107};
108
109/** Create a context based on a URI. This function only uses the scheme/domain/port
110 * parts of the URI. This function may fail and return a empty pointer. An optional
111 * string can receive a description of the error */
112std::shared_ptr<context> create_context(const uri& uri, std::string *error = nullptr);
113/** Return a safe default for a URI */
114uri default_uri();
115/** Special case function for the default function */
116std::shared_ptr<context> create_default_context(std::string *error = nullptr);
117/** Create a server based on a URI. This function only uses the scheme/domain/port
118 * parts of the URI. This function may fail and return a empty pointer. An optional
119 * string can receive a description of the error */
120std::shared_ptr<net::server> create_server(std::shared_ptr<context> ctx,
121 const uri& uri, std::string *error);
122/** Return a safe default for a server URI */
123uri default_server_uri();
124/** Print help for the format of a URI, typically for a command-line help.
125 * The help can be client-only, server-only, or both. */
126void print_usage(FILE *f, bool client, bool server);
127
128} // namespace uri
129} // namespace hwstub
130
131#endif /* __HWSTUB_URI_HPP__ */
diff --git a/utils/hwstub/include/hwstub_usb.hpp b/utils/hwstub/include/hwstub_usb.hpp
new file mode 100644
index 0000000000..6a9d4d8798
--- /dev/null
+++ b/utils/hwstub/include/hwstub_usb.hpp
@@ -0,0 +1,194 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2015 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_USB_HPP__
22#define __HWSTUB_USB_HPP__
23
24#include "hwstub_usb.hpp"
25#include <libusb.h>
26
27namespace hwstub {
28namespace usb {
29
30/** USB context
31 *
32 * Context based on libusb. */
33class context : public hwstub::context
34{
35protected:
36 context(libusb_context *ctx, bool cleanup_ctx);
37public:
38 virtual ~context();
39 /** Return native libusb context */
40 libusb_context *native_context();
41 /** Create a USB context. If cleanup_ctx is true, libusb_exit() will be
42 * called on the context on deletion of this class. If ctx is NULL, libusb_init()
43 * will be called with NULL so there is no need to init the default context. */
44 static std::shared_ptr<context> create(libusb_context *ctx, bool cleanup_ctx = false,
45 std::string *error = nullptr);
46
47protected:
48 /* NOTE ctx_dev_t = libusb_device* */
49 libusb_device *from_ctx_dev(ctx_dev_t dev);
50 ctx_dev_t to_ctx_dev(libusb_device *dev);
51 virtual error fetch_device_list(std::vector<ctx_dev_t>& list, void*& ptr);
52 virtual void destroy_device_list(void *ptr);
53 virtual error create_device(ctx_dev_t dev, std::shared_ptr<hwstub::device>& hwdev);
54 virtual bool match_device(ctx_dev_t dev, std::shared_ptr<hwstub::device> hwdev);
55
56 libusb_context *m_usb_ctx; /* libusb context (might be NULL) */
57 bool m_cleanup_ctx; /* cleanup context on delete ? */
58};
59
60/** USB device
61 *
62 * Device based on libusb_device. */
63class device : public hwstub::device
64{
65 friend class context; /* for ctor */
66protected:
67 device(std::shared_ptr<hwstub::context> ctx, libusb_device *dev);
68public:
69 virtual ~device();
70 /** Return native libusb device */
71 libusb_device *native_device();
72 /** Get bus number */
73 uint8_t get_bus_number();
74 /** Get device address */
75 uint8_t get_address();
76 /** Get device VID */
77 uint16_t get_vid();
78 /** Get device PID */
79 uint16_t get_pid();
80
81protected:
82 /** Return true if this might be a hwstub device and should appear in the list */
83 static bool is_hwstub_dev(libusb_device *dev);
84
85 virtual error open_dev(std::shared_ptr<hwstub::handle>& handle);
86 virtual bool has_multiple_open() const;
87
88 libusb_device *m_dev; /* USB device */
89};
90
91/** USB handle
92 *
93 * Handle based on libusb_device_handle. */
94class handle : public hwstub::handle
95{
96protected:
97 handle(std::shared_ptr<hwstub::device> dev, libusb_device_handle *handle);
98public:
99 virtual ~handle();
100 /** set operation timeout */
101 void set_timeout(std::chrono::milliseconds ms);
102
103protected:
104 /* interpret libusb error: >=0 means SUCCESS, others are treated as errors,
105 * LIBUSB_ERROR_NO_DEVICE is treated as DISCONNECTED */
106 error interpret_libusb_error(int err);
107 /* interpret libusb error: <0 returns interpret_libusb_error(err), otherwise
108 * returns SUCCESS if err == expected_value */
109 error interpret_libusb_error(int err, size_t expected_value);
110 /* interpret libusb error: <0 returns interpret_libusb_error(err), otherwise
111 * returns SUCCESS and write size in out_size */
112 error interpret_libusb_size(int err, size_t& out_size);
113
114 libusb_device_handle *m_handle; /* USB handle */
115 unsigned int m_timeout; /* in milliseconds */
116};
117
118/** Rockbox USB handle
119 *
120 * HWSTUB/Rockbox protocol. */
121class rb_handle : public handle
122{
123 friend class device; /* for find_intf() */
124protected:
125 rb_handle(std::shared_ptr<hwstub::device> dev, libusb_device_handle *handle, int intf);
126public:
127 virtual ~rb_handle();
128
129protected:
130 virtual error read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic);
131 virtual error write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic);
132 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
133 virtual error get_dev_log(void *buf, size_t& buf_sz);
134 virtual error exec_dev(uint32_t addr, uint16_t flags);
135 virtual error status() const;
136 virtual size_t get_buffer_size();
137 /* Probe a device to check if it is an hwstub device and return the interface
138 * number, or <0 on error. */
139 static bool find_intf(struct libusb_device_descriptor *dev,
140 struct libusb_config_descriptor *config, int& intf);
141
142 error m_probe_status; /* probing status */
143 int m_intf; /* interface number */
144 uint16_t m_transac_id; /* transaction ID */
145 size_t m_buf_size; /* Device buffer size */
146};
147
148/** JZ USB handle
149 *
150 * JZ boot protocol */
151class jz_handle : public handle
152{
153 friend class device; /* for is_boot_dev() */
154protected:
155 jz_handle(std::shared_ptr<hwstub::device> dev, libusb_device_handle *handle);
156public:
157 virtual ~jz_handle();
158
159protected:
160 virtual error read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic);
161 virtual error write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic);
162 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
163 virtual error get_dev_log(void *buf, size_t& buf_sz);
164 virtual error exec_dev(uint32_t addr, uint16_t flags);
165 virtual error status() const;
166 virtual size_t get_buffer_size();
167 error probe();
168 error probe_jz4760b();
169 error read_reg32(uint32_t addr, uint32_t& value);
170 error write_reg32(uint32_t addr, uint32_t value);
171
172 error jz_cpuinfo(char cpuinfo[8]);
173 error jz_set_addr(uint32_t addr);
174 error jz_set_length(uint32_t size);
175 error jz_upload(void *data, size_t& length);
176 error jz_download(const void *data, size_t& length);
177 error jz_start1(uint32_t addr);
178 error jz_flush_caches();
179 error jz_start2(uint32_t addr);
180 /* Probe a device to check if it is a jz boot device */
181 static bool is_boot_dev(struct libusb_device_descriptor *dev,
182 struct libusb_config_descriptor *config);
183
184 error m_probe_status; /* probing status */
185 struct hwstub_version_desc_t m_desc_version;
186 struct hwstub_layout_desc_t m_desc_layout;
187 struct hwstub_target_desc_t m_desc_target;
188 struct hwstub_jz_desc_t m_desc_jz;
189};
190
191} // namespace usb
192} // namespace hwstub
193
194#endif /* __HWSTUB_USB_HPP__ */
diff --git a/utils/hwstub/include/hwstub_virtual.hpp b/utils/hwstub/include/hwstub_virtual.hpp
new file mode 100644
index 0000000000..d35f98e0ec
--- /dev/null
+++ b/utils/hwstub/include/hwstub_virtual.hpp
@@ -0,0 +1,159 @@
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_VIRTUAL_HPP__
22#define __HWSTUB_VIRTUAL_HPP__
23
24#include "hwstub.hpp"
25#include <libusb.h>
26
27namespace hwstub {
28namespace virt {
29
30class hardware;
31
32/** Virtual context
33 *
34 * A virtual context hosts a number of virtual devices.
35 * This kind of contexts is mostly useful for testing/debugging purposes */
36class context : public hwstub::context
37{
38protected:
39 context();
40public:
41 virtual ~context();
42 /** Create a virtual context. */
43 static std::shared_ptr<context> create();
44 /** To ease creation, the context can be given a specification of the initial
45 * device list using the following format:
46 * dev1;dev2;...
47 * At the moment the only format support for devi is:
48 * dummy(Device name) */
49 static std::shared_ptr<context> create_spec(const std::string& spec, std::string *error = nullptr);
50
51 /** Connect a device to the context. Return false if device is already connected,
52 * and true otherwise. This method is thread-safe. */
53 bool connect(std::shared_ptr<hardware> hw);
54 /** Disconnect a device from the context. Return false if device is not connected,
55 * and true otheriwse. This method is thread-safe. */
56 bool disconnect(std::shared_ptr<hardware> hw);
57
58protected:
59 /* NOTE ctx_dev_t = hardware* */
60 std::shared_ptr<hardware> from_ctx_dev(ctx_dev_t dev);
61 ctx_dev_t to_ctx_dev(std::shared_ptr<hardware>& dev);
62 virtual error fetch_device_list(std::vector<ctx_dev_t>& list, void*& ptr);
63 virtual void destroy_device_list(void *ptr);
64 virtual error create_device(ctx_dev_t dev, std::shared_ptr<device>& hwdev);
65 virtual bool match_device(ctx_dev_t dev, std::shared_ptr<device> hwdev);
66
67 std::vector<std::shared_ptr<hardware>> m_hwlist; /* List of connected hardware */
68};
69
70/** Virtual hardware device (server/provider side)
71 *
72 * This base class represents a virtual piece of hardware that is being accessed
73 * by the context. Users of virtual contexts must inherit from this class and
74 * implement the requests. All requests are guaranteed to be serialize at the device
75 * level so there is no need to care about concurrency issues */
76class hardware : public std::enable_shared_from_this<hardware>
77{
78public:
79 hardware();
80 virtual ~hardware();
81
82 virtual error read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic) = 0;
83 virtual error write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic) = 0;
84 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz) = 0;
85 virtual error get_dev_log(void *buf, size_t& buf_sz) = 0;
86 virtual error exec_dev(uint32_t addr, uint16_t flags) = 0;
87 virtual size_t get_buffer_size() = 0;
88};
89
90/** Dummy implementation of an hardware.
91 *
92 * This dummy hardware will fail all operations except getting descriptors.
93 * The device description can be customised */
94class dummy_hardware : public hardware
95{
96public:
97 dummy_hardware(const std::string& name);
98 virtual ~dummy_hardware();
99
100 virtual error read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic);
101 virtual error write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic);
102 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
103 virtual error get_dev_log(void *buf, size_t& buf_sz);
104 virtual error exec_dev(uint32_t addr, uint16_t flags);
105 virtual size_t get_buffer_size();
106
107protected:
108 struct hwstub_version_desc_t m_desc_version;
109 struct hwstub_layout_desc_t m_desc_layout;
110 struct hwstub_target_desc_t m_desc_target;
111};
112
113/** Virtual device (client/user side)
114 *
115 * Device based on virtual device. */
116class device : public hwstub::device
117{
118 friend class context; /* for ctor */
119protected:
120 device(std::shared_ptr<hwstub::context> ctx, std::shared_ptr<hardware> dev);
121public:
122 virtual ~device();
123 /** Get native device (possibly null) */
124 std::shared_ptr<hardware> native_device();
125
126protected:
127 virtual error open_dev(std::shared_ptr<handle>& handle);
128 virtual bool has_multiple_open() const;
129
130 std::weak_ptr<hardware> m_hwdev; /* pointer to hardware */
131};
132
133/** Virtual handle
134 *
135 * Handle based on virtual device. */
136class handle : public hwstub::handle
137{
138 friend class device; /* for ctor */
139protected:
140 handle(std::shared_ptr<hwstub::device> dev);
141public:
142 virtual ~handle();
143
144protected:
145 virtual error read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic);
146 virtual error write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic);
147 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
148 virtual error get_dev_log(void *buf, size_t& buf_sz);
149 virtual error exec_dev(uint32_t addr, uint16_t flags);
150 virtual error status() const;
151 virtual size_t get_buffer_size();
152
153 std::weak_ptr<hardware> m_hwdev; /* pointer to hardware */
154};
155
156} // namespace virt
157} // namespace hwstub
158
159#endif /* __HWSTUB_VIRTUAL_HPP__ */