summaryrefslogtreecommitdiff
path: root/utils/hwstub/lib
diff options
context:
space:
mode:
Diffstat (limited to 'utils/hwstub/lib')
-rw-r--r--utils/hwstub/lib/Makefile13
-rw-r--r--utils/hwstub/lib/hwstub.cpp627
-rw-r--r--utils/hwstub/lib/hwstub.h70
-rw-r--r--utils/hwstub/lib/hwstub_net.cpp1351
-rw-r--r--utils/hwstub/lib/hwstub_protocol.h1
-rw-r--r--utils/hwstub/lib/hwstub_uri.cpp332
-rw-r--r--utils/hwstub/lib/hwstub_usb.cpp728
-rw-r--r--utils/hwstub/lib/hwstub_virtual.cpp335
8 files changed, 3382 insertions, 75 deletions
diff --git a/utils/hwstub/lib/Makefile b/utils/hwstub/lib/Makefile
index 7c455e4586..92dd358ce3 100644
--- a/utils/hwstub/lib/Makefile
+++ b/utils/hwstub/lib/Makefile
@@ -1,16 +1,21 @@
1CC=gcc
2AR=ar 1AR=ar
3CFLAGS=-W -Wall -O2 `pkg-config --cflags libusb-1.0` -std=c99 -g -fPIC 2INCLUDE=../include
4LDFLAGS=`pkg-config --libs libusb-1.0` -fPIC 3CFLAGS=-W -Wall -O2 `pkg-config --cflags libusb-1.0` -std=c99 -g -fPIC -D_XOPEN_SOURCE=700 -I$(INCLUDE)
4CXXFLAGS=-W -Wall -O2 `pkg-config --cflags libusb-1.0` -std=c++11 -g -fPIC -D_XOPEN_SOURCE=700 -I$(INCLUDE)
5LDFLAGS=`pkg-config --libs libusb-1.0` -fPIC -lpthread
5LIB=libhwstub.a 6LIB=libhwstub.a
6SRC=$(wildcard *.c) 7SRC=$(wildcard *.c)
7OBJ=$(SRC:.c=.o) 8SRCXX=$(wildcard *.cpp)
9OBJ=$(SRCXX:.cpp=.oxx) $(SRCXX:.cpp=.o)
8 10
9all: $(LIB) 11all: $(LIB)
10 12
11%.o: %.c 13%.o: %.c
12 $(CC) $(CFLAGS) -c -o $@ $< 14 $(CC) $(CFLAGS) -c -o $@ $<
13 15
16%.oxx: %.cpp
17 $(CXX) $(CXXFLAGS) -c -o $@ $<
18
14$(LIB): $(OBJ) 19$(LIB): $(OBJ)
15 $(AR) rcs $@ $^ 20 $(AR) rcs $@ $^
16 21
diff --git a/utils/hwstub/lib/hwstub.cpp b/utils/hwstub/lib/hwstub.cpp
new file mode 100644
index 0000000000..7c81146c77
--- /dev/null
+++ b/utils/hwstub/lib/hwstub.cpp
@@ -0,0 +1,627 @@
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#include "hwstub.hpp"
22#include <algorithm>
23#include <cstring>
24
25namespace hwstub {
26
27std::ostream cnull(0);
28std::wostream wcnull(0);
29
30std::string error_string(error err)
31{
32 switch(err)
33 {
34 case error::SUCCESS: return "success";
35 case error::ERROR: return "unspecified error";
36 case error::DISCONNECTED: return "device was disconnected";
37 case error::PROBE_FAILURE: return "probing failed";
38 case error::NO_CONTEXT: return "context was destroyed";
39 case error::USB_ERROR: return "unspecified USB error";
40 case error::DUMMY: return "operation on dummy device";
41 case error::NO_SERVER: return "server could not be reached";
42 case error::SERVER_DISCONNECTED: return "server disconnected";
43 case error::SERVER_MISMATCH: return "incompatible server";
44 case error::NET_ERROR: return "network error";
45 case error::PROTOCOL_ERROR: return "network protocol error";
46 case error::TIMEOUT: return "timeout";
47 case error::OVERFLW: return "overflow";
48 default: return "unknown error";
49 }
50}
51
52/**
53 * Context
54 */
55
56context::context()
57 :m_next_cb_ref(0)
58{
59 clear_debug();
60}
61
62context::~context()
63{
64}
65
66void context::set_debug(std::ostream& os)
67{
68 m_debug = &os;
69}
70
71std::ostream& context::debug()
72{
73 return *m_debug;
74}
75
76error context::get_device_list(std::vector<std::shared_ptr<device>>& list)
77{
78 std::unique_lock<std::recursive_mutex> lock(m_mutex);
79 error err = update_list();
80 if(err != error::SUCCESS)
81 return err;
82 list.resize(m_devlist.size());
83 for(size_t i = 0; i < m_devlist.size(); i++)
84 list[i] = m_devlist[i];
85 return error::SUCCESS;
86}
87
88error context::get_dummy_device(std::shared_ptr<device>& dev)
89{
90 // NOTE: can't use make_shared() because of the protected ctor */
91 dev.reset(new dummy_device(shared_from_this()));
92 return error::SUCCESS;
93}
94
95void context::notify_device(bool arrived, std::shared_ptr<device> dev)
96{
97 for(auto cb : m_callbacks)
98 cb.callback(shared_from_this(), arrived, dev);
99}
100
101void context::change_device(bool arrived, std::shared_ptr<device> dev)
102{
103 std::unique_lock<std::recursive_mutex> lock(m_mutex);
104 if(arrived)
105 {
106 /* add to the list (assumed it's not already there) */
107 m_devlist.push_back(dev);
108 /* notify */
109 notify_device(arrived, dev);
110 }
111 else
112 {
113 dev->disconnect();
114 /* notify first */
115 notify_device(arrived, dev);
116 auto it = std::find(m_devlist.begin(), m_devlist.end(), dev);
117 if(it != m_devlist.end())
118 m_devlist.erase(it);
119 }
120}
121
122error context::update_list()
123{
124 std::unique_lock<std::recursive_mutex> lock(m_mutex);
125 /* fetch new list */
126 std::vector<ctx_dev_t> new_list;
127 void* ptr;
128 error err = fetch_device_list(new_list, ptr);
129 if(err != error::SUCCESS)
130 return err;
131 /* determine devices that have left */
132 std::vector<std::shared_ptr<device>> to_del;
133 for(auto dev : m_devlist)
134 {
135 bool still_there = false;
136 for(auto new_dev : new_list)
137 if(match_device(new_dev, dev))
138 still_there = true;
139 if(!still_there)
140 to_del.push_back(dev);
141 }
142 for(auto dev : to_del)
143 change_device(false, dev);
144 /* determine new devices */
145 std::vector<ctx_dev_t> to_add;
146 for(auto new_dev : new_list)
147 {
148 bool exists = false;
149 for(auto dev : m_devlist)
150 if(match_device(new_dev, dev))
151 exists = true;
152 if(!exists)
153 to_add.push_back(new_dev);
154 }
155 /* create new devices */
156 for(auto dev : to_add)
157 {
158 std::shared_ptr<device> new_dev;
159 err = create_device(dev, new_dev);
160 if(err == error::SUCCESS)
161 change_device(true, new_dev);
162 }
163 /* destroy list */
164 destroy_device_list(ptr);
165 return error::SUCCESS;
166}
167
168context::callback_ref_t context::register_callback(const notification_callback_t& fn)
169{
170 std::unique_lock<std::recursive_mutex> lock(m_mutex);
171 struct callback_t cb;
172 cb.callback = fn;
173 cb.ref = m_next_cb_ref++;
174 m_callbacks.push_back(cb);
175 return cb.ref;
176}
177
178void context::unregister_callback(callback_ref_t ref)
179{
180 std::unique_lock<std::recursive_mutex> lock(m_mutex);
181 for(auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it)
182 {
183 if((*it).ref == ref)
184 {
185 m_callbacks.erase(it);
186 return;
187 }
188 }
189}
190
191void context::start_polling(std::chrono::milliseconds interval)
192{
193 std::unique_lock<std::recursive_mutex> lock(m_mutex);
194 /* create poller on demand */
195 if(!m_poller)
196 m_poller.reset(new context_poller(shared_from_this(), interval));
197 else
198 m_poller->set_interval(interval);
199 m_poller->start();
200}
201
202void context::stop_polling()
203{
204 std::unique_lock<std::recursive_mutex> lock(m_mutex);
205 m_poller->stop();
206}
207
208/**
209 * Context Poller
210 */
211
212context_poller::context_poller(std::weak_ptr<context> ctx, std::chrono::milliseconds interval)
213 :m_ctx(ctx), m_running(false), m_exit(false), m_interval(interval)
214{
215 m_thread = std::thread(context_poller::thread, this);
216}
217
218context_poller::~context_poller()
219{
220 /* set exit flag, wakeup thread, wait for exit */
221 m_mutex.lock();
222 m_exit = true;
223 m_mutex.unlock();
224 m_cond.notify_one();
225 m_thread.join();
226}
227
228void context_poller::set_interval(std::chrono::milliseconds interval)
229{
230 std::unique_lock<std::mutex> lock(m_mutex);
231 /* change interval, wakeup thread to take new interval into account */
232 m_interval = interval;
233 m_cond.notify_one();
234}
235
236void context_poller::start()
237{
238 std::unique_lock<std::mutex> lock(m_mutex);
239 /* change running flag, wakeup thread to start polling */
240 m_running = true;
241 m_cond.notify_one();
242}
243
244void context_poller::stop()
245{
246 std::unique_lock<std::mutex> lock(m_mutex);
247 /* change running flag, wakeup thread to stop polling */
248 m_running = false;
249 m_cond.notify_one();
250}
251
252void context_poller::poll()
253{
254 std::unique_lock<std::mutex> lock(m_mutex);
255 while(true)
256 {
257 /* if asked, exit */
258 if(m_exit)
259 break;
260 /* if running, poll and then sleep for some time */
261 if(m_running)
262 {
263 std::shared_ptr<context> ctx = m_ctx.lock();
264 if(ctx)
265 ctx->update_list();
266 ctx.reset();
267 m_cond.wait_for(lock, m_interval);
268 }
269 /* if not, sleep until awaken */
270 else
271 m_cond.wait(lock);
272 }
273}
274
275void context_poller::thread(context_poller *poller)
276{
277 poller->poll();
278}
279
280/**
281 * Device
282 */
283device::device(std::shared_ptr<context> ctx)
284 :m_ctx(ctx), m_connected(true)
285{
286}
287
288device::~device()
289{
290}
291
292error device::open(std::shared_ptr<handle>& handle)
293{
294 std::unique_lock<std::recursive_mutex> lock(m_mutex);
295 /* get a pointer so that it's not destroyed during the runtime of the function,
296 * the pointer will be released at the end of the function */
297 std::shared_ptr<context> ctx = get_context();
298 if(!ctx)
299 return error::NO_CONTEXT;
300 /* do not even try if device is disconnected */
301 if(!connected())
302 return error::DISCONNECTED;
303 /* NOTE at the moment handle is state-less which means that we can
304 * safely give the same handle each time open() is called without callers
305 * interfering with each other. If handle eventually get a state,
306 * one will need to create a proxy class to encapsulate the state */
307 handle = m_handle.lock();
308 if(has_multiple_open() || !handle)
309 {
310 error err = open_dev(handle);
311 m_handle = handle;
312 return err;
313 }
314 else
315 return error::SUCCESS;
316}
317
318void device::disconnect()
319{
320 std::unique_lock<std::recursive_mutex> lock(m_mutex);
321 m_connected = false;
322}
323
324bool device::connected()
325{
326 return m_connected;
327}
328
329std::shared_ptr<context> device::get_context()
330{
331 return m_ctx.lock();
332}
333
334/**
335 * Handle
336 */
337
338handle::handle(std::shared_ptr<device > dev)
339 :m_dev(dev)
340{
341}
342
343handle::~handle()
344{
345}
346
347std::shared_ptr<device> handle::get_device()
348{
349 return m_dev;
350}
351
352error handle::get_desc(uint16_t desc, void *buf, size_t& buf_sz)
353{
354 std::unique_lock<std::recursive_mutex> lock(m_mutex);
355 /* get a pointer so that it's not destroyed during the runtime of the function,
356 * the pointer will be released at the end of the function */
357 std::shared_ptr<context> ctx = m_dev->get_context();
358 if(!ctx)
359 return error::NO_CONTEXT;
360 /* ensure valid status */
361 error err = status();
362 if(err != error::SUCCESS)
363 return err;
364 return get_dev_desc(desc, buf, buf_sz);
365}
366
367error handle::get_log(void *buf, size_t& buf_sz)
368{
369 std::unique_lock<std::recursive_mutex> lock(m_mutex);
370 /* get a pointer so that it's not destroyed during the runtime of the function,
371 * the pointer will be released at the end of the function */
372 std::shared_ptr<context> ctx = m_dev->get_context();
373 if(!ctx)
374 return error::NO_CONTEXT;
375 /* ensure valid status */
376 error err = status();
377 if(err != error::SUCCESS)
378 return err;
379 return get_dev_log(buf, buf_sz);
380}
381
382error handle::exec(uint32_t addr, uint16_t flags)
383{
384 std::unique_lock<std::recursive_mutex> lock(m_mutex);
385 /* get a pointer so that it's not destroyed during the runtime of the function,
386 * the pointer will be released at the end of the function */
387 std::shared_ptr<context> ctx = m_dev->get_context();
388 if(!ctx)
389 return error::NO_CONTEXT;
390 /* ensure valid status */
391 error err = status();
392 if(err != error::SUCCESS)
393 return err;
394 return exec_dev(addr, flags);
395}
396
397error handle::read(uint32_t addr, void *buf, size_t& sz, bool atomic)
398{
399 std::unique_lock<std::recursive_mutex> lock(m_mutex);
400 /* get a pointer so that it's not destroyed during the runtime of the function,
401 * the pointer will be released at the end of the function */
402 std::shared_ptr<context> ctx = m_dev->get_context();
403 if(!ctx)
404 return error::NO_CONTEXT;
405 /* ensure valid status */
406 error err = status();
407 if(err != error::SUCCESS)
408 return err;
409 /* split transfer as needed */
410 size_t cnt = 0;
411 uint8_t *bufp = (uint8_t *)buf;
412 while(sz > 0)
413 {
414 size_t xfer = std::min(sz, get_buffer_size());
415 err = read_dev(addr, buf, xfer, atomic);
416 if(err != error::SUCCESS)
417 return err;
418 sz -= xfer;
419 bufp += xfer;
420 addr += xfer;
421 cnt += xfer;
422 }
423 sz = cnt;
424 return error::SUCCESS;
425}
426
427error handle::write(uint32_t addr, const void *buf, size_t& sz, bool atomic)
428{
429 std::unique_lock<std::recursive_mutex> lock(m_mutex);
430 /* get a pointer so that it's not destroyed during the runtime of the function,
431 * the pointer will be released at the end of the function */
432 std::shared_ptr<context> ctx = m_dev->get_context();
433 if(!ctx)
434 return error::NO_CONTEXT;
435 /* ensure valid status */
436 error err = status();
437 if(err != error::SUCCESS)
438 return err;
439 /* split transfer as needed */
440 size_t cnt = 0;
441 const uint8_t *bufp = (uint8_t *)buf;
442 while(sz > 0)
443 {
444 size_t xfer = std::min(sz, get_buffer_size());
445 err = write_dev(addr, buf, xfer, atomic);
446 if(err != error::SUCCESS)
447 return err;
448 sz -= xfer;
449 bufp += xfer;
450 addr += xfer;
451 cnt += xfer;
452 }
453 sz = cnt;
454 return error::SUCCESS;
455}
456
457error handle::status() const
458{
459 /* check context */
460 if(!m_dev->get_context())
461 return error::NO_CONTEXT;
462 if(!m_dev->connected())
463 return error::DISCONNECTED;
464 else
465 return error::SUCCESS;
466}
467
468namespace
469{
470 template<typename T>
471 error helper_get_desc(handle *h, uint8_t type, T& desc)
472 {
473 size_t sz = sizeof(desc);
474 error ret = h->get_desc(type, &desc, sz);
475 if(ret != error::SUCCESS)
476 return ret;
477 if(sz != sizeof(desc) || desc.bDescriptorType != type ||
478 desc.bLength != sizeof(desc))
479 return error::ERROR;
480 else
481 return error::SUCCESS;
482 }
483}
484
485error handle::get_version_desc(hwstub_version_desc_t& desc)
486{
487 return helper_get_desc(this, HWSTUB_DT_VERSION, desc);
488}
489
490error handle::get_layout_desc(hwstub_layout_desc_t& desc)
491{
492 return helper_get_desc(this, HWSTUB_DT_LAYOUT, desc);
493}
494
495error handle::get_stmp_desc(hwstub_stmp_desc_t& desc)
496{
497 return helper_get_desc(this, HWSTUB_DT_STMP, desc);
498}
499
500error handle::get_pp_desc(hwstub_pp_desc_t& desc)
501{
502 return helper_get_desc(this, HWSTUB_DT_PP, desc);
503}
504
505error handle::get_jz_desc(hwstub_jz_desc_t& desc)
506{
507 return helper_get_desc(this, HWSTUB_DT_JZ, desc);
508}
509
510error handle::get_target_desc(hwstub_target_desc_t& desc)
511{
512 return helper_get_desc(this, HWSTUB_DT_TARGET, desc);
513}
514
515/** Dummy device */
516dummy_device::dummy_device(std::shared_ptr<context> ctx)
517 :device(ctx)
518{
519}
520
521dummy_device::~dummy_device()
522{
523}
524
525error dummy_device::open_dev(std::shared_ptr<handle>& handle)
526{
527 handle.reset(new dummy_handle(shared_from_this()));
528 return error::SUCCESS;
529}
530
531bool dummy_device::has_multiple_open() const
532{
533 return true;
534}
535
536/** Dummy handle */
537dummy_handle::dummy_handle(std::shared_ptr<device> dev)
538 :handle(dev)
539{
540 m_desc_version.bLength = sizeof(m_desc_version);
541 m_desc_version.bDescriptorType = HWSTUB_DT_VERSION;
542 m_desc_version.bMajor = HWSTUB_VERSION_MAJOR;
543 m_desc_version.bMinor = HWSTUB_VERSION_MINOR;
544 m_desc_version.bRevision = 0;
545
546 m_desc_layout.bLength = sizeof(m_desc_layout);
547 m_desc_layout.bDescriptorType = HWSTUB_DT_LAYOUT;
548 m_desc_layout.dCodeStart = 0;
549 m_desc_layout.dCodeSize = 0;
550 m_desc_layout.dStackStart = 0;
551 m_desc_layout.dStackSize = 0;
552 m_desc_layout.dBufferStart = 0;
553 m_desc_layout.dBufferSize = 1;
554
555 m_desc_target.bLength = sizeof(m_desc_target);
556 m_desc_target.bDescriptorType = HWSTUB_DT_TARGET;
557 m_desc_target.dID = HWSTUB_TARGET_UNK;
558 strcpy(m_desc_target.bName, "Dummy target");
559}
560
561dummy_handle::~dummy_handle()
562{
563}
564
565error dummy_handle::read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic)
566{
567 (void) addr;
568 (void) buf;
569 (void) sz;
570 (void) atomic;
571 return error::DUMMY;
572}
573
574error dummy_handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic)
575{
576 (void) addr;
577 (void) buf;
578 (void) sz;
579 (void) atomic;
580 return error::DUMMY;
581}
582
583error dummy_handle::get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz)
584{
585 void *p = nullptr;
586 switch(desc)
587 {
588 case HWSTUB_DT_VERSION: p = &m_desc_version; break;
589 case HWSTUB_DT_LAYOUT: p = &m_desc_layout; break;
590 case HWSTUB_DT_TARGET: p = &m_desc_target; break;
591 default: break;
592 }
593 if(p == nullptr)
594 return error::ERROR;
595 /* size is in the bLength field of the descriptor */
596 size_t desc_sz = *(uint8_t *)p;
597 buf_sz = std::min(buf_sz, desc_sz);
598 memcpy(buf, p, buf_sz);
599 return error::SUCCESS;
600}
601
602error dummy_handle::get_dev_log(void *buf, size_t& buf_sz)
603{
604 (void) buf;
605 (void) buf_sz;
606 return error::DUMMY;
607}
608
609error dummy_handle::exec_dev(uint32_t addr, uint16_t flags)
610{
611 (void) addr;
612 (void) flags;
613 return error::DUMMY;
614}
615
616error dummy_handle::status() const
617{
618 error err = handle::status();
619 return err == error::SUCCESS ? error::DUMMY : err;
620}
621
622size_t dummy_handle::get_buffer_size()
623{
624 return 1;
625}
626
627} // namespace hwstub
diff --git a/utils/hwstub/lib/hwstub.h b/utils/hwstub/lib/hwstub.h
deleted file mode 100644
index 4d12de8eda..0000000000
--- a/utils/hwstub/lib/hwstub.h
+++ /dev/null
@@ -1,70 +0,0 @@
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/lib/hwstub_net.cpp b/utils/hwstub/lib/hwstub_net.cpp
new file mode 100644
index 0000000000..f561a1004b
--- /dev/null
+++ b/utils/hwstub/lib/hwstub_net.cpp
@@ -0,0 +1,1351 @@
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#include "hwstub_net.hpp"
22#include <sys/socket.h>
23#include <sys/un.h>
24#include <unistd.h>
25#include <cstddef>
26#include <arpa/inet.h>
27#include <sys/types.h>
28#include <netdb.h>
29
30namespace hwstub {
31namespace net {
32
33/**
34 * Context
35 */
36context::context()
37 :m_state(state::HELLO), m_error(error::SUCCESS)
38{
39}
40
41context::~context()
42{
43}
44
45std::shared_ptr<context> context::create_socket(int socket_fd)
46{
47 // NOTE: can't use make_shared() because of the protected ctor */
48 return std::shared_ptr<socket_context>(new socket_context(socket_fd));
49}
50
51std::string context::default_unix_path()
52{
53 return "hwstub";
54}
55
56std::string context::default_tcp_domain()
57{
58 return "localhost";
59}
60
61std::string context::default_tcp_port()
62{
63 return "6666";
64}
65
66namespace
67{
68 /* len is the total length, including a 0 character if any */
69 int create_unix_low(bool abstract, const char *path, size_t len, bool conn,
70 std::string *error)
71 {
72 struct sockaddr_un address;
73 if(len > sizeof(address.sun_path))
74 {
75 if(error)
76 *error = "unix path is too long";
77 return -1;
78 }
79 int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
80 if(socket_fd < 0)
81 {
82 if(error)
83 *error = "socket() failed";
84 return -1;
85 }
86 memset(&address, 0, sizeof(struct sockaddr_un));
87 address.sun_family = AF_UNIX;
88 /* NOTE memcpy, we don't want to add a extra 0 at the end */
89 memcpy(address.sun_path, path, len);
90 /* for abstract name, replace first character by 0 */
91 if(abstract)
92 address.sun_path[0] = 0;
93 /* NOTE sun_path is the last field of the structure */
94 size_t sz = offsetof(struct sockaddr_un, sun_path) + len;
95 /* NOTE don't give sizeof(address) because for abstract names it would contain
96 * extra garbage */
97 if(conn)
98 {
99 if(connect(socket_fd, (struct sockaddr *)&address, sz) != 0)
100 {
101 close(socket_fd);
102 if(error)
103 *error = "connect() failed";
104 return -1;
105 }
106 else
107 return socket_fd;
108 }
109 else
110 {
111 if(bind(socket_fd, (struct sockaddr *)&address, sz) != 0)
112 {
113 close(socket_fd);
114 if(error)
115 *error = "bind() failed";
116 return -1;
117 }
118 else
119 return socket_fd;
120 }
121 }
122
123 int create_tcp_low(const std::string& domain, const std::string& _port, bool server, std::string *error)
124 {
125 std::string port = _port.size() != 0 ? _port : context::default_tcp_port();
126 int socket_fd = -1;
127 struct addrinfo hints;
128 memset(&hints, 0, sizeof(struct addrinfo));
129 hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */
130 hints.ai_socktype = SOCK_STREAM;
131 hints.ai_flags = 0;
132 hints.ai_protocol = 0; /* any protocol */
133
134 struct addrinfo *result;
135 int err = getaddrinfo(domain.c_str(), port.c_str(), &hints, &result);
136 if(err != 0)
137 {
138 if(error)
139 *error = std::string("getaddrinfo failed: ") + gai_strerror(err);
140 return -1;
141 }
142
143 /* getaddrinfo() returns a list of address structures.
144 * Try each address until we successfully connect(2).
145 * If socket(2) (or connect(2)/bind(2)) fails, we (close the socket
146 * and) try the next address. */
147 for(struct addrinfo *rp = result; rp != nullptr; rp = rp->ai_next)
148 {
149 socket_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
150 if(socket_fd == -1)
151 continue;
152
153 int err = 0;
154 if(server)
155 err = bind(socket_fd, rp->ai_addr, rp->ai_addrlen);
156 else
157 err = connect(socket_fd, rp->ai_addr, rp->ai_addrlen);
158 if(err < 0)
159 {
160 close(socket_fd);
161 socket_fd = -1;
162 }
163 else
164 break; /* success */
165 }
166 /* no address was tried */
167 if(socket_fd < 0 && error)
168 *error = "getaddrinfo() returned no usable result (socket()/connect()/bind() failed)";
169 return socket_fd;
170 }
171}
172
173std::shared_ptr<context> context::create_tcp(const std::string& domain,
174 const std::string& port, std::string *error)
175{
176 int fd = create_tcp_low(domain, port, false, error);
177 if(fd >= 0)
178 return context::create_socket(fd);
179 else
180 return std::shared_ptr<context>();
181}
182
183std::shared_ptr<context> context::create_unix(const std::string& path, std::string *error)
184{
185 int fd = create_unix_low(false, path.c_str(), path.size() + 1, true, error);
186 if(fd >= 0)
187 return context::create_socket(fd);
188 else
189 return std::shared_ptr<context>();
190}
191
192std::shared_ptr<context> context::create_unix_abstract(const std::string& path, std::string *error)
193{
194 std::string fake_path = "#" + path; /* the # will be overriden by 0 */
195 int fd = create_unix_low(true, fake_path.c_str(), fake_path.size(), true, error);
196 if(fd >= 0)
197 return context::create_socket(fd);
198 else
199 return std::shared_ptr<context>();
200}
201
202uint32_t context::from_ctx_dev(ctx_dev_t dev)
203{
204 return (uint32_t)(uintptr_t)dev; /* NOTE safe because it was originally a 32-bit int */
205}
206
207hwstub::context::ctx_dev_t context::to_ctx_dev(uint32_t dev)
208{
209 return (ctx_dev_t)(uintptr_t)dev; /* NOTE assume that sizeof(void *)>=sizeof(uint32_t) */
210}
211
212error context::fetch_device_list(std::vector<ctx_dev_t>& list, void*& ptr)
213{
214 (void) ptr;
215 delayed_init();
216 if(m_state == state::DEAD)
217 return m_error;
218 uint32_t args[HWSTUB_NET_ARGS] = {0};
219 uint8_t *data = nullptr;
220 size_t data_sz = 0;
221 debug() << "[net::ctx] --> GET_DEV_LIST\n";
222 error err = send_cmd(HWSERVER_GET_DEV_LIST, args, nullptr, 0, &data, &data_sz);
223 debug() << "[net::ctx] <-- GET_DEV_LIST ";
224 if(err != error::SUCCESS)
225 {
226 debug() << "failed: " << error_string(err) << "\n";
227 return err;
228 }
229 /* sanity check on size */
230 if(data_sz % 4)
231 {
232 debug() << "failed: invalid list size\n";
233 delete[] data;
234 return error::PROTOCOL_ERROR;
235 }
236 debug() << "\n";
237 list.clear();
238 /* each entry is a 32-bit ID in network order size */
239 uint32_t *data_list = (uint32_t *)data;
240 for(size_t i = 0; i < data_sz / 4; i++)
241 list.push_back(to_ctx_dev(from_net_order(data_list[i])));
242 delete[] data;
243 return error::SUCCESS;
244}
245
246void context::destroy_device_list(void *ptr)
247{
248 (void)ptr;
249}
250
251error context::create_device(ctx_dev_t dev, std::shared_ptr<hwstub::device>& hwdev)
252{
253 // NOTE: can't use make_shared() because of the protected ctor */
254 hwdev.reset(new device(shared_from_this(), from_ctx_dev(dev)));
255 return error::SUCCESS;
256}
257
258bool context::match_device(ctx_dev_t dev, std::shared_ptr<hwstub::device> hwdev)
259{
260 device *udev = dynamic_cast<device*>(hwdev.get());
261 return udev != nullptr && udev->device_id() == from_ctx_dev(dev);
262}
263
264uint32_t context::to_net_order(uint32_t u)
265{
266 return htonl(u);
267}
268
269uint32_t context::from_net_order(uint32_t u)
270{
271 return ntohl(u);
272}
273
274error context::send_cmd(uint32_t cmd, uint32_t args[HWSTUB_NET_ARGS], uint8_t *send_data,
275 size_t send_size, uint8_t **recv_data, size_t *recv_size)
276{
277 /* make sure with have the lock, this function might be called concurrently
278 * by the different threads */
279 std::unique_lock<std::recursive_mutex> lock(m_mutex);
280
281 if(m_state == state::DEAD)
282 return m_error;
283 /* do a delayed init, unless with are doing a HELLO */
284 if(m_state == state::HELLO && cmd != HWSERVER_HELLO)
285 delayed_init();
286 /* build header */
287 struct hwstub_net_hdr_t hdr;
288 hdr.magic = to_net_order(HWSERVER_MAGIC);
289 hdr.cmd = to_net_order(cmd);
290 for(size_t i = 0; i < HWSTUB_NET_ARGS; i++)
291 hdr.args[i] = to_net_order(args[i]);
292 hdr.length = to_net_order((uint32_t)send_size);
293 /* send header */
294 size_t sz = sizeof(hdr);
295 error err = send((void *)&hdr, sz);
296 if(err != error::SUCCESS)
297 {
298 m_state = state::DEAD;
299 m_error = err;
300 return err;
301 }
302 if(sz != sizeof(hdr))
303 {
304 m_state = state::DEAD;
305 m_error = error::PROTOCOL_ERROR;
306 }
307 /* send data */
308 if(send_size > 0)
309 {
310 sz = send_size;
311 err = send((void *)send_data, sz);
312 if(err != error::SUCCESS)
313 {
314 m_state = state::DEAD;
315 m_error = err;
316 return err;
317 }
318 if(sz != send_size)
319 {
320 m_state = state::DEAD;
321 m_error = error::PROTOCOL_ERROR;
322 }
323 }
324 /* receive header */
325 sz = sizeof(hdr);
326 err = recv((void *)&hdr, sz);
327 if(err != error::SUCCESS)
328 {
329 m_state = state::DEAD;
330 m_error = err;
331 return err;
332 }
333 if(sz != sizeof(hdr))
334 {
335 m_state = state::DEAD;
336 m_error = error::PROTOCOL_ERROR;
337 return m_error;
338 }
339 /* correct byte order */
340 hdr.magic = from_net_order(hdr.magic);
341 hdr.cmd = from_net_order(hdr.cmd);
342 hdr.length = from_net_order(hdr.length);
343 /* copy arguments */
344 for(size_t i = 0; i < HWSTUB_NET_ARGS; i++)
345 args[i] = from_net_order(hdr.args[i]);
346 /* check header */
347 if(hdr.magic != HWSERVER_MAGIC)
348 {
349 m_state = state::DEAD;
350 m_error = error::PROTOCOL_ERROR;
351 return m_error;
352 }
353 /* check NACK */
354 if(hdr.cmd == HWSERVER_NACK(cmd))
355 {
356 /* translate error */
357 switch(args[0])
358 {
359 case HWERR_FAIL: err = error::ERROR; break;
360 case HWERR_INVALID_ID: err = error::ERROR; break; /* should not happen */
361 case HWERR_DISCONNECTED: err = error::DISCONNECTED; break;
362 }
363 return err;
364 }
365 /* check not ACK */
366 if(hdr.cmd != HWSERVER_ACK(cmd))
367 {
368 m_state = state::DEAD;
369 m_error = error::PROTOCOL_ERROR;
370 return m_error;
371 }
372 /* receive additional data */
373 uint8_t *data = nullptr;
374 if(hdr.length > 0)
375 {
376 data = new uint8_t[hdr.length];
377 sz = hdr.length;
378 err = recv((void *)data, sz);
379 if(err != error::SUCCESS)
380 {
381 m_state = state::DEAD;
382 m_error = err;
383 return err;
384 }
385 if(sz != hdr.length)
386 {
387 m_state = state::DEAD;
388 m_error = error::PROTOCOL_ERROR;
389 return m_error;
390 }
391 }
392 /* copy data if user want it */
393 if(recv_data)
394 {
395 if(*recv_data == nullptr)
396 {
397 *recv_data = data;
398 *recv_size = hdr.length;
399 }
400 else if(*recv_size < hdr.length)
401 {
402 delete[] data;
403 return error::OVERFLW;
404 }
405 else
406 {
407 *recv_size = hdr.length;
408 memcpy(*recv_data, data, *recv_size);
409 delete[] data;
410 }
411 }
412 /* throw it away otherwise */
413 else
414 {
415 delete[] data;
416 }
417 return error::SUCCESS;
418}
419
420void context::delayed_init()
421{
422 /* only do HELLO if we haven't do it yet */
423 if(m_state != state::HELLO)
424 return;
425 debug() << "[net::ctx] --> HELLO " << HWSTUB_VERSION_MAJOR << "."
426 << HWSTUB_VERSION_MINOR << "\n";
427 /* send HELLO with our version and see what the server is up to */
428 uint32_t args[HWSTUB_NET_ARGS] = {0};
429 args[0] = HWSTUB_VERSION_MAJOR << 8 | HWSTUB_VERSION_MINOR;
430 error err = send_cmd(HWSERVER_HELLO, args, nullptr, 0, nullptr, nullptr);
431 if(err != error::SUCCESS)
432 {
433 debug() << "[net::ctx] <-- HELLO failed: " << error_string(err) << "\n";
434 m_state = state::DEAD;
435 m_error = err;
436 return;
437 }
438 /* check the server is running the same version */
439 debug() << "[net::ctx] <-- HELLO " << ((args[0] & 0xff00) >> 8) << "." << (args[0] & 0xff) << "";
440 if(args[0] != (HWSTUB_VERSION_MAJOR << 8 | HWSTUB_VERSION_MINOR))
441 {
442 debug() << " (mismatch)\n";
443 m_state = state::DEAD;
444 m_error = error::SERVER_MISMATCH;
445 }
446 debug() << " (good)\n";
447 /* good, we can now send commands */
448 m_state = state::IDLE;
449}
450
451void context::stop_context()
452{
453 /* make sure with have the lock, this function might be call asynchronously */
454 std::unique_lock<std::recursive_mutex> lock(m_mutex);
455 /* if dead, don't do anything */
456 if(m_state == state::DEAD)
457 return;
458 /* only send BYE if we are initialized */
459 if(m_state == state::IDLE)
460 {
461 debug() << "[net::ctx] --> BYE\n";
462 /* send BYE */
463 uint32_t args[HWSTUB_NET_ARGS] = {0};
464 error err = send_cmd(HWSERVER_BYE, args, nullptr, 0, nullptr, nullptr);
465 if(err != error::SUCCESS)
466 {
467 debug() << "[net::ctx] <-- BYE failed: " << error_string(err) << "\n";
468 m_state = state::DEAD;
469 m_error = err;
470 return;
471 }
472 debug() << "[net::ctx] <-- BYE\n";
473 }
474 /* now we are dead */
475 m_state = state::DEAD;
476 m_error = error::SERVER_DISCONNECTED;
477}
478
479/**
480 * Socket context
481 */
482socket_context::socket_context(int socket_fd)
483 :m_socketfd(socket_fd)
484{
485 set_timeout(std::chrono::milliseconds(1000));
486}
487
488socket_context::~socket_context()
489{
490 stop_context();
491 close(m_socketfd);
492}
493
494void socket_context::set_timeout(std::chrono::milliseconds ms)
495{
496 struct timeval tv;
497 tv.tv_usec = 1000 * (ms.count() % 1000);
498 tv.tv_sec = ms.count() / 1000;
499 /* set timeout for the client operations */
500 setsockopt(m_socketfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
501 setsockopt(m_socketfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
502}
503
504error socket_context::send(void *buffer, size_t& sz)
505{
506 debug() << "[net::ctx::sock] send(" << sz << "): ";
507 int ret = ::send(m_socketfd, buffer, sz, MSG_NOSIGNAL);
508 if(ret >= 0)
509 {
510 debug() << "good(" << ret << ")\n";
511 sz = (size_t)ret;
512 return error::SUCCESS;
513 }
514 /* convert some errors */
515 debug() << "fail(" << errno << "," << strerror(errno) << ")\n";
516 switch(errno)
517 {
518#if EAGAIN != EWOULDBLOCK
519 case EAGAIN:
520#endif
521 case EWOULDBLOCK: return error::TIMEOUT;
522 case ECONNRESET: case EPIPE: return error::SERVER_DISCONNECTED;
523 default: return error::NET_ERROR;
524 }
525}
526
527error socket_context::recv(void *buffer, size_t& sz)
528{
529 debug() << "[net::ctx::sock] recv(" << sz << "): ";
530 int ret = ::recv(m_socketfd, buffer, sz, MSG_WAITALL);
531 if(ret > 0)
532 {
533 debug() << "good(" << ret << ")\n";
534 sz = (size_t)ret;
535 return error::SUCCESS;
536 }
537 if(ret == 0)
538 {
539 debug() << "disconnected\n";
540 return error::SERVER_DISCONNECTED;
541 }
542 debug() << "fail(" << errno << "," << strerror(errno) << ")\n";
543 switch(errno)
544 {
545#if EAGAIN != EWOULDBLOCK
546 case EAGAIN:
547#endif
548 case EWOULDBLOCK: return error::TIMEOUT;
549 default: return error::NET_ERROR;
550 }
551}
552
553/**
554 * Device
555 */
556device::device(std::shared_ptr<hwstub::context> ctx, uint32_t devid)
557 :hwstub::device(ctx), m_device_id(devid)
558{
559}
560
561device::~device()
562{
563}
564
565uint32_t device::device_id()
566{
567 return m_device_id;
568}
569
570error device::open_dev(std::shared_ptr<hwstub::handle>& handle)
571{
572 std::shared_ptr<hwstub::context> hctx = get_context();
573 if(!hctx)
574 return error::NO_CONTEXT;
575 context *ctx = dynamic_cast<context*>(hctx.get());
576 ctx->debug() << "[net::dev] --> DEV_OPEN(" << m_device_id << ")\n";
577 /* ask the server to open the device, note that the device ID may not exists
578 * anymore */
579 uint32_t args[HWSTUB_NET_ARGS] = {0};
580 args[0] = m_device_id;
581 error err = ctx->send_cmd(HWSERVER_DEV_OPEN, args, nullptr, 0, nullptr, nullptr);
582 if(err != error::SUCCESS)
583 {
584 ctx->debug() << "[net::ctx::dev] <-- DEV_OPEN failed: " << error_string(err) << "\n";
585 return err;
586 }
587 ctx->debug() << "[net::ctx::dev] <-- DEV_OPEN: handle = " << args[0] << "\n";
588 // NOTE: can't use make_shared() because of the protected ctor */
589 handle.reset(new hwstub::net::handle(shared_from_this(), args[0]));
590 return error::SUCCESS;
591}
592
593bool device::has_multiple_open() const
594{
595 return false;
596}
597
598/**
599 * Handle
600 */
601handle::handle(std::shared_ptr<hwstub::device> dev, uint32_t hid)
602 :hwstub::handle(dev), m_handle_id(hid)
603{
604}
605
606handle::~handle()
607{
608 /* try to close the handle, if context is still accessible */
609 std::shared_ptr<hwstub::context> hctx = get_device()->get_context();
610 if(hctx)
611 {
612 context *ctx = dynamic_cast<context*>(hctx.get());
613 ctx->debug() << "[net::handle] --> DEV_CLOSE(" << m_handle_id << ")\n";
614 uint32_t args[HWSTUB_NET_ARGS] = {0};
615 args[0] = m_handle_id;
616 error err = ctx->send_cmd(HWSERVER_DEV_CLOSE, args, nullptr, 0, nullptr, nullptr);
617 if(err != error::SUCCESS)
618 ctx->debug() << "[net::handle] <-- DEV_CLOSE failed: " << error_string(err) << "\n";
619 else
620 ctx->debug() << "[net::handle] <-- DEV_CLOSE\n";
621 }
622}
623
624error handle::read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic)
625{
626 std::shared_ptr<hwstub::context> hctx = get_device()->get_context();
627 if(!hctx)
628 return error::NO_CONTEXT;
629
630 context *ctx = dynamic_cast<context*>(hctx.get());
631 ctx->debug() << "[net::handle] --> READ(" << m_handle_id << ",0x" << std::hex
632 << addr << "," << sz << "," << atomic << ")\n";
633 uint32_t args[HWSTUB_NET_ARGS] = {0};
634 args[0] = m_handle_id;
635 args[1] = addr;
636 args[2] = sz;
637 args[3] = atomic ? HWSERVER_RW_ATOMIC : 0;
638 error err = ctx->send_cmd(HWSERVER_READ, args, nullptr, 0, (uint8_t **)&buf, &sz);
639 if(err != error::SUCCESS)
640 {
641 ctx->debug() << "[net::handle] <-- READ failed: " << error_string(err) << "\n";
642 return err;
643 }
644 ctx->debug() << "[net::handle] <-- READ\n";
645 return error::SUCCESS;
646}
647
648error handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic)
649{
650 std::shared_ptr<hwstub::context> hctx = get_device()->get_context();
651 if(!hctx)
652 return error::NO_CONTEXT;
653
654 context *ctx = dynamic_cast<context*>(hctx.get());
655 ctx->debug() << "[net::handle] --> WRITE(" << m_handle_id << ",0x" << std::hex
656 << addr << "," << sz << "," << atomic << ")\n";
657 uint32_t args[HWSTUB_NET_ARGS] = {0};
658 args[0] = m_handle_id;
659 args[1] = addr;
660 args[2] = atomic ? HWSERVER_RW_ATOMIC : 0;
661 error err = ctx->send_cmd(HWSERVER_WRITE, args, (uint8_t *)buf, sz, nullptr, nullptr);
662 if(err != error::SUCCESS)
663 {
664 ctx->debug() << "[net::handle] <-- WRITE failed: " << error_string(err) << "\n";
665 return err;
666 }
667 ctx->debug() << "[net::handle] <-- WRITE\n";
668 return error::SUCCESS;
669}
670
671error handle::get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz)
672{
673 std::shared_ptr<hwstub::context> hctx = get_device()->get_context();
674 if(!hctx)
675 return error::NO_CONTEXT;
676
677 context *ctx = dynamic_cast<context*>(hctx.get());
678 ctx->debug() << "[net::handle] --> GET_DESC(" << m_handle_id << ",0x" << std::hex
679 << desc << "," << buf_sz << ")\n";
680 uint32_t args[HWSTUB_NET_ARGS] = {0};
681 args[0] = m_handle_id;
682 args[1] = desc;
683 args[2] = buf_sz;
684 error err = ctx->send_cmd(HWSERVER_GET_DESC, args, nullptr, 0, (uint8_t **)&buf, &buf_sz);
685 if(err != error::SUCCESS)
686 {
687 ctx->debug() << "[net::handle] <-- GET_DESC failed: " << error_string(err) << "\n";
688 return err;
689 }
690 ctx->debug() << "[net::handle] <-- GET_DESC\n";
691 return error::SUCCESS;
692}
693
694error handle::get_dev_log(void *buf, size_t& buf_sz)
695{
696 std::shared_ptr<hwstub::context> hctx = get_device()->get_context();
697 if(!hctx)
698 return error::NO_CONTEXT;
699
700 context *ctx = dynamic_cast<context*>(hctx.get());
701 ctx->debug() << "[net::handle] --> GET_LOG(" << buf_sz << ")\n";
702 uint32_t args[HWSTUB_NET_ARGS] = {0};
703 args[0] = m_handle_id;
704 args[1] = buf_sz;
705 error err = ctx->send_cmd(HWSERVER_GET_LOG, args, nullptr, 0, (uint8_t **)&buf, &buf_sz);
706 if(err != error::SUCCESS)
707 {
708 ctx->debug() << "[net::handle] <-- GET_LOG failed: " << error_string(err) << "\n";
709 return err;
710 }
711 ctx->debug() << "[net::handle] <-- GET_LOG\n";
712 return error::SUCCESS;
713}
714
715error handle::exec_dev(uint32_t addr, uint16_t flags)
716{
717 (void) addr;
718 (void) flags;
719 return error::DUMMY;
720}
721
722error handle::status() const
723{
724 return hwstub::handle::status();
725}
726
727size_t handle::get_buffer_size()
728{
729 return 2048;
730}
731
732/**
733 * Server
734 */
735server::server(std::shared_ptr<hwstub::context> ctx)
736 :m_context(ctx)
737{
738 clear_debug();
739}
740
741server::~server()
742{
743}
744
745void server::stop_server()
746{
747 std::unique_lock<std::recursive_mutex> lock(m_mutex);
748 /* ask all client threads to stop */
749 for(auto& cl : m_client)
750 cl.exit = true;
751 /* wait for each thread to stop */
752 for(auto& cl : m_client)
753 cl.future.wait();
754}
755
756std::shared_ptr<server> server::create_unix(std::shared_ptr<hwstub::context> ctx,
757 const std::string& path, std::string *error)
758{
759 int fd = create_unix_low(false, path.c_str(), path.size() + 1, false, error);
760 if(fd >= 0)
761 return socket_server::create_socket(ctx, fd);
762 else
763 return std::shared_ptr<server>();
764}
765
766std::shared_ptr<server> server::create_unix_abstract(std::shared_ptr<hwstub::context> ctx,
767 const std::string& path, std::string *error)
768{
769 std::string fake_path = "#" + path; /* the # will be overriden by 0 */
770 int fd = create_unix_low(true, fake_path.c_str(), fake_path.size(), false, error);
771 if(fd >= 0)
772 return socket_server::create_socket(ctx, fd);
773 else
774 return std::shared_ptr<server>();
775}
776
777std::shared_ptr<server> server::create_socket(std::shared_ptr<hwstub::context> ctx,
778 int socket_fd)
779{
780 return socket_server::create(ctx, socket_fd);
781}
782
783std::shared_ptr<server> server::create_tcp(std::shared_ptr<hwstub::context> ctx,
784 const std::string& domain, const std::string& port, std::string *error)
785{
786 int fd = create_tcp_low(domain, port, true, error);
787 if(fd >= 0)
788 return socket_server::create_socket(ctx, fd);
789 else
790 return std::shared_ptr<server>();
791}
792
793void server::set_debug(std::ostream& os)
794{
795 m_debug = &os;
796}
797
798std::ostream& server::debug()
799{
800 return *m_debug;
801}
802
803server::client_state::client_state(srv_client_t cl, std::future<void>&& f)
804 :client(cl), future(std::move(f)), exit(false), next_dev_id(42),
805 next_handle_id(19)
806{
807}
808
809void server::client_thread2(server *s, client_state *cs)
810{
811 s->client_thread(cs);
812}
813
814uint32_t server::to_net_order(uint32_t u)
815{
816 return htonl(u);
817}
818
819uint32_t server::from_net_order(uint32_t u)
820{
821 return ntohl(u);
822}
823
824void server::client_thread(client_state *state)
825{
826 debug() << "[net::srv::client] start: " << state->client << "\n";
827 while(!state->exit)
828 {
829 /* wait for some header */
830 struct hwstub_net_hdr_t hdr;
831 size_t sz = sizeof(hdr);
832 error err;
833 /* wait for some command, or exit flag */
834 do
835 err = recv(state->client, (void *)&hdr, sz);
836 while(err == error::TIMEOUT && !state->exit);
837 if(state->exit || err != error::SUCCESS || sz != sizeof(hdr))
838 break;
839 /* convert to host order */
840 hdr.magic = from_net_order(hdr.magic);
841 hdr.cmd = from_net_order(hdr.cmd);
842 hdr.length = from_net_order(hdr.length);
843 /* copy arguments */
844 for(size_t i = 0; i < HWSTUB_NET_ARGS; i++)
845 hdr.args[i] = from_net_order(hdr.args[i]);
846 /* check header */
847 if(hdr.magic != HWSERVER_MAGIC)
848 break;
849 /* receive data
850 * FIXME check length here */
851 uint8_t *data = nullptr;
852 if(hdr.length > 0)
853 {
854 data = new uint8_t[hdr.length];
855 sz = hdr.length;
856 /* wait for some command, or exit flag */
857 do
858 err = recv(state->client, (void *)data, sz);
859 while(err == error::TIMEOUT && !state->exit);
860 if(state->exit || err != error::SUCCESS || sz != hdr.length)
861 {
862 delete[] data;
863 break;
864 }
865 }
866 /* hande command */
867 uint8_t *send_data = nullptr;
868 size_t send_size = 0;
869 err = handle_cmd(state, hdr.cmd, hdr.args, data, hdr.length,
870 send_data, send_size);
871 /* free data */
872 delete[] data;
873 /* construct header */
874 if(err != error::SUCCESS)
875 {
876 hdr.magic = to_net_order(HWSERVER_MAGIC);
877 hdr.cmd = to_net_order(HWSERVER_NACK(hdr.cmd));
878 hdr.length = to_net_order(0);
879 hdr.args[0] = to_net_order(HWERR_FAIL);
880 for(size_t i = 1; i < HWSTUB_NET_ARGS; i++)
881 hdr.args[i] = to_net_order(0);
882 send_size = 0;
883 }
884 else
885 {
886 hdr.magic = to_net_order(HWSERVER_MAGIC);
887 hdr.cmd = to_net_order(HWSERVER_ACK(hdr.cmd));
888 hdr.length = to_net_order(send_size);
889 for(size_t i = 0; i < HWSTUB_NET_ARGS; i++)
890 hdr.args[i] = to_net_order(hdr.args[i]);
891 }
892 /* send header */
893 sz = sizeof(hdr);
894 do
895 err = send(state->client, (void *)&hdr, sz);
896 while(err == error::TIMEOUT && !state->exit);
897 if(state->exit || err != error::SUCCESS || sz != sizeof(hdr))
898 {
899 delete[] send_data;
900 break;
901 }
902 /* send data if there is some */
903 if(send_size > 0)
904 {
905 sz = send_size;
906 do
907 err = send(state->client, (void *)send_data, sz);
908 while(err == error::TIMEOUT && !state->exit);
909 delete[] send_data;
910 if(state->exit || err != error::SUCCESS || sz != send_size)
911 break;
912 }
913 }
914 debug() << "[net::srv::client] stop: " << state->client << "\n";
915 /* clean client state to avoiding keeping references to objets */
916 state->dev_map.clear();
917 state->handle_map.clear();
918 /* kill client */
919 terminate_client(state->client);
920}
921
922void server::client_arrived(srv_client_t client)
923{
924 std::unique_lock<std::recursive_mutex> lock(m_mutex);
925 debug() << "[net::srv] client arrived: " << client << "\n";
926 /* the naive way would be to use a std::thread but this class is annoying
927 * because it is impossible to check if a thread has exited, except by calling
928 * join() which is blocking. Fortunately, std::packaged_task and std::future
929 * provide a way around this */
930
931 std::packaged_task<void(server*, client_state*)> task(&server::client_thread2);
932 m_client.emplace_back(client, task.get_future());
933 std::thread(std::move(task), this, &m_client.back()).detach();
934}
935
936void server::client_left(srv_client_t client)
937{
938 std::unique_lock<std::recursive_mutex> lock(m_mutex);
939 debug() << "[net::srv] client left: " << client << "\n";
940 /* find thread and set its exit flag, also cleanup threads that finished */
941 for(auto it = m_client.begin(); it != m_client.end();)
942 {
943 /* check if thread has finished */
944 if(it->future.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
945 {
946 it = m_client.erase(it);
947 continue;
948 }
949 /* set exit flag if this our thread */
950 if(it->client == client)
951 it->exit = true;
952 ++it;
953 }
954}
955
956error server::handle_cmd(client_state *state, uint32_t cmd, uint32_t args[HWSTUB_NET_ARGS],
957 uint8_t *recv_data, size_t recv_size, uint8_t*& send_data, size_t& send_size)
958{
959 send_data = nullptr;
960 send_size = 0;
961 /* NOTE: commands are serialized by the client thread, this function is thus
962 * thread safe WITH RESPECT TO CLIENT DATA. If you need to use global data here,
963 * protect it by a mutex or make sure it is safe (hwstub context is thread-safe) */
964
965 /* HELLO */
966 if(cmd == HWSERVER_HELLO)
967 {
968 debug() << "[net::srv::cmd] --> HELLO " << ((args[0] & 0xff00) >> 8)
969 << "." << (args[0] & 0xff);
970 if(args[0] != (HWSTUB_VERSION_MAJOR << 8 | HWSTUB_VERSION_MINOR))
971 {
972 debug() << " (mismatch)\n";
973 return error::ERROR;
974 }
975 debug() << " (good)\n";
976 debug() << "[net::srv::cmd] <-- HELLO " << HWSTUB_VERSION_MAJOR << "."
977 << HWSTUB_VERSION_MINOR << "\n";
978 /* send HELLO with our version */
979 args[0] = HWSTUB_VERSION_MAJOR << 8 | HWSTUB_VERSION_MINOR;
980 return error::SUCCESS;
981 }
982 /* BYE */
983 else if(cmd == HWSERVER_BYE)
984 {
985 debug() << "[net::srv::cmd] --> BYE\n";
986 /* ask client thread to exit after this */
987 state->exit = true;
988 debug() << "[net::srv::cmd] <-- BYE\n";
989 return error::SUCCESS;
990 }
991 /* GET_DEV_LIST */
992 else if(cmd == HWSERVER_GET_DEV_LIST)
993 {
994 debug() << "[net::srv::cmd] --> GET_DEV_LIST\n";
995 /* fetch list again */
996 std::vector<std::shared_ptr<hwstub::device>> list;
997 error err = m_context->get_device_list(list);
998 if(err != error::SUCCESS)
999 {
1000 debug() << "[net::srv::cmd] cannot fetch list: " << hwstub::error_string(err) << "\n";
1001 debug() << "[net::srv::cmd] <-- GET_DEV_LIST (error)\n";
1002 return err;
1003 }
1004 /* update list: drop device that left */
1005 std::vector<uint32_t> to_drop;
1006 for(auto it : state->dev_map)
1007 {
1008 bool still_there = false;
1009 /* this has quadratic complexity, optimize this if needed */
1010 for(auto dev : list)
1011 if(it.second == dev)
1012 still_there = true;
1013 if(!still_there)
1014 to_drop.push_back(it.first);
1015 }
1016 for(auto id : to_drop)
1017 state->dev_map.erase(state->dev_map.find(id));
1018 /* add new devices */
1019 std::vector<std::shared_ptr<hwstub::device>> to_add;
1020 for(auto dev : list)
1021 {
1022 bool already_there = false;
1023 for(auto it : state->dev_map)
1024 if(it.second == dev)
1025 already_there = true;
1026 if(!already_there)
1027 to_add.push_back(dev);
1028 }
1029 for(auto dev : to_add)
1030 state->dev_map[state->next_dev_id++] = dev;
1031 /* create response list */
1032 send_size = sizeof(uint32_t) * state->dev_map.size();
1033 send_data = new uint8_t[send_size];
1034 uint32_t *p = (uint32_t *)send_data;
1035 for(auto it : state->dev_map)
1036 *p++ = to_net_order(it.first);
1037 debug() << "[net::srv::cmd] <-- GET_DEV_LIST\n";
1038 return error::SUCCESS;
1039 }
1040 /* DEV_OPEN */
1041 else if(cmd == HWSERVER_DEV_OPEN)
1042 {
1043 uint32_t devid = args[0];
1044 debug() << "[net::srv::cmd] --> DEV_OPEN(" << devid << ")\n";
1045 /* check ID is valid */
1046 auto it = state->dev_map.find(devid);
1047 if(it == state->dev_map.end())
1048 {
1049 debug() << "[net::srv::cmd] unknwon device ID\n";
1050 debug() << "[net::srv::cmd] <-- DEV_OPEN (error)\n";
1051 return error::ERROR;
1052 }
1053 /* good, now try to get a handle */
1054 std::shared_ptr<hwstub::handle> handle;
1055 error err = it->second->open(handle);
1056 if(err != error::SUCCESS)
1057 {
1058 debug() << "[net::srv::cmd] cannot open device: " << hwstub::error_string(err) << "\n";
1059 return err;
1060 }
1061 /* record ID and return it */
1062 args[0] = state->next_handle_id;
1063 state->handle_map[state->next_handle_id++] = handle;
1064 return error::SUCCESS;
1065 }
1066 /* DEV_CLOSE */
1067 else if(cmd == HWSERVER_DEV_CLOSE)
1068 {
1069 uint32_t hid = args[0];
1070 debug() << "[net::srv::cmd] --> DEV_CLOSE(" << hid << ")\n";
1071 /* check ID is valid */
1072 auto it = state->handle_map.find(hid);
1073 if(it == state->handle_map.end())
1074 {
1075 debug() << "[net::srv::cmd] unknwon handle ID\n";
1076 debug() << "[net::srv::cmd] <-- DEV_CLOSE (error)\n";
1077 return error::ERROR;
1078 }
1079 /* release ID and handle */
1080 state->handle_map.erase(it);
1081 debug() << "[net::srv::cmd] <-- DEV_CLOSE\n";
1082 return error::SUCCESS;
1083 }
1084 /* HWSERVER_GET_DESC */
1085 else if(cmd == HWSERVER_GET_DESC)
1086 {
1087 uint32_t hid = args[0];
1088 uint32_t did = args[1];
1089 uint32_t len = args[2];
1090 debug() << "[net::srv::cmd] --> GET_DESC(" << hid << ",0x" << std::hex << did
1091 << "," << len << ")\n";
1092 /* check ID is valid */
1093 auto it = state->handle_map.find(hid);
1094 if(it == state->handle_map.end())
1095 {
1096 debug() << "[net::srv::cmd] unknown handle ID\n";
1097 debug() << "[net::srv::cmd] <-- GET_DESC (error)\n";
1098 return error::ERROR;
1099 }
1100 /* query desc */
1101 send_size = len;
1102 send_data = new uint8_t[send_size];
1103 error err = it->second->get_desc(did, send_data, send_size);
1104 if(err != error::SUCCESS)
1105 {
1106 delete[] send_data;
1107 debug() << "[net::srv::cmd] cannot get descriptor: " << error_string(err) << "\n";
1108 debug() << "[net::srv::cmd] <-- GET_DESC (error)\n";
1109 return err;
1110 }
1111 debug() << "[net::srv::cmd] <-- GET_DESC\n";
1112 return error::SUCCESS;
1113 }
1114 /* HWSERVER_GET_LOG */
1115 else if(cmd == HWSERVER_GET_LOG)
1116 {
1117 uint32_t hid = args[0];
1118 uint32_t len = args[1];
1119 debug() << "[net::srv::cmd] --> GET_LOG(" << hid << "," << len << ")\n";
1120 /* check ID is valid */
1121 auto it = state->handle_map.find(hid);
1122 if(it == state->handle_map.end())
1123 {
1124 debug() << "[net::srv::cmd] unknown handle ID\n";
1125 debug() << "[net::srv::cmd] <-- GET_DESC (error)\n";
1126 return error::ERROR;
1127 }
1128 /* query log */
1129 send_size = len;
1130 send_data = new uint8_t[send_size];
1131 error err = it->second->get_log(send_data, send_size);
1132 if(err != error::SUCCESS)
1133 {
1134 delete[] send_data;
1135 debug() << "[net::srv::cmd] cannot get log: " << error_string(err) << "\n";
1136 debug() << "[net::srv::cmd] <-- GET_LOG (error)\n";
1137 return err;
1138 }
1139 if(send_size == 0)
1140 delete[] send_data;
1141 debug() << "[net::srv::cmd] <-- GET_LOG\n";
1142 return error::SUCCESS;
1143 }
1144 /* HWSERVER_READ */
1145 else if(cmd == HWSERVER_READ)
1146 {
1147 uint32_t hid = args[0];
1148 uint32_t addr = args[1];
1149 uint32_t len = args[2];
1150 uint32_t flags = args[3];
1151 debug() << "[net::srv::cmd] --> READ(" << hid << ",0x" << std::hex << addr << ","
1152 << len << ",0x" << std::hex << flags << ")\n";
1153 /* check ID is valid */
1154 auto it = state->handle_map.find(hid);
1155 if(it == state->handle_map.end())
1156 {
1157 debug() << "[net::srv::cmd] unknown handle ID\n";
1158 debug() << "[net::srv::cmd] <-- READ (error)\n";
1159 return error::ERROR;
1160 }
1161 /* read */
1162 send_size = len;
1163 send_data = new uint8_t[send_size];
1164 error err = it->second->read(addr, send_data, send_size, !!(flags & HWSERVER_RW_ATOMIC));
1165 if(err != error::SUCCESS)
1166 {
1167 delete[] send_data;
1168 debug() << "[net::srv::cmd] cannot read: " << error_string(err) << "\n";
1169 debug() << "[net::srv::cmd] <-- READ (error)\n";
1170 return err;
1171 }
1172 debug() << "[net::srv::cmd] <-- READ\n";
1173 return error::SUCCESS;
1174 }
1175 /* HWSERVER_WRITE */
1176 else if(cmd == HWSERVER_WRITE)
1177 {
1178 uint32_t hid = args[0];
1179 uint32_t addr = args[1];
1180 uint32_t flags = args[2];
1181 debug() << "[net::srv::cmd] --> WRITE(" << hid << ",0x" << std::hex << addr << ","
1182 << recv_size << ",0x" << std::hex << flags << ")\n";
1183 /* check ID is valid */
1184 auto it = state->handle_map.find(hid);
1185 if(it == state->handle_map.end())
1186 {
1187 debug() << "[net::srv::cmd] unknown handle ID\n";
1188 debug() << "[net::srv::cmd] <-- WRITE (error)\n";
1189 return error::ERROR;
1190 }
1191 /* write */
1192 error err = it->second->write(addr, recv_data, recv_size, !!(flags & HWSERVER_RW_ATOMIC));
1193 if(err != error::SUCCESS)
1194 {
1195 delete[] send_data;
1196 debug() << "[net::srv::cmd] cannot write: " << error_string(err) << "\n";
1197 debug() << "[net::srv::cmd] <-- WRITE (error)\n";
1198 return err;
1199 }
1200 debug() << "[net::srv::cmd] <-- WRITE\n";
1201 return error::SUCCESS;
1202 }
1203 else
1204 {
1205 debug() << "[net::srv::cmd] <-> unknown cmd (0x" << std::hex << cmd << ")\n";
1206 return error::ERROR;
1207 }
1208}
1209
1210/*
1211 * Socket server
1212 */
1213socket_server::socket_server(std::shared_ptr<hwstub::context> contex, int socket_fd)
1214 :server(contex), m_socketfd(socket_fd)
1215{
1216 m_discovery_exit = false;
1217 set_timeout(std::chrono::milliseconds(1000));
1218 m_discovery_thread = std::thread(&socket_server::discovery_thread1, this);
1219}
1220
1221socket_server::~socket_server()
1222{
1223 /* first stop discovery thread to make sure no more clients are created */
1224 m_discovery_exit = true;
1225 m_discovery_thread.join();
1226 close(m_socketfd);
1227 /* ask server to do a clean stop */
1228 stop_server();
1229}
1230
1231std::shared_ptr<server> socket_server::create(std::shared_ptr<hwstub::context> ctx,
1232 int socket_fd)
1233{
1234 // NOTE: can't use make_shared() because of the protected ctor */
1235 return std::shared_ptr<socket_server>(new socket_server(ctx, socket_fd));
1236}
1237
1238void socket_server::set_timeout(std::chrono::milliseconds ms)
1239{
1240 m_timeout.tv_usec = 1000 * (ms.count() % 1000);
1241 m_timeout.tv_sec = ms.count() / 1000;
1242}
1243
1244int socket_server::from_srv_client(srv_client_t cli)
1245{
1246 return (int)(intptr_t)cli;
1247}
1248
1249socket_server::srv_client_t socket_server::to_srv_client(int fd)
1250{
1251 return (srv_client_t)(intptr_t)fd;
1252}
1253
1254void socket_server::discovery_thread1(socket_server *s)
1255{
1256 s->discovery_thread();
1257}
1258
1259void socket_server::terminate_client(srv_client_t client)
1260{
1261 debug() << "[net::srv::sock] terminate client: " << client << "\n";
1262 /* simply close connection */
1263 close(from_srv_client(client));
1264}
1265
1266error socket_server::send(srv_client_t client, void *buffer, size_t& sz)
1267{
1268 debug() << "[net::ctx::sock] send(" << client << ", " << sz << "): ";
1269 int ret = ::send(from_srv_client(client), buffer, sz, MSG_NOSIGNAL);
1270 if(ret >= 0)
1271 {
1272 debug() << "good(" << ret << ")\n";
1273 sz = (size_t)ret;
1274 return error::SUCCESS;
1275 }
1276 /* convert some errors */
1277 debug() << "fail(" << errno << "," << strerror(errno) << ")\n";
1278 switch(errno)
1279 {
1280#if EAGAIN != EWOULDBLOCK
1281 case EAGAIN:
1282#endif
1283 case EWOULDBLOCK: return error::TIMEOUT;
1284 case ECONNRESET: case EPIPE: return error::SERVER_DISCONNECTED;
1285 default: return error::NET_ERROR;
1286 }
1287}
1288
1289error socket_server::recv(srv_client_t client, void *buffer, size_t& sz)
1290{
1291 debug() << "[net::ctx::sock] recv(" << client << ", " << sz << "): ";
1292 int ret = ::recv(from_srv_client(client), buffer, sz, MSG_WAITALL);
1293 if(ret > 0)
1294 {
1295 debug() << "good(" << ret << ")\n";
1296 sz = (size_t)ret;
1297 return error::SUCCESS;
1298 }
1299 if(ret == 0)
1300 {
1301 debug() << "disconnected\n";
1302 return error::SERVER_DISCONNECTED;
1303 }
1304 debug() << "fail(" << errno << "," << strerror(errno) << ")\n";
1305 switch(errno)
1306 {
1307#if EAGAIN != EWOULDBLOCK
1308 case EAGAIN:
1309#endif
1310 case EWOULDBLOCK: return error::TIMEOUT;
1311 default: return error::NET_ERROR;
1312 }
1313}
1314
1315void socket_server::discovery_thread()
1316{
1317 debug() << "[net::srv:sock::discovery] start\n";
1318 /* begin listening to incoming connections */
1319 if(listen(m_socketfd, LISTEN_QUEUE_SIZE) < 0)
1320 {
1321 debug() << "[net::srv::sock::discovery] listen() failed: " << errno << "\n";
1322 return;
1323 }
1324
1325 /* handle connections */
1326 while(!m_discovery_exit)
1327 {
1328 /* since accept() is blocking, use select to ensure a timeout */
1329 struct timeval tmo = m_timeout; /* NOTE select() can overwrite timeout */
1330 fd_set set;
1331 FD_ZERO(&set);
1332 FD_SET(m_socketfd, &set);
1333 /* wait for some activity */
1334 int ret = select(m_socketfd + 1, &set, nullptr, nullptr, &tmo);
1335 if(ret < 0 || !FD_ISSET(m_socketfd, &set))
1336 continue;
1337 int clifd = accept(m_socketfd, nullptr, nullptr);
1338 if(clifd >= 0)
1339 {
1340 debug() << "[net::srv::sock::discovery] new client\n";
1341 /* set timeout for the client operations */
1342 setsockopt(clifd, SOL_SOCKET, SO_RCVTIMEO, (char *)&m_timeout, sizeof(m_timeout));
1343 setsockopt(clifd, SOL_SOCKET, SO_SNDTIMEO, (char *)&m_timeout, sizeof(m_timeout));
1344 client_arrived(to_srv_client(clifd));
1345 }
1346 }
1347 debug() << "[net::srv:sock::discovery] stop\n";
1348}
1349
1350} // namespace uri
1351} // namespace net
diff --git a/utils/hwstub/lib/hwstub_protocol.h b/utils/hwstub/lib/hwstub_protocol.h
deleted file mode 100644
index 35510fa9b2..0000000000
--- a/utils/hwstub/lib/hwstub_protocol.h
+++ /dev/null
@@ -1 +0,0 @@
1#include "../hwstub_protocol.h"
diff --git a/utils/hwstub/lib/hwstub_uri.cpp b/utils/hwstub/lib/hwstub_uri.cpp
new file mode 100644
index 0000000000..e2f252f3dc
--- /dev/null
+++ b/utils/hwstub/lib/hwstub_uri.cpp
@@ -0,0 +1,332 @@
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#include "hwstub_uri.hpp"
22#include "hwstub_usb.hpp"
23#include "hwstub_virtual.hpp"
24#include "hwstub_net.hpp"
25#include <cctype>
26
27namespace hwstub {
28namespace uri {
29
30void print_usage(FILE *f, bool client, bool server)
31{
32 if(client && server)
33 fprintf(f, "A context/server");
34 else if(client)
35 fprintf(f, "A context");
36 else
37 fprintf(f, "A server");
38 fprintf(f, " URI has the following format:\n");
39 fprintf(f, " scheme:[//domain[:port]][/]\n");
40 fprintf(f, "The scheme is mandatory and specifies the type of context to create.\n");
41 fprintf(f, "The following scheme are recognized:\n");
42 if(client)
43 fprintf(f, " usb USB context (using libusb)\n");
44 fprintf(f, " tcp TCP context\n");
45 fprintf(f, " unix Local unix domain context\n");
46 if(client)
47 fprintf(f, " virt A virtual context (testing and debugging mostly)\n");
48 fprintf(f, " default Default choice made by the library\n");
49 if(client)
50 {
51 fprintf(f, "When creating a USB context, the domain and port must be empty:\n");
52 fprintf(f, " usb:\n");
53 }
54 fprintf(f, "When creating a TCP context, the domain and port are the usual TCP parameters:\n");
55 fprintf(f, " tcp://localhost\n");
56 fprintf(f, " tcp://localhost:6666\n");
57 fprintf(f, "The port is optional, in which the default port %s is used.\n", hwstub::net::context::default_tcp_port().c_str());
58 fprintf(f, "When creating a unix domain context, the port must be empty and there are two type of unix domains.\n");
59 fprintf(f, "Normal domains are specified by filesystem paths. Abstract domains (Linux-only) can be arbitrary strings,\n");
60 fprintf(f, "in which case the domain must start with a '#':\n");
61 fprintf(f, " unix:///path/to/socket\n");
62 fprintf(f, " unix://#hwstub\n");
63 if(client)
64 {
65 fprintf(f, "When creating a virtual context, the domain must contain a specification of the devices.\n");
66 fprintf(f, "The device list is of the form type(param);type(param);... where the only supported type\n");
67 fprintf(f, "at the moment is 'dummy' with a single parameter which is the device name:\n");
68 fprintf(f, " virt://dummy(Device A);dummy(Device B);dummy(Super device C)\n");
69 }
70 if(server && client)
71 fprintf(f, "Note that usb and virt schemes are not supported for server URIs.\n");
72}
73
74uri::uri(const std::string& uri)
75 :m_uri(uri), m_valid(false)
76{
77 parse();
78}
79
80bool uri::validate_scheme()
81{
82 /* scheme must be nonempty */
83 if(m_scheme.empty())
84 {
85 m_error = "empty scheme";
86 return false;
87 }
88 /* and contain alphanumeric characters or '+', '-' or '.' */
89 for(auto c : m_scheme)
90 if(!isalnum(c) && c != '+' && c != '-' && c != '.')
91 {
92 m_error = std::string("invalid character '") + c + "' in scheme";
93 return false;
94 }
95 return true;
96}
97
98bool uri::validate_domain()
99{
100 return true;
101}
102
103bool uri::validate_port()
104{
105 return true;
106}
107
108void uri::parse()
109{
110 std::string str = m_uri;
111 /* try to find the first ':' */
112 size_t scheme_colon = str.find(':');
113 if(scheme_colon == std::string::npos)
114 {
115 m_error = "URI contains no scheme";
116 return;
117 }
118 /* found scheme */
119 m_scheme = str.substr(0, scheme_colon);
120 /* validate scheme */
121 if(!validate_scheme())
122 return;
123 /* remove scheme: */
124 str = str.substr(scheme_colon + 1);
125 /* check if it begins with // */
126 if(str.size() >= 2 && str[0] == '/' && str[1] == '/')
127 {
128 /* remove it */
129 str = str.substr(2);
130 /* find next '/' */
131 std::string path;
132 size_t next_slash = str.find('/');
133 /* if none, we can assume the path is empty, otherwise split path */
134 if(next_slash != std::string::npos)
135 {
136 path = str.substr(next_slash);
137 str = str.substr(0, next_slash);
138 }
139 /* find last ':' if any, indicating a port */
140 size_t port_colon = str.rfind(':');
141 if(port_colon != std::string::npos)
142 {
143 m_domain = str.substr(0, port_colon);
144 m_port = str.substr(port_colon + 1);
145 }
146 else
147 m_domain = str;
148 if(!validate_domain() || !validate_port())
149 return;
150 /* pursue with path */
151 str = path;
152 }
153 /* next is path */
154 m_path = str;
155 m_valid = true;
156}
157
158bool uri::valid() const
159{
160 return m_valid;
161}
162
163std::string uri::error() const
164{
165 return m_error;
166}
167
168std::string uri::full_uri() const
169{
170 return m_uri;
171}
172
173std::string uri::scheme() const
174{
175 return m_scheme;
176}
177
178std::string uri::domain() const
179{
180 return m_domain;
181}
182
183std::string uri::port() const
184{
185 return m_port;
186}
187
188std::string uri::path() const
189{
190 return m_path;
191}
192
193/** Context creator */
194
195std::shared_ptr<context> create_default_context(std::string *error)
196{
197 /* first try to create a unix context with abstract name 'hwstub' */
198 std::shared_ptr<context> ctx = hwstub::net::context::create_unix_abstract(
199 hwstub::net::context::default_unix_path(), error);
200 if(ctx)
201 return ctx;
202 /* otherwise try default tcp */
203 ctx = hwstub::net::context::create_tcp(hwstub::net::context::default_tcp_domain(),
204 hwstub::net::context::default_tcp_port(), error);
205 if(ctx)
206 return ctx;
207 /* otherwise default to usb */
208 return hwstub::usb::context::create(nullptr, false, error);
209}
210
211std::shared_ptr<context> create_context(const uri& uri, std::string *error)
212{
213 /* check URI is valid */
214 if(!uri.valid())
215 {
216 if(error)
217 *error = "invalid URI: " + uri.error();
218 return std::shared_ptr<context>();
219 }
220 /* handle different types of contexts */
221 if(uri.scheme() == "usb")
222 {
223 /* domain and port must be empty */
224 if(!uri.domain().empty() || !uri.port().empty())
225 {
226 if(error)
227 *error = "USB URI cannot contain a domain or a port";
228 return std::shared_ptr<context>();
229 }
230 /* in doubt, create a new libusb context and let the context destroy it */
231 libusb_context *ctx;
232 libusb_init(&ctx);
233 return hwstub::usb::context::create(ctx, true);
234 }
235 else if(uri.scheme() == "virt")
236 {
237 /* port must be empty */
238 if(!uri.port().empty())
239 {
240 if(error)
241 *error = "virt URI cannot contain a port";
242 return std::shared_ptr<context>();
243 }
244 return hwstub::virt::context::create_spec(uri.domain(), error);
245 }
246 else if(uri.scheme() == "tcp")
247 {
248 return hwstub::net::context::create_tcp(uri.domain(), uri.port(), error);
249 }
250 else if(uri.scheme() == "unix")
251 {
252 /* port must be empty */
253 if(!uri.port().empty())
254 {
255 if(error)
256 *error = "unix URI cannot contain a port";
257 return std::shared_ptr<context>();
258 }
259 if(!uri.domain().empty() && uri.domain()[0] == '#')
260 return hwstub::net::context::create_unix_abstract(uri.domain().substr(1), error);
261 else
262 return hwstub::net::context::create_unix(uri.domain(), error);
263 }
264 else if(uri.scheme() == "default")
265 {
266 /* domain and port must be empty */
267 if(!uri.domain().empty() || !uri.port().empty())
268 {
269 if(error)
270 *error = "Default URI cannot contain a domain or a port";
271 return std::shared_ptr<context>();
272 }
273 return create_default_context(error);
274 }
275 else
276 {
277 if(error)
278 *error = "unknown scheme '" + uri.scheme() + "'";
279 return std::shared_ptr<context>();
280 }
281}
282
283std::shared_ptr<net::server> create_server(std::shared_ptr<context> ctx,
284 const uri& uri, std::string *error)
285{
286 /* check URI is valid */
287 if(!uri.valid())
288 {
289 if(error)
290 *error = "invalid URI: " + uri.error();
291 return std::shared_ptr<net::server>();
292 }
293 /* handle different types of contexts */
294 if(uri.scheme() == "unix")
295 {
296 /* port must be empty */
297 if(!uri.port().empty())
298 {
299 if(error)
300 *error = "unix URI cannot contain a port";
301 return std::shared_ptr<net::server>();
302 }
303 if(!uri.domain().empty() && uri.domain()[0] == '#')
304 return hwstub::net::server::create_unix_abstract(ctx, uri.domain().substr(1), error);
305 else
306 return hwstub::net::server::create_unix(ctx, uri.domain(), error);
307 }
308 else if(uri.scheme() == "tcp")
309 {
310 return hwstub::net::server::create_tcp(ctx, uri.domain(), uri.port(), error);
311 }
312 else
313 {
314 if(error)
315 *error = "unknown scheme '" + uri.scheme() + "'";
316 return std::shared_ptr<net::server>();
317 }
318}
319
320uri default_uri()
321{
322 return uri("default:");
323}
324
325uri default_server_uri()
326{
327 return uri("unix://#" + hwstub::net::context::default_unix_path());
328}
329
330} // namespace uri
331} // namespace hwstub
332
diff --git a/utils/hwstub/lib/hwstub_usb.cpp b/utils/hwstub/lib/hwstub_usb.cpp
new file mode 100644
index 0000000000..28c64d9df3
--- /dev/null
+++ b/utils/hwstub/lib/hwstub_usb.cpp
@@ -0,0 +1,728 @@
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#include "hwstub.hpp"
22#include "hwstub_usb.hpp"
23#include <cstring> /* for memcpy */
24
25namespace hwstub {
26namespace usb {
27
28const uint8_t VR_GET_CPU_INFO = 0;
29const uint8_t VR_SET_DATA_ADDRESS = 1;
30const uint8_t VR_SET_DATA_LENGTH = 2;
31const uint8_t VR_FLUSH_CACHES = 3;
32const uint8_t VR_PROGRAM_START1 = 4;
33const uint8_t VR_PROGRAM_START2 = 5;
34
35/**
36 * Context
37 */
38
39context::context(libusb_context *ctx, bool cleanup_ctx)
40 :m_usb_ctx(ctx), m_cleanup_ctx(cleanup_ctx)
41{
42}
43
44context::~context()
45{
46 if(m_cleanup_ctx)
47 libusb_exit(m_usb_ctx);
48}
49
50std::shared_ptr<context> context::create(libusb_context *ctx, bool cleanup_ctx,
51 std::string *error)
52{
53 (void) error;
54 if(ctx == nullptr)
55 libusb_init(nullptr);
56 // NOTE: can't use make_shared() because of the protected ctor */
57 return std::shared_ptr<context>(new context(ctx, cleanup_ctx));
58}
59
60libusb_context *context::native_context()
61{
62 return m_usb_ctx;
63}
64
65libusb_device *context::from_ctx_dev(ctx_dev_t dev)
66{
67 return reinterpret_cast<libusb_device*>(dev);
68}
69
70hwstub::context::ctx_dev_t context::to_ctx_dev(libusb_device *dev)
71{
72 return static_cast<ctx_dev_t>(dev);
73}
74
75error context::fetch_device_list(std::vector<ctx_dev_t>& list, void*& ptr)
76{
77 libusb_device **usb_list;
78 ssize_t ret = libusb_get_device_list(m_usb_ctx, &usb_list);
79 if(ret < 0)
80 return error::ERROR;
81 ptr = (void *)usb_list;
82 list.clear();
83 for(int i = 0; i < ret; i++)
84 if(device::is_hwstub_dev(usb_list[i]))
85 list.push_back(to_ctx_dev(usb_list[i]));
86 return error::SUCCESS;
87}
88
89void context::destroy_device_list(void *ptr)
90{
91 /* remove all references */
92 libusb_free_device_list((libusb_device **)ptr, 1);
93}
94
95error context::create_device(ctx_dev_t dev, std::shared_ptr<hwstub::device>& hwdev)
96{
97 // NOTE: can't use make_shared() because of the protected ctor */
98 hwdev.reset(new device(shared_from_this(), from_ctx_dev(dev)));
99 return error::SUCCESS;
100}
101
102bool context::match_device(ctx_dev_t dev, std::shared_ptr<hwstub::device> hwdev)
103{
104 device *udev = dynamic_cast<device*>(hwdev.get());
105 return udev != nullptr && udev->native_device() == dev;
106}
107
108/**
109 * Device
110 */
111device::device(std::shared_ptr<hwstub::context> ctx, libusb_device *dev)
112 :hwstub::device(ctx), m_dev(dev)
113{
114 libusb_ref_device(dev);
115}
116
117device::~device()
118{
119 libusb_unref_device(m_dev);
120}
121
122libusb_device *device::native_device()
123{
124 return m_dev;
125}
126
127bool device::is_hwstub_dev(libusb_device *dev)
128{
129 struct libusb_device_descriptor dev_desc;
130 struct libusb_config_descriptor *config = nullptr;
131 int intf = 0;
132 if(libusb_get_device_descriptor(dev, &dev_desc) != 0)
133 goto Lend;
134 if(libusb_get_config_descriptor(dev, 0, &config) != 0)
135 goto Lend;
136 /* Try to find Rockbox hwstub interface or a JZ device */
137 if(rb_handle::find_intf(&dev_desc, config, intf) ||
138 jz_handle::is_boot_dev(&dev_desc, config))
139 {
140 libusb_free_config_descriptor(config);
141 return true;
142 }
143Lend:
144 if(config)
145 libusb_free_config_descriptor(config);
146 return false;
147}
148
149error device::open_dev(std::shared_ptr<hwstub::handle>& handle)
150{
151 int intf = -1;
152 /* open the device */
153 libusb_device_handle *h;
154 int err = libusb_open(m_dev, &h);
155 if(err != LIBUSB_SUCCESS)
156 return error::ERROR;
157 /* fetch some descriptors */
158 struct libusb_device_descriptor dev_desc;
159 struct libusb_config_descriptor *config = nullptr;
160 if(libusb_get_device_descriptor(m_dev, &dev_desc) != 0)
161 goto Lend;
162 if(libusb_get_config_descriptor(m_dev, 0, &config) != 0)
163 goto Lend;
164 /* Try to find Rockbox hwstub interface */
165 if(rb_handle::find_intf(&dev_desc, config, intf))
166 {
167 libusb_free_config_descriptor(config);
168 /* create the handle */
169 // NOTE: can't use make_shared() because of the protected ctor */
170 handle.reset(new rb_handle(shared_from_this(), h, intf));
171 }
172 /* Maybe this is a JZ device ? */
173 else if(jz_handle::is_boot_dev(&dev_desc, config))
174 {
175 libusb_free_config_descriptor(config);
176 /* create the handle */
177 // NOTE: can't use make_shared() because of the protected ctor */
178 handle.reset(new jz_handle(shared_from_this(), h));
179 }
180 else
181 {
182 libusb_free_config_descriptor(config);
183 return error::ERROR;
184 }
185 /* the class will perform some probing on creation: check that it actually worked */
186 if(handle->valid())
187 return error::SUCCESS;
188 /* abort */
189 handle.reset(); // will close the libusb handle
190 return error::ERROR;
191
192Lend:
193 if(config)
194 libusb_free_config_descriptor(config);
195 libusb_close(h);
196 return error::ERROR;
197}
198
199bool device::has_multiple_open() const
200{
201 /* libusb only allows one handle per device */
202 return false;
203}
204
205uint8_t device::get_bus_number()
206{
207 return libusb_get_bus_number(native_device());
208}
209
210uint8_t device::get_address()
211{
212 return libusb_get_device_address(native_device());
213}
214
215uint16_t device::get_vid()
216{
217 /* NOTE: doc says it's cached so it should always succeed */
218 struct libusb_device_descriptor dev_desc;
219 libusb_get_device_descriptor(native_device(), &dev_desc);
220 return dev_desc.idVendor;
221}
222
223uint16_t device::get_pid()
224{
225 /* NOTE: doc says it's cached so it should always succeed */
226 struct libusb_device_descriptor dev_desc;
227 libusb_get_device_descriptor(native_device(), &dev_desc);
228 return dev_desc.idProduct;
229}
230
231/**
232 * USB handle
233 */
234handle::handle(std::shared_ptr<hwstub::device> dev, libusb_device_handle *handle)
235 :hwstub::handle(dev), m_handle(handle)
236{
237 set_timeout(std::chrono::milliseconds(100));
238}
239
240handle::~handle()
241{
242 libusb_close(m_handle);
243}
244
245error handle::interpret_libusb_error(int err)
246{
247 if(err >= 0)
248 return error::SUCCESS;
249 if(err == LIBUSB_ERROR_NO_DEVICE)
250 return error::DISCONNECTED;
251 else
252 return error::USB_ERROR;
253}
254
255error handle::interpret_libusb_error(int err, size_t expected_val)
256{
257 if(err < 0)
258 return interpret_libusb_error(err);
259 if((size_t)err != expected_val)
260 return error::ERROR;
261 return error::SUCCESS;
262}
263
264error handle::interpret_libusb_size(int err, size_t& out_siz)
265{
266 if(err < 0)
267 return interpret_libusb_error(err);
268 out_siz = (size_t)err;
269 return error::SUCCESS;
270}
271
272void handle::set_timeout(std::chrono::milliseconds ms)
273{
274 m_timeout = ms.count();
275}
276
277/**
278 * Rockbox Handle
279 */
280
281rb_handle::rb_handle(std::shared_ptr<hwstub::device> dev,
282 libusb_device_handle *handle, int intf)
283 :hwstub::usb::handle(dev, handle), m_intf(intf), m_transac_id(0), m_buf_size(1)
284{
285 m_probe_status = error::SUCCESS;
286 /* claim interface */
287 if(libusb_claim_interface(m_handle, m_intf) != 0)
288 m_probe_status = error::PROBE_FAILURE;
289 /* check version */
290 if(m_probe_status == error::SUCCESS)
291 {
292 struct hwstub_version_desc_t ver_desc;
293 m_probe_status = get_version_desc(ver_desc);
294 if(m_probe_status == error::SUCCESS)
295 {
296 if(ver_desc.bMajor != HWSTUB_VERSION_MAJOR ||
297 ver_desc.bMinor < HWSTUB_VERSION_MINOR)
298 m_probe_status = error::PROBE_FAILURE;
299 }
300 }
301 /* get buffer size */
302 if(m_probe_status == error::SUCCESS)
303 {
304 struct hwstub_layout_desc_t layout_desc;
305 m_probe_status = get_layout_desc(layout_desc);
306 if(m_probe_status == error::SUCCESS)
307 m_buf_size = layout_desc.dBufferSize;
308 }
309}
310
311rb_handle::~rb_handle()
312{
313}
314
315size_t rb_handle::get_buffer_size()
316{
317 return m_buf_size;
318}
319
320error rb_handle::status() const
321{
322 error err = handle::status();
323 if(err == error::SUCCESS)
324 err = m_probe_status;
325 return err;
326}
327
328error rb_handle::get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz)
329{
330 return interpret_libusb_size(libusb_control_transfer(m_handle,
331 LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN,
332 LIBUSB_REQUEST_GET_DESCRIPTOR, desc << 8, m_intf, (unsigned char *)buf, buf_sz, m_timeout),
333 buf_sz);
334}
335
336error rb_handle::get_dev_log(void *buf, size_t& buf_sz)
337{
338 return interpret_libusb_size(libusb_control_transfer(m_handle,
339 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN,
340 HWSTUB_GET_LOG, 0, m_intf, (unsigned char *)buf, buf_sz, m_timeout), buf_sz);
341}
342
343error rb_handle::exec_dev(uint32_t addr, uint16_t flags)
344{
345 struct hwstub_exec_req_t exec;
346 exec.dAddress = addr;
347 exec.bmFlags = flags;
348 return interpret_libusb_error(libusb_control_transfer(m_handle,
349 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
350 HWSTUB_EXEC, 0, m_intf, (unsigned char *)&exec, sizeof(exec), m_timeout), sizeof(exec));
351}
352
353error rb_handle::read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic)
354{
355 struct hwstub_read_req_t read;
356 read.dAddress = addr;
357 error err = interpret_libusb_error(libusb_control_transfer(m_handle,
358 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
359 HWSTUB_READ, m_transac_id, m_intf, (unsigned char *)&read, sizeof(read), m_timeout),
360 sizeof(read));
361 if(err != error::SUCCESS)
362 return err;
363 return interpret_libusb_size(libusb_control_transfer(m_handle,
364 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN,
365 atomic ? HWSTUB_READ2_ATOMIC : HWSTUB_READ2, m_transac_id++, m_intf,
366 (unsigned char *)buf, sz, m_timeout), sz);
367}
368
369error rb_handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic)
370{
371 size_t hdr_sz = sizeof(struct hwstub_write_req_t);
372 uint8_t *tmp_buf = new uint8_t[sz + hdr_sz];
373 struct hwstub_write_req_t *req = reinterpret_cast<struct hwstub_write_req_t *>(tmp_buf);
374 req->dAddress = addr;
375 memcpy(tmp_buf + hdr_sz, buf, sz);
376 error ret = interpret_libusb_error(libusb_control_transfer(m_handle,
377 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
378 atomic ? HWSTUB_WRITE_ATOMIC : HWSTUB_WRITE, m_transac_id++, m_intf,
379 (unsigned char *)req, sz + hdr_sz, m_timeout), sz + hdr_sz);
380 delete[] tmp_buf;
381 return ret;
382}
383
384bool rb_handle::find_intf(struct libusb_device_descriptor *dev,
385 struct libusb_config_descriptor *config, int& intf_idx)
386{
387 (void) dev;
388 /* search hwstub interface */
389 for(unsigned i = 0; i < config->bNumInterfaces; i++)
390 {
391 /* hwstub interface has only one setting */
392 if(config->interface[i].num_altsetting != 1)
393 continue;
394 const struct libusb_interface_descriptor *intf = &config->interface[i].altsetting[0];
395 /* check class/subclass/protocol */
396 if(intf->bInterfaceClass == HWSTUB_CLASS &&
397 intf->bInterfaceSubClass == HWSTUB_SUBCLASS &&
398 intf->bInterfaceProtocol == HWSTUB_PROTOCOL)
399 {
400 /* found it ! */
401 intf_idx = i;
402 return true;
403 }
404 }
405 return false;
406}
407
408/**
409 * JZ Handle
410 */
411
412namespace
413{
414 uint16_t jz_bcd(char *bcd)
415 {
416 uint16_t v = 0;
417 for(int i = 0; i < 4; i++)
418 v = (bcd[i] - '0') | v << 4;
419 return v;
420 }
421}
422
423jz_handle::jz_handle(std::shared_ptr<hwstub::device> dev,
424 libusb_device_handle *handle)
425 :hwstub::usb::handle(dev, handle)
426{
427 m_probe_status = probe();
428}
429
430jz_handle::~jz_handle()
431{
432}
433
434error jz_handle::probe()
435{
436 char cpuinfo[8];
437 /* Get CPU info and devise descriptor */
438 error err = jz_cpuinfo(cpuinfo);
439 if(err != error::SUCCESS)
440 return err;
441 struct libusb_device_descriptor dev_desc;
442 err = interpret_libusb_error(libusb_get_device_descriptor(
443 libusb_get_device(m_handle), &dev_desc), 0);
444 if(err != error::SUCCESS)
445 return err;
446 /** parse CPU info */
447 /* if cpuinfo if of the form JZxxxxVy then extract xxxx */
448 if(cpuinfo[0] == 'J' && cpuinfo[1] == 'Z' && cpuinfo[6] == 'V')
449 m_desc_jz.wChipID = jz_bcd(cpuinfo + 2);
450 /* if cpuinfo if of the form Bootxxxx then extract xxxx */
451 else if(strncmp(cpuinfo, "Boot", 4) == 4)
452 m_desc_jz.wChipID = jz_bcd(cpuinfo + 4);
453 /* else use usb id */
454 else
455 m_desc_jz.wChipID = dev_desc.idProduct;
456 m_desc_jz.bRevision = 0;
457
458 /** Retrieve product string */
459 memset(m_desc_target.bName, 0, sizeof(m_desc_target.bName));
460 err = interpret_libusb_error(libusb_get_string_descriptor_ascii(m_handle,
461 dev_desc.iProduct, (unsigned char *)m_desc_target.bName, sizeof(m_desc_target.bName)));
462 if(err != error::SUCCESS)
463 return err;
464 /** The JZ4760 and JZ4760B cannot be distinguished by the above information,
465 * for this the best way I have found is to check the SRAM size: 48KiB vs 16KiB.
466 * This requires to enable AHB1 and SRAM clock and read/write to SRAM, but
467 * this code will leaves registers and ram is the same state as before.
468 * In case of failure, simply assume JZ4760. */
469 if(m_desc_jz.wChipID == 0x4760)
470 probe_jz4760b();
471
472 /** Fill descriptors */
473 m_desc_version.bLength = sizeof(m_desc_version);
474 m_desc_version.bDescriptorType = HWSTUB_DT_VERSION;
475 m_desc_version.bMajor = HWSTUB_VERSION_MAJOR;
476 m_desc_version.bMinor = HWSTUB_VERSION_MINOR;
477 m_desc_version.bRevision = 0;
478
479 m_desc_layout.bLength = sizeof(m_desc_layout);
480 m_desc_layout.bDescriptorType = HWSTUB_DT_LAYOUT;
481 m_desc_layout.dCodeStart = 0xbfc00000; /* ROM */
482 m_desc_layout.dCodeSize = 0x2000; /* 8kB per datasheet */
483 m_desc_layout.dStackStart = 0; /* As far as I can tell, the ROM uses no stack */
484 m_desc_layout.dStackSize = 0;
485 m_desc_layout.dBufferStart = 0x080000000;
486 m_desc_layout.dBufferSize = 0x4000;
487
488 m_desc_target.bLength = sizeof(m_desc_target);
489 m_desc_target.bDescriptorType = HWSTUB_DT_TARGET;
490 m_desc_target.dID = HWSTUB_TARGET_JZ;
491
492 m_desc_jz.bLength = sizeof(m_desc_jz);
493 m_desc_jz.bDescriptorType = HWSTUB_DT_JZ;
494
495 /* claim interface */
496 if(libusb_claim_interface(m_handle, 0) != 0)
497 m_probe_status = error::PROBE_FAILURE;
498
499 return m_probe_status;
500}
501
502error jz_handle::read_reg32(uint32_t addr, uint32_t& value)
503{
504 size_t sz = sizeof(value);
505 error err = read_dev(addr, &value, sz, true);
506 if(err == error::SUCCESS && sz != sizeof(value))
507 err = error::ERROR;
508 return err;
509}
510
511error jz_handle::write_reg32(uint32_t addr, uint32_t value)
512{
513 size_t sz = sizeof(value);
514 error err = write_dev(addr, &value, sz, true);
515 if(err == error::SUCCESS && sz != sizeof(value))
516 err = error::ERROR;
517 return err;
518}
519
520error jz_handle::probe_jz4760b()
521{
522 /* first read CPM_CLKGR1 */
523 const uint32_t cpm_clkgr1_addr = 0xb0000028;
524 uint32_t cpm_clkgr1;
525 error err = read_reg32(cpm_clkgr1_addr, cpm_clkgr1);
526 if(err != error::SUCCESS)
527 return err;
528 /* Bit 7 controls AHB1 clock and bit 5 the SRAM. Note that SRAM is on AHB1.
529 * Only ungate if gated */
530 uint32_t cpm_clkgr1_mask = 1 << 7 | 1 << 5;
531 if(cpm_clkgr1 & cpm_clkgr1_mask)
532 {
533 /* ungate both clocks */
534 err = write_reg32(cpm_clkgr1_addr, cpm_clkgr1 & ~cpm_clkgr1_mask);
535 if(err != error::SUCCESS)
536 return err;
537 }
538 /* read first word of SRAM and then at end (supposedly) */
539 uint32_t sram_addr = 0xb32d0000;
540 uint32_t sram_end_addr = sram_addr + 16 * 1024; /* SRAM is 16KiB on JZ4760B */
541 uint32_t sram_start, sram_end;
542 err = read_reg32(sram_addr, sram_start);
543 if(err != error::SUCCESS)
544 goto Lrestore;
545 err = read_reg32(sram_end_addr, sram_end);
546 if(err != error::SUCCESS)
547 goto Lrestore;
548 /* if start and end are different, clearly the size is not 16KiB and this is
549 * JZ4760 and we have nothing to do */
550 if(sram_start != sram_end)
551 goto Lrestore;
552 /* now reverse all bits of the first word */
553 sram_start ^= 0xffffffff;
554 err = write_reg32(sram_addr, sram_start);
555 if(err != error::SUCCESS)
556 goto Lrestore;
557 /* and read again at end */
558 err = read_reg32(sram_end_addr, sram_end);
559 if(err != error::SUCCESS)
560 goto Lrestore;
561 /* if they are still equal, we identified JZ4760B */
562 if(sram_start == sram_end)
563 m_desc_jz.bRevision = 'B';
564 /* restore SRAM value */
565 sram_start ^= 0xffffffff;
566 err = write_reg32(sram_addr, sram_start);
567 if(err != error::SUCCESS)
568 goto Lrestore;
569
570Lrestore:
571 /* restore gates if needed */
572 if(cpm_clkgr1 & cpm_clkgr1_mask)
573 return write_reg32(cpm_clkgr1_addr, cpm_clkgr1);
574 else
575 return error::SUCCESS;
576}
577
578size_t jz_handle::get_buffer_size()
579{
580 return m_desc_layout.dBufferSize;
581}
582
583error jz_handle::status() const
584{
585 error err = handle::status();
586 if(err == error::SUCCESS)
587 err = m_probe_status;
588 return err;
589}
590
591error jz_handle::get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz)
592{
593 void *p = nullptr;
594 switch(desc)
595 {
596 case HWSTUB_DT_VERSION: p = &m_desc_version; break;
597 case HWSTUB_DT_LAYOUT: p = &m_desc_layout; break;
598 case HWSTUB_DT_TARGET: p = &m_desc_target; break;
599 case HWSTUB_DT_JZ: p = &m_desc_jz; break;
600 default: break;
601 }
602 if(p == nullptr)
603 return error::ERROR;
604 /* size is in the bLength field of the descriptor */
605 size_t desc_sz = *(uint8_t *)p;
606 buf_sz = std::min(buf_sz, desc_sz);
607 memcpy(buf, p, buf_sz);
608 return error::SUCCESS;
609}
610
611error jz_handle::get_dev_log(void *buf, size_t& buf_sz)
612{
613 (void) buf;
614 buf_sz = 0;
615 return error::SUCCESS;
616}
617
618error jz_handle::exec_dev(uint32_t addr, uint16_t flags)
619{
620 (void) flags;
621 /* FIXME the ROM always do call so the stub can always return, this behaviour
622 * cannot be changed */
623 /* NOTE assume that exec at 0x80000000 is a first stage load with START1,
624 * otherwise flush cache and use START2 */
625 if(addr == 0x80000000)
626 return jz_start1(addr);
627 error ret = jz_flush_caches();
628 if(ret == error::SUCCESS)
629 return jz_start2(addr);
630 else
631 return ret;
632}
633
634error jz_handle::read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic)
635{
636 (void) atomic;
637 /* NOTE disassembly shows that the ROM will do atomic read on aligned words */
638 error ret = jz_set_addr(addr);
639 if(ret == error::SUCCESS)
640 ret = jz_set_length(sz);
641 if(ret == error::SUCCESS)
642 ret = jz_upload(buf, sz);
643 return ret;
644}
645
646error jz_handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic)
647{
648 (void) atomic;
649 /* NOTE disassembly shows that the ROM will do atomic read on aligned words */
650 /* IMPORTANT BUG Despite what the manual suggest, one must absolutely NOT send
651 * a VR_SET_DATA_LENGTH request for a write, otherwise it will have completely
652 * random effects */
653 error ret = jz_set_addr(addr);
654 if(ret == error::SUCCESS)
655 ret = jz_download(buf, sz);
656 return ret;
657}
658
659bool jz_handle::is_boot_dev(struct libusb_device_descriptor *dev,
660 struct libusb_config_descriptor *config)
661{
662 (void) config;
663 /* don't bother checking the config descriptor and use the device ID only */
664 return dev->idVendor == 0x601a && dev->idProduct >= 0x4740 && dev->idProduct <= 0x4780;
665}
666
667error jz_handle::jz_cpuinfo(char cpuinfo[8])
668{
669 return interpret_libusb_error(libusb_control_transfer(m_handle,
670 LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
671 VR_GET_CPU_INFO, 0, 0, (unsigned char *)cpuinfo, 8, m_timeout), 8);
672}
673
674error jz_handle::jz_set_addr(uint32_t addr)
675{
676 return interpret_libusb_error(libusb_control_transfer(m_handle,
677 LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
678 VR_SET_DATA_ADDRESS, addr >> 16, addr & 0xffff, NULL, 0, m_timeout), 0);
679}
680
681error jz_handle::jz_set_length(uint32_t size)
682{
683 return interpret_libusb_error(libusb_control_transfer(m_handle,
684 LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
685 VR_SET_DATA_LENGTH, size >> 16, size & 0xffff, NULL, 0, m_timeout), 0);
686}
687
688error jz_handle::jz_upload(void *data, size_t& length)
689{
690 int xfer = 0;
691 error err = interpret_libusb_error(libusb_bulk_transfer(m_handle,
692 LIBUSB_ENDPOINT_IN | 1, (unsigned char *)data, length, &xfer, m_timeout));
693 length = xfer;
694 return err;
695}
696
697error jz_handle::jz_download(const void *data, size_t& length)
698{
699 int xfer = 0;
700 error err = interpret_libusb_error(libusb_bulk_transfer(m_handle,
701 LIBUSB_ENDPOINT_OUT | 1, (unsigned char *)data, length, &xfer, m_timeout));
702 length = xfer;
703 return err;
704}
705
706error jz_handle::jz_start1(uint32_t addr)
707{
708 return interpret_libusb_error(libusb_control_transfer(m_handle,
709 LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
710 VR_PROGRAM_START1, addr >> 16, addr & 0xffff, NULL, 0, m_timeout), 0);
711}
712
713error jz_handle::jz_flush_caches()
714{
715 return interpret_libusb_error(libusb_control_transfer(m_handle,
716 LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
717 VR_FLUSH_CACHES, 0, 0, NULL, 0, m_timeout), 0);
718}
719
720error jz_handle::jz_start2(uint32_t addr)
721{
722 return interpret_libusb_error(libusb_control_transfer(m_handle,
723 LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
724 VR_PROGRAM_START2, addr >> 16, addr & 0xffff, NULL, 0, m_timeout), 0);
725}
726
727} // namespace usb
728} // namespace hwstub
diff --git a/utils/hwstub/lib/hwstub_virtual.cpp b/utils/hwstub/lib/hwstub_virtual.cpp
new file mode 100644
index 0000000000..fada56fb83
--- /dev/null
+++ b/utils/hwstub/lib/hwstub_virtual.cpp
@@ -0,0 +1,335 @@
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#include "hwstub_virtual.hpp"
22#include <algorithm>
23#include <cstring>
24
25namespace hwstub {
26namespace virt {
27
28/**
29 * Context
30 */
31context::context()
32{
33}
34
35context::~context()
36{
37}
38
39std::shared_ptr<context> context::create()
40{
41 // NOTE: can't use make_shared() because of the protected ctor */
42 return std::shared_ptr<context>(new context());
43}
44
45std::shared_ptr<context> context::create_spec(const std::string& spec, std::string *error)
46{
47 std::shared_ptr<context> ctx = create();
48 /* parse spec */
49 std::string str = spec;
50 while(!str.empty())
51 {
52 /* find next separator, if any */
53 std::string dev_spec;
54 size_t dev_sep = str.find(';');
55 if(dev_sep == std::string::npos)
56 {
57 dev_spec = str;
58 str.clear();
59 }
60 else
61 {
62 dev_spec = str.substr(0, dev_sep);
63 str = str.substr(dev_sep + 1);
64 }
65 /* handle dev spec: find ( and )*/
66 size_t lparen = dev_spec.find('(');
67 if(lparen == std::string::npos)
68 {
69 if(error)
70 *error = "invalid device spec '" + dev_spec + "': missing (";
71 return std::shared_ptr<context>();
72 }
73 if(dev_spec.back() != ')')
74 {
75 if(error)
76 *error = "invalid device spec '" + dev_spec + "': missing )";
77 return std::shared_ptr<context>();
78 }
79 std::string args = dev_spec.substr(lparen + 1, dev_spec.size() - lparen - 2);
80 std::string type = dev_spec.substr(0, lparen);
81 if(type == "dummy")
82 {
83 ctx->connect(std::make_shared<dummy_hardware>(args));
84 }
85 else
86 {
87 if(error)
88 *error = "invalid device spec '" + dev_spec + "': unknown device type";
89 return std::shared_ptr<context>();
90 }
91 }
92 return ctx;
93}
94
95bool context::connect(std::shared_ptr<hardware> hw)
96{
97 std::unique_lock<std::recursive_mutex> lock(m_mutex);
98 if(std::find(m_hwlist.begin(), m_hwlist.end(), hw) != m_hwlist.end())
99 return false;
100 m_hwlist.push_back(hw);
101 return true;
102}
103
104bool context::disconnect(std::shared_ptr<hardware> hw)
105{
106 std::unique_lock<std::recursive_mutex> lock(m_mutex);
107 auto it = std::find(m_hwlist.begin(), m_hwlist.end(), hw);
108 if(it == m_hwlist.end())
109 return false;
110 m_hwlist.erase(it);
111 return true;
112}
113
114std::shared_ptr<hardware> context::from_ctx_dev(ctx_dev_t dev)
115{
116 return ((hardware *)dev)->shared_from_this();
117}
118
119hwstub::context::ctx_dev_t context::to_ctx_dev(std::shared_ptr<hardware>& dev)
120{
121 return (ctx_dev_t)dev.get();
122}
123
124error context::fetch_device_list(std::vector<ctx_dev_t>& list, void*& ptr)
125{
126 (void) ptr;
127 list.resize(m_hwlist.size());
128 for(size_t i = 0; i < m_hwlist.size(); i++)
129 list[i] = to_ctx_dev(m_hwlist[i]);
130 return error::SUCCESS;
131}
132
133void context::destroy_device_list(void *ptr)
134{
135 (void) ptr;
136}
137
138error context::create_device(ctx_dev_t dev, std::shared_ptr<hwstub::device>& hwdev)
139{
140 // NOTE: can't use make_shared() because of the protected ctor */
141 hwdev.reset(new device(shared_from_this(), from_ctx_dev(dev)));
142 return error::SUCCESS;
143}
144
145bool context::match_device(ctx_dev_t dev, std::shared_ptr<hwstub::device> hwdev)
146{
147 device *udev = dynamic_cast<device*>(hwdev.get());
148 return udev != nullptr && udev->native_device().get() == from_ctx_dev(dev).get();
149}
150
151/**
152 * Device
153 */
154device::device(std::shared_ptr<hwstub::context> ctx, std::shared_ptr<hardware> dev)
155 :hwstub::device(ctx), m_hwdev(dev)
156{
157}
158
159device::~device()
160{
161}
162
163std::shared_ptr<hardware> device::native_device()
164{
165 return m_hwdev.lock();
166}
167
168error device::open_dev(std::shared_ptr<hwstub::handle>& h)
169{
170 // NOTE: can't use make_shared() because of the protected ctor */
171 h.reset(new handle(shared_from_this()));
172 return error::SUCCESS;
173}
174
175bool device::has_multiple_open() const
176{
177 return true;
178}
179
180/**
181 * Handle
182 */
183handle::handle(std::shared_ptr<hwstub::device> dev)
184 :hwstub::handle(dev)
185{
186 m_hwdev = dynamic_cast<device*>(dev.get())->native_device();
187}
188
189handle::~handle()
190{
191}
192
193
194error handle::read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic)
195{
196 auto p = m_hwdev.lock();
197 return p ? p->read_dev(addr, buf, sz, atomic) : error::DISCONNECTED;
198}
199
200error handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic)
201{
202 auto p = m_hwdev.lock();
203 return p ? p->write_dev(addr, buf, sz, atomic) : error::DISCONNECTED;
204}
205
206error handle::get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz)
207{
208 auto p = m_hwdev.lock();
209 return p ? p->get_dev_desc(desc, buf, buf_sz) : error::DISCONNECTED;
210}
211
212error handle::get_dev_log(void *buf, size_t& buf_sz)
213{
214 auto p = m_hwdev.lock();
215 return p ? p->get_dev_log(buf, buf_sz) : error::DISCONNECTED;
216}
217
218error handle::exec_dev(uint32_t addr, uint16_t flags)
219{
220 auto p = m_hwdev.lock();
221 return p ? p->exec_dev(addr, flags) : error::DISCONNECTED;
222}
223
224error handle::status() const
225{
226 return hwstub::handle::status();
227}
228
229size_t handle::get_buffer_size()
230{
231 auto p = m_hwdev.lock();
232 return p ? p->get_buffer_size() : 1;
233}
234
235/**
236 * Hardware
237 */
238hardware::hardware()
239{
240}
241
242hardware::~hardware()
243{
244}
245
246/**
247 * Dummy hardware
248 */
249dummy_hardware::dummy_hardware(const std::string& name)
250{
251 m_desc_version.bLength = sizeof(m_desc_version);
252 m_desc_version.bDescriptorType = HWSTUB_DT_VERSION;
253 m_desc_version.bMajor = HWSTUB_VERSION_MAJOR;
254 m_desc_version.bMinor = HWSTUB_VERSION_MINOR;
255 m_desc_version.bRevision = 0;
256
257 m_desc_layout.bLength = sizeof(m_desc_layout);
258 m_desc_layout.bDescriptorType = HWSTUB_DT_LAYOUT;
259 m_desc_layout.dCodeStart = 0;
260 m_desc_layout.dCodeSize = 0;
261 m_desc_layout.dStackStart = 0;
262 m_desc_layout.dStackSize = 0;
263 m_desc_layout.dBufferStart = 0;
264 m_desc_layout.dBufferSize = 1;
265
266 m_desc_target.bLength = sizeof(m_desc_target);
267 m_desc_target.bDescriptorType = HWSTUB_DT_TARGET;
268 m_desc_target.dID = HWSTUB_TARGET_UNK;
269 strncpy(m_desc_target.bName, name.c_str(), sizeof(m_desc_target.bName));
270 m_desc_target.bName[sizeof(m_desc_target.bName) - 1] = 0;
271}
272
273dummy_hardware::~dummy_hardware()
274{
275}
276
277error dummy_hardware::read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic)
278{
279 (void) addr;
280 (void) buf;
281 (void) sz;
282 (void) atomic;
283 return error::DUMMY;
284}
285
286error dummy_hardware::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic)
287{
288 (void) addr;
289 (void) buf;
290 (void) sz;
291 (void) atomic;
292 return error::DUMMY;
293}
294
295error dummy_hardware::get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz)
296{
297 void *p = nullptr;
298 switch(desc)
299 {
300 case HWSTUB_DT_VERSION: p = &m_desc_version; break;
301 case HWSTUB_DT_LAYOUT: p = &m_desc_layout; break;
302 case HWSTUB_DT_TARGET: p = &m_desc_target; break;
303 default: break;
304 }
305 if(p == nullptr)
306 return error::ERROR;
307 /* size is in the bLength field of the descriptor */
308 size_t desc_sz = *(uint8_t *)p;
309 buf_sz = std::min(buf_sz, desc_sz);
310 memcpy(buf, p, buf_sz);
311 return error::SUCCESS;
312}
313
314error dummy_hardware::get_dev_log(void *buf, size_t& buf_sz)
315{
316 (void) buf;
317 (void) buf_sz;
318 return error::DUMMY;
319}
320
321error dummy_hardware::exec_dev(uint32_t addr, uint16_t flags)
322{
323 (void) addr;
324 (void) flags;
325 return error::DUMMY;
326}
327
328size_t dummy_hardware::get_buffer_size()
329{
330 return 1;
331}
332
333} // namespace virt
334} // namespace net
335