Update README.md
[mfgtools.git] / libuuu / usbhotplug.cpp
blob48607c6fdedcf8385745dac66ebe7a0306e1453e
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 if (info)
251 sid = info->serial_idx;
254 serial.resize(SERIAL_NO_MAX);
255 libusb_open(dev, &dev_handle);
256 if (sid && dev_handle)
257 ret = libusb_get_string_descriptor_ascii(dev_handle, sid, (unsigned char*)serial.c_str(), SERIAL_NO_MAX);
258 libusb_close(dev_handle);
259 if(ret >= 0)
260 serial.resize(ret);
262 return str_to_upper(serial);
265 static string get_device_serial_no(libusb_device *dev)
267 string str;
269 struct libusb_device_descriptor desc;
270 int r = libusb_get_device_descriptor(dev, &desc);
271 if (r < 0) {
272 set_last_err_string("failure get device descriptor");
273 return str;
276 ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice);
278 return get_device_serial_no(dev, &desc, item);
281 static int open_libusb(libusb_device *dev, void **usb_device_handle)
283 int retry = 10;
285 while (retry)
287 retry--;
289 /* work around windows open device failure 1/10
290 * sometime HID device detect need some time, refresh list
291 * to make sure HID driver installed.
293 * On linux, udev rules may need some time to kick in,
294 * so also retry on -EACCES.
296 CAutoList l;
298 int ret;
299 if ((ret = libusb_open(dev, (libusb_device_handle **)(usb_device_handle))) < 0)
301 if ((ret != LIBUSB_ERROR_NOT_SUPPORTED && ret != LIBUSB_ERROR_ACCESS)
302 || (retry == 0))
304 set_last_err_string("Failure open usb device" TRY_SUDO);
305 return -1;
307 this_thread::sleep_for(200ms);
309 else
311 return 0;
315 return -1;
319 Thread function. Didn't call this function directly.
320 Unbalance libusb_unref_device.
321 Before start thread, need call libusb_ref_device to dev is free
323 libusb_get_list()
324 libusb_ref_device // avoid free at libusb_free_list if run_usb_cmd have not open device in time.
325 thread start run_usb_cmds;
326 libusb_free_list()
328 static int run_usb_cmds(ConfigItem *item, libusb_device *dev, short bcddevice)
330 int ret;
331 uuu_notify nt;
332 nt.type = uuu_notify::NOTIFY_DEV_ATTACH;
334 string str;
335 str = get_device_path(dev);
336 str += "-";
337 str += get_device_serial_no(dev);
338 nt.str = (char*)str.c_str();
339 call_notify(nt);
341 CmdUsbCtx ctx;
342 ctx.m_config_item = item;
343 ctx.m_current_bcd = bcddevice;
345 if ((ret = open_libusb(dev, &(ctx.m_dev))))
347 nt.type = uuu_notify::NOTIFY_CMD_END;
348 nt.status = -1;
349 call_notify(nt);
350 return ret;
353 ret = run_cmds(item->m_protocol.c_str(), &ctx);
354 g_known_device_state = KnownDeviceDone;
356 nt.type = uuu_notify::NOTIFY_THREAD_EXIT;
357 call_notify(nt);
359 libusb_unref_device(dev); //ref_device when start thread
360 clear_env();
361 return ret;
364 static int usb_add(libusb_device *dev)
366 struct libusb_device_descriptor desc;
367 int r = libusb_get_device_descriptor(dev, &desc);
368 if (r < 0) {
369 set_last_err_string("failure get device descriptor");
370 return r;
373 string str;
374 str = get_device_path(dev);
375 if (!g_filter_usbpath.is_valid(str))
376 return -1;
378 ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice);
380 if (item)
382 string serial = get_device_serial_no(dev, &desc, item);
383 if (!g_filter_usbserial_no.is_valid(serial))
384 return -1;
386 g_known_device_state = KnownDeviceToDo;
389 * start new thread, need increase dev ref number.
390 * otherwise polling thread, free_device_list free device if open device call after free_device_list.
392 libusb_ref_device(dev);
394 std::thread(run_usb_cmds, item, dev, desc.bcdDevice).detach();
396 return 0;
399 static int usb_remove(libusb_device * /*dev*/)
402 return 0;
405 void compare_list(libusb_device ** old, libusb_device **nw)
407 libusb_device * dev;
408 int i = 0;
410 if (old == nullptr)
412 while ((dev = nw[i++]) != nullptr)
414 usb_add(dev);
416 return;
419 while ((dev = nw[i++]) != nullptr)
421 libusb_device * p;
422 int j = 0;
423 while ((p = old[j++]) != nullptr)
425 if (p == dev)
426 break;//find it.
428 if (p != dev)
429 usb_add(dev);
432 i = 0;
433 while ((dev = old[i++]) != nullptr)
435 libusb_device * p;
436 int j = 0;
437 while ((p = nw[j++]) != nullptr)
439 if (p == dev)
440 break;//find it.
442 if (p != dev)
443 usb_remove(dev);
447 static int check_usb_timeout(Timer& usb_timer)
449 auto known_device_state = g_known_device_state.load();
450 if (known_device_state == KnownDeviceDone)
452 g_known_device_state = known_device_state = WaitNextKnownDevice;
453 usb_timer.reset();
456 auto usb_timeout = g_wait_usb_timeout.load();
457 if (usb_timeout >= 0s && known_device_state == NoKnownDevice)
459 if (usb_timer.is_elapsed(usb_timeout))
461 set_last_err_string("Timeout: Wait for Known USB Device");
462 return -1;
466 usb_timeout = g_wait_next_usb_timeout.load();
467 if (usb_timeout >= 0s && g_known_device_state == WaitNextKnownDevice)
469 if (usb_timer.is_elapsed(usb_timeout))
471 set_last_err_string("Timeout: Wait for next USB Device");
472 return -1;
476 return 0;
479 int polling_usb(std::atomic<int>& bexit)
481 if (run_cmds("CFG:", nullptr))
482 return -1;
484 Timer usb_timer;
486 CAutoList oldlist(nullptr);
488 while(!bexit)
490 CAutoList newlist;
491 if (!newlist.good())
493 return -1;
496 compare_list(oldlist.list, newlist.list);
498 std::swap(oldlist, newlist);
500 this_thread::sleep_for(g_usb_poll_period.load());
502 if (check_usb_timeout(usb_timer))
503 return -1;
506 return 0;
509 CmdUsbCtx::~CmdUsbCtx()
511 if (m_dev)
513 libusb_close((libusb_device_handle*)m_dev);
514 m_dev = 0;
518 int CmdUsbCtx::look_for_match_device(const char *pro)
520 if (run_cmds("CFG:", nullptr))
521 return -1;
523 Timer usb_timer;
525 while (1)
527 CAutoList l;
529 if (!l.good()) {
530 break;
533 size_t i = 0;
534 libusb_device *dev;
536 while ((dev = l.list[i++]) != nullptr)
538 struct libusb_device_descriptor desc;
539 int r = libusb_get_device_descriptor(dev, &desc);
540 if (r < 0) {
541 set_last_err_string("failure get device descriptor");
542 return -1;
544 string str = get_device_path(dev);
546 if (!g_filter_usbpath.is_valid(str))
547 continue;
549 ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice);
551 string serial_no = get_device_serial_no(dev, &desc, item);
552 if (!g_filter_usbserial_no.is_valid(serial_no))
553 continue;
555 if (item && item->m_protocol == str_to_upper(pro))
557 uuu_notify nt;
558 nt.type = uuu_notify::NOTIFY_DEV_ATTACH;
559 m_config_item = item;
560 m_current_bcd = desc.bcdDevice;
562 int ret;
563 if ((ret = open_libusb(dev, &(m_dev))))
564 return ret;
566 nt.str = (char*)str.c_str();
567 call_notify(nt);
569 return 0;
573 this_thread::sleep_for(200ms);
575 uuu_notify nt;
576 nt.type = nt.NOTIFY_WAIT_FOR;
577 nt.str = (char*)"Wait for Known USB";
578 call_notify(nt);
580 if (check_usb_timeout(usb_timer))
581 return -1;
584 return -1;
587 int uuu_add_usbpath_filter(const char *path)
589 g_filter_usbpath.push_back(path);
590 return 0;
593 int uuu_add_usbserial_no_filter(const char *serial_no)
595 g_filter_usbserial_no.push_back(serial_no);
596 return 0;
599 int uuu_for_each_devices(uuu_ls_usb_devices fn, void *p)
601 CAutoList l;
602 size_t i = 0;
603 libusb_device *dev;
605 if (!l.good()) {
606 return -1;
609 while ((dev = l.list[i++]) != nullptr)
611 struct libusb_device_descriptor desc;
612 int r = libusb_get_device_descriptor(dev, &desc);
613 if (r < 0) {
614 set_last_err_string("failure get device descriptor");
615 return -1;
617 string str = get_device_path(dev);
619 ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice);
620 if (item)
622 string serial = get_device_serial_no(dev, &desc, item);
623 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))
625 set_last_err_string("call back return error");
626 return -1;
631 return 0;
634 int uuu_set_wait_timeout(int timeout_in_seconds)
636 g_wait_usb_timeout = seconds{timeout_in_seconds};
637 return 0;
640 void uuu_set_poll_period(int period_in_milliseconds)
642 g_usb_poll_period = milliseconds{period_in_milliseconds};
645 int uuu_set_wait_next_timeout(int timeout_in_seconds)
647 g_wait_next_usb_timeout = seconds{timeout_in_seconds};
648 return 0;