fix 'ret' have not initialized
[mfgtools.git] / libuuu / usbhotplug.cpp
blob48a0fc539f81f584901b9cea3e2ef1e90bddf5e2
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 "rominfo.h"
50 #include "vector"
51 #include <time.h>
52 #include <iostream>
54 using chrono::milliseconds;
55 using chrono::operator ""ms;
56 using chrono::seconds;
57 using chrono::operator ""s;
59 static atomic<seconds> g_wait_usb_timeout{-1s};
60 static atomic<milliseconds> g_usb_poll_period{200ms};
61 static atomic<seconds> g_wait_next_usb_timeout{-1s};
63 enum KnownDeviceState {
64 NoKnownDevice,
65 KnownDeviceToDo,
66 KnownDeviceDone,
67 WaitNextKnownDevice,
69 static atomic<KnownDeviceState> g_known_device_state{NoKnownDevice};
71 class CAutoDeInit
73 public:
74 CAutoDeInit()
76 if (libusb_init(nullptr) < 0)
77 throw runtime_error{ "Call libusb_init failure" };
79 ~CAutoDeInit()
81 libusb_exit(nullptr);
83 } g_autoDeInit;
85 class CAutoList
87 public:
88 libusb_device **list = nullptr;
90 CAutoList(libusb_device **list)
92 this->list = list;
93 m_rc = -1;
96 CAutoList(CAutoList &&other)
98 this->list = other.list;
99 this->m_rc = other.m_rc;
100 other.list = nullptr;
103 CAutoList()
105 m_rc = libusb_get_device_list(nullptr, &list);
106 if (m_rc < 0) {
107 set_last_err_string(std::string("libusb_get_device_list failed: ") +
108 libusb_strerror(static_cast<libusb_error>(m_rc)));
112 ~CAutoList()
114 if (list != nullptr) {
115 libusb_free_device_list(list, 1);
119 CAutoList& operator=(CAutoList &&other)
121 this->list = other.list;
122 this->m_rc = other.m_rc;
123 other.list = nullptr;
124 return *this;
127 CAutoList& operator=(const CAutoList&) = delete; // Prevent copy, allow move only
128 CAutoList(const CAutoList&) = delete; // Prevent copy, allow move only
130 bool good() const
132 return m_rc >= 0;
135 private:
136 int m_rc = 0;
139 struct filter {
140 vector<string> list;
141 mutex lock;
143 void push_back(string filter)
145 lock_guard<mutex> guard{lock};
146 list.emplace_back(std::move(filter));
150 static struct: public filter {
151 bool is_valid(const string& path)
153 lock_guard<mutex> guard{lock};
154 if (list.empty())
155 return true;
157 auto end = list.end();
158 auto pos = find(list.begin(), end, path);
159 return pos != end;
161 } g_filter_usbpath;
164 static struct: public filter {
165 bool is_valid(const string& serial_no)
167 lock_guard<mutex> guard{lock};
168 if (list.empty())
169 return true;
171 if (serial_no.empty())
172 return false;
174 for(auto it: list) {
175 if (compare_str(serial_no.substr(0, it.length()), it, true))
176 return true;
179 return false;
181 } g_filter_usbserial_no;
183 struct Timer
185 using Clock = chrono::steady_clock;
186 Clock::time_point start;
188 explicit Timer(Clock::time_point start) : start{start} {}
189 Timer() : Timer{Clock::now()} {}
191 bool is_elapsed(Clock::duration interval) const
193 return (Clock::now() - start) >= interval;
196 void reset(Clock::time_point start)
198 this->start = start;
201 void reset()
203 reset(Clock::now());
207 #ifdef _MSC_VER
208 #define TRY_SUDO
209 #else
210 #define TRY_SUDO ",Try sudo uuu"
211 #endif
213 static string get_device_path(libusb_device *dev)
215 uint8_t path[8];
217 int bus = libusb_get_bus_number(dev);
219 string_ex str;
221 str.format("%d:", bus);
223 int ret = libusb_get_port_numbers(dev, path, sizeof(path));
224 if (ret < 0)
225 return "";
227 string_ex s;
228 s.format("%d", path[0]);
229 str.append(s);
231 for (int j = 1; j < ret; j++)
233 s.format("%d", path[j]);
234 str.append(s);
236 return str;
239 #define SERIAL_NO_MAX 512
241 static string get_device_serial_no(libusb_device *dev, struct libusb_device_descriptor *desc, ConfigItem *item)
243 string serial;
244 struct libusb_device_handle *dev_handle = NULL;
245 int sid = desc->iSerialNumber;
246 int ret = 0;
248 if (!sid) {
249 const ROM_INFO *info= search_rom_info(item);
250 sid = info->serial_idx;
253 serial.resize(SERIAL_NO_MAX);
254 libusb_open(dev, &dev_handle);
255 if (sid && dev_handle)
256 ret = libusb_get_string_descriptor_ascii(dev_handle, sid, (unsigned char*)serial.c_str(), SERIAL_NO_MAX);
257 libusb_close(dev_handle);
258 if(ret >= 0)
259 serial.resize(ret);
261 return str_to_upper(serial);
264 static string get_device_serial_no(libusb_device *dev)
266 string str;
268 struct libusb_device_descriptor desc;
269 int r = libusb_get_device_descriptor(dev, &desc);
270 if (r < 0) {
271 set_last_err_string("failure get device descriptor");
272 return str;
275 ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice);
277 return get_device_serial_no(dev, &desc, item);
280 static int open_libusb(libusb_device *dev, void **usb_device_handle)
282 int retry = 10;
284 while (retry)
286 retry--;
288 /* work around windows open device failure 1/10
289 * sometime HID device detect need some time, refresh list
290 * to make sure HID driver installed.
292 * On linux, udev rules may need some time to kick in,
293 * so also retry on -EACCES.
295 CAutoList l;
297 int ret;
298 if ((ret = libusb_open(dev, (libusb_device_handle **)(usb_device_handle))) < 0)
300 if ((ret != LIBUSB_ERROR_NOT_SUPPORTED && ret != LIBUSB_ERROR_ACCESS)
301 || (retry == 0))
303 set_last_err_string("Failure open usb device" TRY_SUDO);
304 return -1;
306 this_thread::sleep_for(200ms);
308 else
310 return 0;
314 return -1;
318 Thread function. Didn't call this function directly.
319 Unbalance libusb_unref_device.
320 Before start thread, need call libusb_ref_device to dev is free
322 libusb_get_list()
323 libusb_ref_device // avoid free at libusb_free_list if run_usb_cmd have not open device in time.
324 thread start run_usb_cmds;
325 libusb_free_list()
327 static int run_usb_cmds(ConfigItem *item, libusb_device *dev, short bcddevice)
329 int ret;
330 uuu_notify nt;
331 nt.type = uuu_notify::NOTIFY_DEV_ATTACH;
333 string str;
334 str = get_device_path(dev);
335 str += "-";
336 str += get_device_serial_no(dev);
337 nt.str = (char*)str.c_str();
338 call_notify(nt);
340 CmdUsbCtx ctx;
341 ctx.m_config_item = item;
342 ctx.m_current_bcd = bcddevice;
344 if ((ret = open_libusb(dev, &(ctx.m_dev))))
346 nt.type = uuu_notify::NOTIFY_CMD_END;
347 nt.status = -1;
348 call_notify(nt);
349 return ret;
352 ret = run_cmds(item->m_protocol.c_str(), &ctx);
353 g_known_device_state = KnownDeviceDone;
355 nt.type = uuu_notify::NOTIFY_THREAD_EXIT;
356 call_notify(nt);
358 libusb_unref_device(dev); //ref_device when start thread
359 clear_env();
360 return ret;
363 static int usb_add(libusb_device *dev)
365 struct libusb_device_descriptor desc;
366 int r = libusb_get_device_descriptor(dev, &desc);
367 if (r < 0) {
368 set_last_err_string("failure get device descriptor");
369 return r;
372 string str;
373 str = get_device_path(dev);
374 if (!g_filter_usbpath.is_valid(str))
375 return -1;
377 ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice);
379 if (item)
381 string serial = get_device_serial_no(dev, &desc, item);
382 if (!g_filter_usbserial_no.is_valid(serial))
383 return -1;
385 g_known_device_state = KnownDeviceToDo;
388 * start new thread, need increase dev ref number.
389 * otherwise polling thread, free_device_list free device if open device call after free_device_list.
391 libusb_ref_device(dev);
393 std::thread(run_usb_cmds, item, dev, desc.bcdDevice).detach();
395 return 0;
398 static int usb_remove(libusb_device * /*dev*/)
401 return 0;
404 void compare_list(libusb_device ** old, libusb_device **nw)
406 libusb_device * dev;
407 int i = 0;
409 if (old == nullptr)
411 while ((dev = nw[i++]) != nullptr)
413 usb_add(dev);
415 return;
418 while ((dev = nw[i++]) != nullptr)
420 libusb_device * p;
421 int j = 0;
422 while ((p = old[j++]) != nullptr)
424 if (p == dev)
425 break;//find it.
427 if (p != dev)
428 usb_add(dev);
431 i = 0;
432 while ((dev = old[i++]) != nullptr)
434 libusb_device * p;
435 int j = 0;
436 while ((p = nw[j++]) != nullptr)
438 if (p == dev)
439 break;//find it.
441 if (p != dev)
442 usb_remove(dev);
446 static int check_usb_timeout(Timer& usb_timer)
448 auto known_device_state = g_known_device_state.load();
449 if (known_device_state == KnownDeviceDone)
451 g_known_device_state = known_device_state = WaitNextKnownDevice;
452 usb_timer.reset();
455 auto usb_timeout = g_wait_usb_timeout.load();
456 if (usb_timeout >= 0s && known_device_state == NoKnownDevice)
458 if (usb_timer.is_elapsed(usb_timeout))
460 set_last_err_string("Timeout: Wait for Known USB Device");
461 return -1;
465 usb_timeout = g_wait_next_usb_timeout.load();
466 if (usb_timeout >= 0s && g_known_device_state == WaitNextKnownDevice)
468 if (usb_timer.is_elapsed(usb_timeout))
470 set_last_err_string("Timeout: Wait for next USB Device");
471 return -1;
475 return 0;
478 int polling_usb(std::atomic<int>& bexit)
480 if (run_cmds("CFG:", nullptr))
481 return -1;
483 Timer usb_timer;
485 CAutoList oldlist(nullptr);
487 while(!bexit)
489 CAutoList newlist;
490 if (!newlist.good())
492 return -1;
495 compare_list(oldlist.list, newlist.list);
497 std::swap(oldlist, newlist);
499 this_thread::sleep_for(g_usb_poll_period.load());
501 if (check_usb_timeout(usb_timer))
502 return -1;
505 return 0;
508 CmdUsbCtx::~CmdUsbCtx()
510 if (m_dev)
512 libusb_close((libusb_device_handle*)m_dev);
513 m_dev = 0;
517 int CmdUsbCtx::look_for_match_device(const char *pro)
519 if (run_cmds("CFG:", nullptr))
520 return -1;
522 Timer usb_timer;
524 while (1)
526 CAutoList l;
528 if (!l.good()) {
529 break;
532 size_t i = 0;
533 libusb_device *dev;
535 while ((dev = l.list[i++]) != nullptr)
537 struct libusb_device_descriptor desc;
538 int r = libusb_get_device_descriptor(dev, &desc);
539 if (r < 0) {
540 set_last_err_string("failure get device descriptor");
541 return -1;
543 string str = get_device_path(dev);
545 if (!g_filter_usbpath.is_valid(str))
546 continue;
548 ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice);
550 string serial_no = get_device_serial_no(dev, &desc, item);
551 if (!g_filter_usbserial_no.is_valid(serial_no))
552 continue;
554 if (item && item->m_protocol == str_to_upper(pro))
556 uuu_notify nt;
557 nt.type = uuu_notify::NOTIFY_DEV_ATTACH;
558 m_config_item = item;
559 m_current_bcd = desc.bcdDevice;
561 int ret;
562 if ((ret = open_libusb(dev, &(m_dev))))
563 return ret;
565 nt.str = (char*)str.c_str();
566 call_notify(nt);
568 return 0;
572 this_thread::sleep_for(200ms);
574 uuu_notify nt;
575 nt.type = nt.NOTIFY_WAIT_FOR;
576 nt.str = (char*)"Wait for Known USB";
577 call_notify(nt);
579 if (check_usb_timeout(usb_timer))
580 return -1;
583 return -1;
586 int uuu_add_usbpath_filter(const char *path)
588 g_filter_usbpath.push_back(path);
589 return 0;
592 int uuu_add_usbserial_no_filter(const char *serial_no)
594 g_filter_usbserial_no.push_back(serial_no);
595 return 0;
598 int uuu_for_each_devices(uuu_ls_usb_devices fn, void *p)
600 CAutoList l;
601 size_t i = 0;
602 libusb_device *dev;
604 if (!l.good()) {
605 return -1;
608 while ((dev = l.list[i++]) != nullptr)
610 struct libusb_device_descriptor desc;
611 int r = libusb_get_device_descriptor(dev, &desc);
612 if (r < 0) {
613 set_last_err_string("failure get device descriptor");
614 return -1;
616 string str = get_device_path(dev);
618 ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice);
619 if (item)
621 string serial = get_device_serial_no(dev, &desc, item);
622 if (fn(str.c_str(), item->m_chip.c_str(), item->m_protocol.c_str(), desc.idVendor, desc.idProduct, desc.bcdDevice, serial.c_str(), p))
624 set_last_err_string("call back return error");
625 return -1;
630 return 0;
633 int uuu_set_wait_timeout(int timeout_in_seconds)
635 g_wait_usb_timeout = seconds{timeout_in_seconds};
636 return 0;
639 void uuu_set_poll_period(int period_in_milliseconds)
641 g_usb_poll_period = milliseconds{period_in_milliseconds};
644 int uuu_set_wait_next_timeout(int timeout_in_seconds)
646 g_wait_next_usb_timeout = seconds{timeout_in_seconds};
647 return 0;