use ref_name as release name
[mfgtools.git] / libuuu / usbhotplug.cpp
blobdd2f6d8d552b8d3bdd4c1e23f41a36755a7a97e5
1 /*
2 * Copyright 2018 NXP.
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright notice, this
11 * list of conditions and the following disclaimer in the documentation and/or
12 * other materials provided with the distribution.
14 * Neither the name of the NXP Semiconductor nor the names of its
15 * contributors may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
33 * Windows libusb don't support hotplug yet
34 * Will polling devices list every 100ms
37 #include <thread>
38 #include <atomic>
39 #include <mutex>
40 #include <utility>
41 #include <algorithm>
42 #include <stdexcept>
43 #include "libusb.h"
44 #include "liberror.h"
45 #include "config.h"
46 #include "cmd.h"
47 #include "libcomm.h"
48 #include "libuuu.h"
49 #include "vector"
50 #include <time.h>
52 using chrono::milliseconds;
53 using chrono::operator ""ms;
54 using chrono::seconds;
55 using chrono::operator ""s;
57 static atomic<seconds> g_wait_usb_timeout{-1s};
58 static atomic<milliseconds> g_usb_poll_period{200ms};
59 static atomic<seconds> g_wait_next_usb_timeout{-1s};
61 enum KnownDeviceState {
62 NoKnownDevice,
63 KnownDeviceToDo,
64 KnownDeviceDone,
65 WaitNextKnownDevice,
67 static atomic<KnownDeviceState> g_known_device_state{NoKnownDevice};
69 class CAutoDeInit
71 public:
72 CAutoDeInit()
74 if (libusb_init(nullptr) < 0)
75 throw runtime_error{ "Call libusb_init failure" };
77 ~CAutoDeInit()
79 libusb_exit(nullptr);
81 } g_autoDeInit;
83 class CAutoList
85 public:
86 libusb_device **list = nullptr;
88 CAutoList(libusb_device **list)
90 this->list = list;
91 m_rc = -1;
94 CAutoList(CAutoList &&other)
96 this->list = other.list;
97 this->m_rc = other.m_rc;
98 other.list = nullptr;
101 CAutoList()
103 m_rc = libusb_get_device_list(nullptr, &list);
104 if (m_rc < 0) {
105 set_last_err_string(std::string("libusb_get_device_list failed: ") +
106 libusb_strerror(static_cast<libusb_error>(m_rc)));
110 ~CAutoList()
112 if (list != nullptr) {
113 libusb_free_device_list(list, 1);
117 CAutoList& operator=(CAutoList &&other)
119 this->list = other.list;
120 this->m_rc = other.m_rc;
121 other.list = nullptr;
122 return *this;
125 CAutoList& operator=(const CAutoList&) = delete; // Prevent copy, allow move only
126 CAutoList(const CAutoList&) = delete; // Prevent copy, allow move only
128 bool good() const
130 return m_rc >= 0;
133 private:
134 int m_rc = 0;
137 static struct {
138 vector<string> list;
139 mutex lock;
141 void push_back(string filter)
143 lock_guard<mutex> guard{lock};
144 list.emplace_back(std::move(filter));
147 bool is_valid(const string& path)
149 lock_guard<mutex> guard{lock};
150 if (list.empty())
151 return true;
153 auto end = list.end();
154 auto pos = find(list.begin(), end, path);
155 return pos != end;
157 } g_filter_usbpath;
159 struct Timer
161 using Clock = chrono::steady_clock;
162 Clock::time_point start;
164 explicit Timer(Clock::time_point start) : start{start} {}
165 Timer() : Timer{Clock::now()} {}
167 bool is_elapsed(Clock::duration interval) const
169 return (Clock::now() - start) >= interval;
172 void reset(Clock::time_point start)
174 this->start = start;
177 void reset()
179 reset(Clock::now());
183 #ifdef _MSC_VER
184 #define TRY_SUDO
185 #else
186 #define TRY_SUDO ",Try sudo uuu"
187 #endif
189 static string get_device_path(libusb_device *dev)
191 uint8_t path[8];
193 int bus = libusb_get_bus_number(dev);
195 string_ex str;
197 str.format("%d:", bus);
199 int ret = libusb_get_port_numbers(dev, path, sizeof(path));
200 if (ret < 0)
201 return "";
203 string_ex s;
204 s.format("%d", path[0]);
205 str.append(s);
207 for (int j = 1; j < ret; j++)
209 s.format("%d", path[j]);
210 str.append(s);
212 return str;
215 static int open_libusb(libusb_device *dev, void **usb_device_handle)
217 int retry = 1;
218 #ifdef WIN32
219 retry = 5;
220 #endif
222 while (retry)
224 retry--;
226 /* work around windows open device failure 1/10
227 * sometime HID device detect need some time, refresh list
228 * to make sure HID driver installed.
230 CAutoList l;
232 int ret;
233 if ((ret = libusb_open(dev, (libusb_device_handle **)(usb_device_handle))) < 0)
235 if ((ret != LIBUSB_ERROR_NOT_SUPPORTED) || (retry == 0))
237 set_last_err_string("Failure open usb device" TRY_SUDO);
238 return -1;
240 this_thread::sleep_for(200ms);
242 else
244 return 0;
248 return -1;
252 Thread function. Didn't call this function directly.
253 Unbalance libusb_unref_device.
254 Before start thread, need call libusb_ref_device to dev is free
256 libusb_get_list()
257 libusb_ref_device // avoid free at libusb_free_list if run_usb_cmd have not open device in time.
258 thread start run_usb_cmds;
259 libusb_free_list()
261 static int run_usb_cmds(ConfigItem *item, libusb_device *dev, short bcddevice)
263 int ret;
264 uuu_notify nt;
265 nt.type = uuu_notify::NOTIFY_DEV_ATTACH;
267 string str;
268 str = get_device_path(dev);
269 nt.str = (char*)str.c_str();
270 call_notify(nt);
272 CmdUsbCtx ctx;
273 ctx.m_config_item = item;
274 ctx.m_current_bcd = bcddevice;
276 if ((ret = open_libusb(dev, &(ctx.m_dev))))
278 nt.type = uuu_notify::NOTIFY_CMD_END;
279 nt.status = -1;
280 call_notify(nt);
281 return ret;
284 ret = run_cmds(item->m_protocol.c_str(), &ctx);
285 g_known_device_state = KnownDeviceDone;
287 nt.type = uuu_notify::NOTIFY_THREAD_EXIT;
288 call_notify(nt);
290 libusb_unref_device(dev); //ref_device when start thread
291 clear_env();
292 return ret;
295 static int usb_add(libusb_device *dev)
297 struct libusb_device_descriptor desc;
298 int r = libusb_get_device_descriptor(dev, &desc);
299 if (r < 0) {
300 set_last_err_string("failure get device descriptor");
301 return r;
304 string str;
305 str = get_device_path(dev);
306 if (!g_filter_usbpath.is_valid(str))
307 return -1;
309 ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice);
311 if (item)
313 g_known_device_state = KnownDeviceToDo;
316 * start new thread, need increase dev ref number.
317 * otherwise polling thread, free_device_list free device if open device call after free_device_list.
319 libusb_ref_device(dev);
321 std::thread(run_usb_cmds, item, dev, desc.bcdDevice).detach();
323 return 0;
326 static int usb_remove(libusb_device * /*dev*/)
329 return 0;
332 void compare_list(libusb_device ** old, libusb_device **nw)
334 libusb_device * dev;
335 int i = 0;
337 if (old == nullptr)
339 while ((dev = nw[i++]) != nullptr)
341 usb_add(dev);
343 return;
346 while ((dev = nw[i++]) != nullptr)
348 libusb_device * p;
349 int j = 0;
350 while ((p = old[j++]) != nullptr)
352 if (p == dev)
353 break;//find it.
355 if (p != dev)
356 usb_add(dev);
359 i = 0;
360 while ((dev = old[i++]) != nullptr)
362 libusb_device * p;
363 int j = 0;
364 while ((p = nw[j++]) != nullptr)
366 if (p == dev)
367 break;//find it.
369 if (p != dev)
370 usb_remove(dev);
374 static int check_usb_timeout(Timer& usb_timer)
376 auto known_device_state = g_known_device_state.load();
377 if (known_device_state == KnownDeviceDone)
379 g_known_device_state = known_device_state = WaitNextKnownDevice;
380 usb_timer.reset();
383 auto usb_timeout = g_wait_usb_timeout.load();
384 if (usb_timeout >= 0s && known_device_state == NoKnownDevice)
386 if (usb_timer.is_elapsed(usb_timeout))
388 set_last_err_string("Timeout: Wait for Known USB Device");
389 return -1;
393 usb_timeout = g_wait_next_usb_timeout.load();
394 if (usb_timeout >= 0s && g_known_device_state == WaitNextKnownDevice)
396 if (usb_timer.is_elapsed(usb_timeout))
398 set_last_err_string("Timeout: Wait for next USB Device");
399 return -1;
403 return 0;
406 int polling_usb(std::atomic<int>& bexit)
408 if (run_cmds("CFG:", nullptr))
409 return -1;
411 Timer usb_timer;
413 CAutoList oldlist(nullptr);
415 while(!bexit)
417 CAutoList newlist;
418 if (!newlist.good())
420 return -1;
423 compare_list(oldlist.list, newlist.list);
425 std::swap(oldlist, newlist);
427 this_thread::sleep_for(g_usb_poll_period.load());
429 if (check_usb_timeout(usb_timer))
430 return -1;
433 return 0;
436 CmdUsbCtx::~CmdUsbCtx()
438 if (m_dev)
440 libusb_close((libusb_device_handle*)m_dev);
441 m_dev = 0;
445 int CmdUsbCtx::look_for_match_device(const char *pro)
447 if (run_cmds("CFG:", nullptr))
448 return -1;
450 Timer usb_timer;
452 while (1)
454 CAutoList l;
456 if (!l.good()) {
457 break;
460 size_t i = 0;
461 libusb_device *dev;
463 while ((dev = l.list[i++]) != nullptr)
465 struct libusb_device_descriptor desc;
466 int r = libusb_get_device_descriptor(dev, &desc);
467 if (r < 0) {
468 set_last_err_string("failure get device descriptor");
469 return -1;
471 string str = get_device_path(dev);
473 if (!g_filter_usbpath.is_valid(str))
474 continue;
476 ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice);
477 if (item && item->m_protocol == str_to_upper(pro))
479 uuu_notify nt;
480 nt.type = uuu_notify::NOTIFY_DEV_ATTACH;
481 m_config_item = item;
482 m_current_bcd = desc.bcdDevice;
484 int ret;
485 if ((ret = open_libusb(dev, &(m_dev))))
486 return ret;
488 nt.str = (char*)str.c_str();
489 call_notify(nt);
491 return 0;
495 this_thread::sleep_for(200ms);
497 uuu_notify nt;
498 nt.type = nt.NOTIFY_WAIT_FOR;
499 nt.str = (char*)"Wait for Known USB";
500 call_notify(nt);
502 check_usb_timeout(usb_timer);
505 return -1;
508 int uuu_add_usbpath_filter(const char *path)
510 g_filter_usbpath.push_back(path);
511 return 0;
514 int uuu_for_each_devices(uuu_ls_usb_devices fn, void *p)
516 CAutoList l;
517 size_t i = 0;
518 libusb_device *dev;
520 if (!l.good()) {
521 return -1;
524 while ((dev = l.list[i++]) != nullptr)
526 struct libusb_device_descriptor desc;
527 int r = libusb_get_device_descriptor(dev, &desc);
528 if (r < 0) {
529 set_last_err_string("failure get device descriptor");
530 return -1;
532 string str = get_device_path(dev);
534 ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice);
535 if (item)
537 if (fn(str.c_str(), item->m_chip.c_str(), item->m_protocol.c_str(), desc.idVendor, desc.idProduct, desc.bcdDevice, p))
539 set_last_err_string("call back return error");
540 return -1;
545 return 0;
548 int uuu_set_wait_timeout(int timeout_in_seconds)
550 g_wait_usb_timeout = seconds{timeout_in_seconds};
551 return 0;
554 void uuu_set_poll_period(int period_in_milliseconds)
556 g_usb_poll_period = milliseconds{period_in_milliseconds};
559 int uuu_set_wait_next_timeout(int timeout_in_seconds)
561 g_wait_next_usb_timeout = seconds{timeout_in_seconds};
562 return 0;