summaryrefslogtreecommitdiff
path: root/utils/hwstub/lib/hwstub.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/hwstub/lib/hwstub.cpp')
-rw-r--r--utils/hwstub/lib/hwstub.cpp627
1 files changed, 627 insertions, 0 deletions
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