scanner.cpp
Go to the documentation of this file.
1 
2 // Copyright (c) 2003-2021 Xsens Technologies B.V. or subsidiaries worldwide.
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice,
9 // this list of conditions, and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice,
12 // this list of conditions, and the following disclaimer in the documentation
13 // and/or other materials provided with the distribution.
14 //
15 // 3. Neither the names of the copyright holders nor the names of their contributors
16 // may be used to endorse or promote products derived from this software without
17 // specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 // THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
24 // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
26 // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.THE LAWS OF THE NETHERLANDS
28 // SHALL BE EXCLUSIVELY APPLICABLE AND ANY DISPUTES SHALL BE FINALLY SETTLED UNDER THE RULES
29 // OF ARBITRATION OF THE INTERNATIONAL CHAMBER OF COMMERCE IN THE HAGUE BY ONE OR MORE
30 // ARBITRATORS APPOINTED IN ACCORDANCE WITH SAID RULES.
31 //
32 
33 
34 // Copyright (c) 2003-2021 Xsens Technologies B.V. or subsidiaries worldwide.
35 // All rights reserved.
36 //
37 // Redistribution and use in source and binary forms, with or without modification,
38 // are permitted provided that the following conditions are met:
39 //
40 // 1. Redistributions of source code must retain the above copyright notice,
41 // this list of conditions, and the following disclaimer.
42 //
43 // 2. Redistributions in binary form must reproduce the above copyright notice,
44 // this list of conditions, and the following disclaimer in the documentation
45 // and/or other materials provided with the distribution.
46 //
47 // 3. Neither the names of the copyright holders nor the names of their contributors
48 // may be used to endorse or promote products derived from this software without
49 // specific prior written permission.
50 //
51 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
52 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
53 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
54 // THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 // SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
56 // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
58 // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
59 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.THE LAWS OF THE NETHERLANDS
60 // SHALL BE EXCLUSIVELY APPLICABLE AND ANY DISPUTES SHALL BE FINALLY SETTLED UNDER THE RULES
61 // OF ARBITRATION OF THE INTERNATIONAL CHAMBER OF COMMERCE IN THE HAGUE BY ONE OR MORE
62 // ARBITRATORS APPOINTED IN ACCORDANCE WITH SAID RULES.
63 //
64 
65 #include "scanner.h"
66 
67 #ifdef _WIN32
68  #include <devguid.h>
69  #include <initguid.h>
70  #include <Usbiodef.h>
71  #include <cfgmgr32.h>
72  #include <regstr.h>
73  #include <regex>
74  #include "idfetchhelpers.h"
75 #else
76  #include <stdlib.h>
77  #include <string.h>
78  #include <dirent.h>
79  #include "xslibusb.h"
80  #include "udev.h"
81 #endif
82 #include <algorithm>
83 
84 #include "serialportcommunicator.h"
85 #include "usbcommunicator.h"
86 #include <iostream>
87 #include <atomic>
89 #include <xstypes/xsbusid.h>
90 #include <xstypes/xsportinfo.h>
91 #include <xstypes/xsintarray.h>
92 #include <xstypes/xsstringarray.h>
93 #include "enumerateusbdevices.h"
94 #include <xscommon/journaller.h>
95 
97 {
98 volatile std::atomic_bool abortPortScan{false};
99 Scanner* gScanner = nullptr;
101 }
102 using namespace XsScannerNamespace;
103 
111 {
112  if (!gScanner)
113  gScanner = new Scanner();
114  return *gScanner;
115 }
116 
123 {
124 }
125 
132 {
133  gScanLogCallback = cb;
134 }
135 
144 XsResultValue Scanner::fetchBasicInfo(XsPortInfo& portInfo, uint32_t singleScanTimeout, bool detectRs485)
145 {
146  auto serial = Communicator::createUniquePtr<SerialPortCommunicator>();
147  auto usb = Communicator::createUniquePtr<UsbCommunicator>();
148 
149  SerialPortCommunicator* port = (portInfo.isUsb() ? usb.get() : serial.get());
150 
151  port->setGotoConfigTimeout(singleScanTimeout);
152  LOGXSSCAN("Opening port " << portInfo.portName() << " (" << portInfo.portNumber() << ") @ " << portInfo.baudrate() << " baud, expected device " << portInfo.deviceId());
153  if (!port->openPort(portInfo, OPS_OpenPort))
154  {
155  LOGXSSCAN("Failed to open port because: " << XsResultValue_toString(port->lastResult()));
156  return port->lastResult();
157  }
158 
159  if (!port->openPort(portInfo, OPS_InitStart, detectRs485))
160  {
161  LOGXSSCAN("Failed to initialize port because: " << XsResultValue_toString(port->lastResult()));
162  if (port->lastResult() < XRV_ERROR)
163  {
164  LOGXSSCAN("Attempting to reset device");
165  // we got an xbus protocol error message, attempt to reset the device and try again (once)
166  XsMessage snd(XMID_Reset);
167  snd.setBusId(XS_BID_MASTER);
168  if (!port->writeMessage(snd))
169  {
170  LOGXSSCAN("Failed to wriite reset to device because: " << XsResultValue_toString(port->lastResult()));
171  return port->lastResult();
172  }
173  LOGXSSCAN("Reopening port after reset");
174  port->closePort();
175  XsTime::msleep(2000);
176  if (!port->openPort(portInfo, OPS_Full, detectRs485))
177  {
178  LOGXSSCAN("Failed to reopen port after reset because: " << XsResultValue_toString(port->lastResult()));
179  return port->lastResult();
180  }
181  }
182  else
183  return port->lastResult();
184  }
185 
186  LOGXSSCAN("Port " << portInfo.portName() << " opened successfully, device is " << port->masterDeviceId());
187  portInfo.setDeviceId(port->masterDeviceId());
188 
189  // Enable flow control for Awinda2 stations/dongles which support this:
190  if (port->masterDeviceId().isAwinda2())
191  {
192  XsPortLinesOptions portLinesOptions = portInfo.linesOptions();
193  XsVersion fwVersion = port->firmwareRevision();
194  XsVersion hwVersion = port->hardwareRevision();
195 
196  if (port->masterDeviceId().isAwinda2Station() && fwVersion.major() != 255 && fwVersion >= XsVersion(4, 2, 1))
197  portLinesOptions = (XsPortLinesOptions)(portLinesOptions | XPLO_RtsCtsFlowControl);
198  else if (port->masterDeviceId().isAwinda2Dongle() && fwVersion.major() != 255 && fwVersion >= XsVersion(4, 3, 2) && hwVersion >= XsVersion(2, 3))
199  portLinesOptions = (XsPortLinesOptions)(portLinesOptions | XPLO_RtsCtsFlowControl);
200  else
201  portLinesOptions = (XsPortLinesOptions)(portLinesOptions & ~XPLO_RtsCtsFlowControl);
202  LOGXSSCAN("Setting flow control options for Awinda device to " << JLHEXLOG(portLinesOptions));
203  portInfo.setLinesOptions(portLinesOptions);
204  }
205 
206  return XRV_OK;
207 }
208 
224 bool Scanner::xsScanPort(XsPortInfo& portInfo, XsBaudRate baud, uint32_t singleScanTimeout, bool detectRs485)
225 {
226  LOGXSSCAN("Scanning port " << portInfo.portName() << " at baudrate " << baud << " with timeout " << singleScanTimeout << " detectRs485 " << detectRs485);
227  XsResultValue res;
228  XsBaudRate baudrate;
229 
230  if (baud == 0)
231  baudrate = XBR_115k2;
232  else
233  baudrate = baud;
234 
235  while (!abortPortScan)
236  {
237  portInfo.setBaudrate(baudrate);
238  res = fetchBasicInfo(portInfo, singleScanTimeout, detectRs485);
239  if (res == XRV_OK)
240  {
241  LOGXSSCAN("Scan successfully found device " << portInfo.deviceId() << " on port " << portInfo.portName());
242  portInfo.setBaudrate(baudrate);
243  return true;
244  }
245 
246  // failed, determine if we need to scan other baudrates or not
247  if (res != XRV_TIMEOUT && res != XRV_TIMEOUTNODATA && res != XRV_CONFIGCHECKFAIL)
248  {
249  LOGXSSCAN("Failed to fetch basic info: " << XsResultValue_toString(res));
250  return false;
251  }
252  LOGXSSCAN("Failed to fetch basic info within timeout");
253 
254  // not detected, try next baudrate
255  if (baud != 0)
256  {
257  LOGXSSCAN("Failed to find device");
258  return false;
259  }
260  switch (baudrate)
261  {
262  default:
263  case XBR_115k2:
264  baudrate = XBR_2000k;
265  break;
266  case XBR_2000k:
267  baudrate = XBR_921k6;
268  break;
269  case XBR_921k6:
270  baudrate = XBR_460k8;
271  break;
272  case XBR_460k8:
273  baudrate = XBR_230k4;
274  break;
275  case XBR_230k4:
276  // On some systems a delay of about 100ms seems necessary to successfully perform the scan.
277  XsTime::msleep(100);
278  baudrate = XBR_57k6;
279  break;
280  case XBR_57k6:
281  baudrate = XBR_38k4;
282  break;
283  case XBR_38k4:
284  baudrate = XBR_19k2;
285  break;
286  case XBR_19k2:
287  baudrate = XBR_9600;
288  break;
289  case XBR_9600:
290  LOGXSSCAN("No more available baudrates, failed to find device");
291  return false; // could not detect Xsens device, return false
292  }
293  LOGXSSCAN("Checking next baudrate: " << baudrate);
294  }
295  LOGXSSCAN("Port scan aborted by external trigger");
296  return false;
297 }
298 
315 bool Scanner::xsScanPorts(XsPortInfoArray& ports, XsBaudRate baudrate, uint32_t singleScanTimeout, bool ignoreNonXsensDevices, bool detectRs485)
316 {
317  ports.clear();
318 
319  if (!xsEnumerateSerialPorts(ports, ignoreNonXsensDevices))
320  return false;
321 
322  if (!xsEnumerateUsbDevices(ports))
323  return false;
324 
325  return xsFilterResponsiveDevices(ports, baudrate, singleScanTimeout, detectRs485);
326 }
327 
339 bool Scanner::xsFilterResponsiveDevices(XsPortInfoArray& ports, XsBaudRate baudrate, uint32_t singleScanTimeout, bool detectRs485)
340 {
341  // try to connect so we can detect if there really is an MT / XM attached
342  unsigned p = 0;
343  while (!abortPortScan && p < ports.size())
344  {
345  if (ports[p].isNetwork() || xsScanPort(ports[p], baudrate, singleScanTimeout, detectRs485))
346  ++p;
347  else
348  {
349  LOGXSSCAN("Port : " << ports[p].portName() << " is not responsive, discarding");
350  ports.erase(ports.begin() + p);
351  }
352  }
353 
354  if (abortPortScan)
355  {
356  abortPortScan = false;
357  return false;
358  }
359 
360  // Now sort the final list by ascending port nr
361  std::sort(ports.begin(), ports.end());
362  abortPortScan = false;
363  return true;
364 }
365 
366 #ifdef _WIN32
367 
371 std::string Scanner::getDevicePath(HDEVINFO hDevInfo, SP_DEVINFO_DATA* DeviceInfoData)
372 {
373  char deviceInstanceID[MAX_DEVICE_ID_LEN];
374  SetupDiGetDeviceInstanceIdA(hDevInfo, DeviceInfoData, deviceInstanceID, MAX_DEVICE_ID_LEN, NULL);
375  return std::string(deviceInstanceID);
376 }
377 #endif
378 
383 bool Scanner::isXsensUsbDevice(uint16_t vid, uint16_t pid)
384 {
385  switch (vid)
386  {
387  // Xsens
388  case XSENS_VENDOR_ID:
389  // ignore the body pack serial port
390  return (pid != 0x0100);
391 
392  // FTDI reserved PIDs
393  case FTDI_VENDOR_ID:
394  return (pid >= 0xd388 && pid <= 0xd38f);
395 
396  default:
397  return false;
398  }
399 }
400 
406 bool Scanner::xsEnumerateSerialPorts(XsPortInfoArray& ports, bool ignoreNonXsensDevices)
407 {
408  LOGXSSCAN("Enumerating USB devices");
409  XsPortInfo current;
410 
411 #ifdef _WIN32
412  HDEVINFO hDevInfo;
413  SP_DEVINFO_DATA DeviceInfoData;
414  DWORD i;
415 
416  // Create a HDEVINFO with all present devices.
417  hDevInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS, 0, 0, DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
418 
419  if (hDevInfo == INVALID_HANDLE_VALUE)
420  {
421  LOGXSSCAN("Failed to get any USB device information, check permissions");
422  return false;
423  }
424 
425  // Enumerate through all devices in Set.
426  DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
427  for (i = 0; !abortPortScan && SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); ++i)
428  {
429  // Get the registry key which stores the ports settings
430  HKEY hDeviceKey = SetupDiOpenDevRegKey(hDevInfo, &DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
431  if (hDeviceKey == INVALID_HANDLE_VALUE)
432  continue;
433 
434  xsens::JanitorFuncStdCall<HKEY, LSTATUS> devkeyCleaner(&RegCloseKey, hDeviceKey, true);
435 
436  // Read in the name of the port
437  char pszPortName[256];
438  DWORD dwSize = 256;
439  DWORD dwType = 0;
440  if ((RegQueryValueExA(hDeviceKey, "PortName", NULL, &dwType, (LPBYTE) pszPortName, &dwSize) != ERROR_SUCCESS) || (dwType != REG_SZ))
441  continue;
442 
443  // If it looks like "COMX" then
444  // add it to the array which will be returned
445  if (_strnicmp(pszPortName, "COM", 3))
446  continue;
447  int32_t nPort = atoi(&pszPortName[3]);
448  if (nPort == 0)
449  continue;
450 
451  std::string devpath = getDevicePath(hDevInfo, &DeviceInfoData);
452  uint16_t vid = vidFromString(devpath);
453  uint16_t pid = pidFromString(devpath);
454 
455  LOGXSSCAN("Found USB device " << devpath);
456  if (ignoreNonXsensDevices)
457  {
458  if (!isXsensUsbDevice(vid, pid))
459  {
460  LOGXSSCAN("Ignoring non-Xsens device " << devpath);
461  continue;
462  }
463  }
464 
465  current.setPortName(pszPortName);
466  current.setBaudrate(XBR_Invalid);
467  current.setDeviceId(deviceIdFromDevPath(devpath));
468  current.setVidPid(vid, pid);
469  ports.push_back(current);
470 
471  current.clear();
472  }
473 
474  // Cleanup
475 
476  SetupDiDestroyDeviceInfoList(hDevInfo);
477 
478  // Now sort the list by ascending port nr
479  std::sort(ports.begin(), ports.end());
480 
481 #else // !_WIN32
482  Udev xsudev;
483  struct udev* udev = xsudev.unew();
484  if (udev)
485  {
486  struct udev_enumerate* enumerate = xsudev.enumerate_new(udev);
487 
488  xsudev.enumerate_add_match_subsystem(enumerate, "tty");
489  xsudev.enumerate_scan_devices(enumerate);
490 
491  struct udev_list_entry* devices, *dev_list_entry;
492  devices = xsudev.enumerate_get_list_entry(enumerate);
493  for (dev_list_entry = devices; dev_list_entry != nullptr; dev_list_entry = xsudev.list_entry_get_next(dev_list_entry))
494  {
495  const char* path = xsudev.list_entry_get_name(dev_list_entry);
496  auto devcleaner = [&](struct udev_device * d)
497  {
498  xsudev.device_unref(d);
499  };
500  std::unique_ptr<struct udev_device, decltype(devcleaner)>
501  device(xsudev.device_new_from_syspath(udev, path), devcleaner);
502  if (!device)
503  continue;
504 
505  if (!xsudev.device_get_parent(device.get()))
506  // this is probably a console, definitely not a physical serial port device
507  continue;
508 
509  struct udev_device* usbParentDevice = xsudev.device_get_parent_with_subsystem_devtype(device.get(), "usb", "usb_device");
510  unsigned int vid = 0, pid = 0;
511  if (usbParentDevice)
512  {
513  const char* vendor = xsudev.device_get_sysattr_value(usbParentDevice, "idVendor");
514  const char* product = xsudev.device_get_sysattr_value(usbParentDevice, "idProduct");
515 
516  if (vendor && product)
517  {
518  sscanf(vendor, "%x", &vid);
519  sscanf(product, "%x", &pid);
520  }
521  }
522 
523  LOGXSSCAN("Found USB device " << path);
524 
525  if (ignoreNonXsensDevices)
526  {
527  if (!isXsensUsbDevice(vid, pid))
528  {
529  LOGXSSCAN("Ignoring non-Xsens device " << path);
530  continue;
531  }
532  }
533 
534  XsPortInfo portInfo;
535 
536  const char* devnode = xsudev.device_get_devnode(device.get());
537  if (strlen(devnode) > 255 || strncmp(devnode, "/dev/ttyS", 9) == 0)
538  continue;
539  portInfo.setPortName(devnode);
540  portInfo.setVidPid(vid, pid);
541 
542  if (usbParentDevice)
543  {
544  const char* deviceidstring;
545  deviceidstring = xsudev.device_get_sysattr_value(usbParentDevice, "serial");
546  if (deviceidstring)
547  {
548  int deviceId = 0;
549  sscanf(deviceidstring, "%08X", &deviceId);
550  portInfo.setDeviceId(deviceId);
551  }
552  }
553 
554  ports.push_back(portInfo);
555  }
556  xsudev.enumerate_unref(enumerate);
557  xsudev.unref(udev);
558  }
559  else
560  {
561  (void)ignoreNonXsensDevices;
562  DIR* dir;
563  struct dirent* entry;
564 
565  if ((dir = opendir("/dev/")) == NULL)
566  return false;
567 
568  while ((entry = readdir(dir)))
569  {
570  if (strncmp("ttyUSB", entry->d_name, 6) == 0)
571  {
572  char name[261];
573  sprintf(name, "/dev/%s", entry->d_name);
574  current.setPortName(name);
575  ports.push_back(current);
576  LOGXSSCAN("Found USB device " << name);
577  }
578  }
579  closedir(dir);
580  }
581 #endif // _WIN32
582  return true;
583 }
584 
590 {
591  (void)ports;
592  return false;
593 }
594 
600 {
601  (void)ports;
602  return false;
603 }
604 
608 
609 #define ILLEGAL_HUB (0)
610 #ifdef _WIN32
611 #define HUB_SEARCH_STRING ("Hub_#")
612 
618 int Scanner::xsScanGetHubNumber(HDEVINFO hDevInfo, SP_DEVINFO_DATA* deviceInfoData)
619 {
620  DWORD DataT;
621  char buffer[256];
622  int result = ILLEGAL_HUB;
623 
624  if (SetupDiGetDeviceRegistryPropertyA(hDevInfo,
625  deviceInfoData,
626  SPDRP_LOCATION_INFORMATION,
627  &DataT,
628  (PBYTE)buffer,
629  256,
630  NULL))
631  {
632  LOGXSSCAN("Registry access successful: \"" << buffer << "\"");
633  char const* hubString = strstr((char const*)buffer, HUB_SEARCH_STRING);
634  if (hubString)
635  result = strtol(hubString + strlen(HUB_SEARCH_STRING), 0, 10);
636  }
637  else
638  LOGXSSCAN("Get Hub Number failed with error " << GetLastError());
639 
640  return result;
641 }
642 
647 XsPortInfo Scanner::xsScanPortByHubId(const char* id)
648 {
649  assert(id != nullptr);
650  if (!id)
651  return XsPortInfo();
652 
653  LOGXSSCAN("xsScanPortByHubId with id " << id);
654 
655  // Get device interface info set handle for all devices attached to system
656  HDEVINFO hDevInfo = SetupDiGetClassDevs(
657  &GUID_DEVCLASS_PORTS, /* CONST GUID * ClassGuid - USB class GUID */
658  NULL, /* PCTSTR Enumerator */
659  NULL, /* HWND hwndParent */
660  DIGCF_PRESENT | DIGCF_DEVICEINTERFACE /* DWORD Flags */
661  );
662 
663  if (hDevInfo == INVALID_HANDLE_VALUE)
664  {
665  LOGXSSCAN("Failed to get any USB device information, check permissions");
666  return XsPortInfo();
667  }
668 
669  // Retrieve a context structure for a device interface of a device
670  // information set.
671 
672  SP_DEVICE_INTERFACE_DATA devInterfaceData;
673  ZeroMemory(&devInterfaceData, sizeof(SP_DEVICE_INTERFACE_DATA));
674  devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
675  XsPortInfo portInfo;
676  int port = 0;
677  for (DWORD dwIndex = 0; port == 0; ++dwIndex) //lint !e440 funky condition is ok
678  {
679  BOOL bRet = SetupDiEnumDeviceInterfaces(
680  hDevInfo, /* HDEVINFO DeviceInfoSet */
681  NULL, /* PSP_DEVINFO_DATA DeviceInfoData */
682  &GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR, /* CONST GUID * InterfaceClassGuid */
683  dwIndex,
684  &devInterfaceData /* PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData */
685  );
686  if (!bRet)
687  {
688  if (GetLastError() == ERROR_NO_MORE_ITEMS)
689  break;
690  }
691  else
692  {
693  size_t buffer[1024 / sizeof(size_t)]; // we use size_t instead of char to ensure proper data alignment
694 
695  SP_DEVINFO_DATA diData;
696  diData.cbSize = sizeof(SP_DEVINFO_DATA);
697  SP_DEVICE_INTERFACE_DETAIL_DATA_A& ifdData = *(SP_DEVICE_INTERFACE_DETAIL_DATA_A*) buffer;
698  ifdData.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
699  DWORD reqSize;
700  if (SetupDiGetDeviceInterfaceDetailA(hDevInfo, &devInterfaceData, &ifdData, 1020, &reqSize, &diData))
701  {
702  // determine my own id
703  // strip A#0000#* from path
704  char* end = strrchr(ifdData.DevicePath, '#');
705  if (end)
706  end[0] = 0;
707  end = strrchr(ifdData.DevicePath, '#');
708  if (end)
709  end[0] = 0;
710  char* start = strrchr(ifdData.DevicePath, '+');
711  if (!start || !end)
712  continue;
713  ++start;
714 
715  // compare IDs
716  if (_strnicmp(id, start, strlen(id)) == 0)
717  {
718  // we found a match, determine port nr
719  // Get the registry key which stores the ports settings
720  HKEY hDeviceKey = SetupDiOpenDevRegKey(hDevInfo, &diData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
721  if (hDeviceKey != INVALID_HANDLE_VALUE)
722  {
723  // Read in the name of the port
724  char pszPortName[256] = "";
725  DWORD dwSize = 256;
726  DWORD dwType = 0;
727  if ((RegQueryValueExA(hDeviceKey, "PortName", NULL, &dwType, (LPBYTE) pszPortName, &dwSize) == ERROR_SUCCESS) && (dwType == REG_SZ))
728  {
729  // If it looks like "COMX" then
730  // add it to the array which will be returned
731  if (!_strnicmp(pszPortName, "COM", 3))
732  port = atoi(&pszPortName[3]);
733  portInfo.setPortName(pszPortName);
734  std::string devpath = getDevicePath(hDevInfo, &diData);
735  uint16_t vid = vidFromString(devpath);
736  uint16_t pid = pidFromString(devpath);
737  LOGXSSCAN("Adding " << pszPortName << " with vid " << JLHEXLOG(vid) << " and pid " << JLHEXLOG(pid));
738  portInfo.setVidPid(vid, pid);
739  }
740  }
741  // Close the key now that we are finished with it
742  RegCloseKey(hDeviceKey);
743  }
744  }
745  }
746  }
747 
748  SetupDiDestroyDeviceInfoList(hDevInfo);
749 
750  return portInfo;
751 }
752 
758 bool Scanner::xsScanXsensUsbHubs(XsIntArray& hubs, XsPortInfoArray& ports)
759 {
760  LOGXSSCAN("xsScanXsensUsbHubs");
761 
762  // clear the lists
763  hubs.clear();
764  ports.clear();
765 
766  // Get device interface info set handle for all devices attached to system
767  HDEVINFO hDevInfo = SetupDiGetClassDevs(
768  &GUID_DEVINTERFACE_USB_DEVICE, /* CONST GUID * ClassGuid - USB class GUID */
769  NULL, /* PCTSTR Enumerator */
770  NULL, /* HWND hwndParent */
771  DIGCF_PRESENT | DIGCF_DEVICEINTERFACE /* DWORD Flags */
772  );
773 
774  if (hDevInfo == INVALID_HANDLE_VALUE)
775  {
776  LOGXSSCAN("Failed to get any USB device information, check permissions");
777  return false;
778  }
779 
780  // Retrieve a context structure for a device interface of a device
781  // information set.
782 
783  SP_DEVICE_INTERFACE_DATA devInterfaceData;
784  ZeroMemory(&devInterfaceData, sizeof(SP_DEVICE_INTERFACE_DATA));
785  devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
786  for (DWORD dwIndex = 0; ; ++dwIndex)
787  {
788  BOOL bRet = SetupDiEnumDeviceInterfaces(
789  hDevInfo, /* HDEVINFO DeviceInfoSet */
790  NULL, /* PSP_DEVINFO_DATA DeviceInfoData */
791  &GUID_DEVINTERFACE_USB_DEVICE, /* CONST GUID * InterfaceClassGuid */
792  dwIndex,
793  &devInterfaceData /* PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData */
794  );
795  if (!bRet)
796  {
797  if (GetLastError() == ERROR_NO_MORE_ITEMS)
798  break;
799  continue;
800  }
801 
802  size_t tmp1[1024 / sizeof(size_t)];
803  size_t tmp2[1024 / sizeof(size_t)];
804  char* buffer = (char*) tmp1;
805  SP_DEVINFO_DATA diData;
806  diData.cbSize = sizeof(SP_DEVINFO_DATA);
807 
808  SP_DEVICE_INTERFACE_DETAIL_DATA_A& ifdData = *(SP_DEVICE_INTERFACE_DETAIL_DATA_A*) tmp2;
809  ifdData.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
810  DWORD reqSize;
811  XsPortInfo port;
812  int hubNr = 0;
813  while (SetupDiGetDeviceInterfaceDetailA(hDevInfo, &devInterfaceData, &ifdData, 1020, &reqSize, &diData))
814  {
815  DWORD dataT;
816  if (SetupDiGetDeviceRegistryPropertyA(hDevInfo, &diData, SPDRP_MFG, &dataT, (PBYTE)buffer, 256, NULL))
817  {
818  if (_strnicmp(buffer, "xsens", 5)) // if this is NOT an xsens device, ignore it
819  break;
820  }
821  else
822  break;
823 
824  hubNr = xsScanGetHubNumber(hDevInfo, &diData);
825  // we found something, determine its port
826  if (hubNr)
827  {
828  char* hubEnd = strrchr(ifdData.DevicePath, '#');
829  if (!hubEnd || hubEnd == ifdData.DevicePath)
830  break;
831  hubEnd[0] = 0;
832  char* hubStart = strrchr(ifdData.DevicePath, '#');
833  if (!hubStart || hubStart == ifdData.DevicePath)
834  break;
835  ++hubStart;
836  port = xsScanPortByHubId(hubStart);
837  }
838  break;
839  }
840  if (port.portName().size())
841  {
842  LOGXSSCAN("xsScanXsensUsbHubs Adding hub " << hubNr << " and port " << port.portName());
843  hubs.push_back(hubNr);
844  ports.push_back(port);
845  }
846  }
847 
848  SetupDiDestroyDeviceInfoList(hDevInfo);
849 
850  return true;
851 }
852 #endif // _WIN32
853 
880 {
881 #ifdef _WIN32
882  int behindXsensHub = ILLEGAL_HUB;
883  XsIntArray hubs;
884  XsPortInfoArray ports;
885  if (!xsScanXsensUsbHubs(hubs, ports))
886  return XsUsbHubInfo();
887 
888  // check if it's in the ports list before going through all THAT trouble...
889  for (size_t i = 0; i < ports.size(); ++i)
890  if (_stricmp(ports[i].portName_c_str(), portInfo.portName_c_str()) == 0)
891  return XsUsbHubInfo(hubs[i]);
892 
893  HDEVINFO hDevInfo;
894  SP_DEVINFO_DATA devInfoData;
895 
896  // Create a HDEVINFO with all present devices.
897  hDevInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS, 0, 0, DIGCF_PRESENT | DIGCF_PROFILE);
898 
899  if (hDevInfo == INVALID_HANDLE_VALUE)
900  return XsUsbHubInfo();
901 
902  // Enumerate through all devices in Set.
903  devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
904  for (DWORD i = 0; behindXsensHub == ILLEGAL_HUB && SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); ++i)
905  {
906  DWORD dataT;
907  char buffer[256];
908 
909  // Call function with null to begin with,
910  // then use the returned buffer size
911  // to Alloc the buffer. Keep calling until
912  // success or an unknown failure.
913  //
914 
915  if (!SetupDiGetDeviceRegistryPropertyA(hDevInfo,
916  &devInfoData,
917  SPDRP_MFG,
918  &dataT,
919  (PBYTE)buffer,
920  256,
921  NULL))
922  continue;
923 
924  // Get the registry key which stores the ports settings
925  HKEY hDeviceKey = SetupDiOpenDevRegKey(hDevInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
926  if (hDeviceKey != INVALID_HANDLE_VALUE)
927  {
928  // Read in the name of the port
929  char pszPortName[256];
930  DWORD dwSize = 256;
931  DWORD dwType = 0;
932  if ((RegQueryValueExA(hDeviceKey, "PortName", NULL, &dwType, (LPBYTE) pszPortName, &dwSize) == ERROR_SUCCESS) && (dwType == REG_SZ))
933  {
934  // If it looks like "COMX" then
935  // add it to the array which will be returned
936  int32_t nLen = (int32_t) strlen(pszPortName);
937  if (nLen > 3)
938  {
939  if (_strnicmp(pszPortName, "COM", 3))
940  continue;
941  //int32_t nPort = atoi(&pszPortName[3]);
942  if (_stricmp(pszPortName, portInfo.portName_c_str()) == 0)
943  {
944  //This is the port we are looking for
945  //check if the hub number is in the hubs list
946  int hub = xsScanGetHubNumber(hDevInfo, &devInfoData);
947  JLDEBUGG("Got hub " << hub << " for port " << std::string(pszPortName));
948 
949  if (hub != ILLEGAL_HUB)
950  {
951  for (XsIntArray::const_iterator hi = hubs.begin(); hi != hubs.end(); ++hi)
952  {
953  if (*hi == hub)
954  {
955  behindXsensHub = hub;
956  break;
957  }
958  }
959  }
960  }
961  }
962  }
963  }
964  // Close the key now that we are finished with it
965  RegCloseKey(hDeviceKey);
966  }
967 
968  // Cleanup
969 
970  SetupDiDestroyDeviceInfoList(hDevInfo);
971  return XsUsbHubInfo(behindXsensHub);
972 #else
973  XsUsbHubInfo inf;
974 
975  Udev xsudev;
976 
977  udev* udevInstance = xsudev.unew();
978  if (udevInstance == NULL)
979  {
980  fprintf(stderr, "Unable to create udev object\n");
981  return XsUsbHubInfo();
982  }
983 
984  udev_enumerate* enumerate = xsudev.enumerate_new(udevInstance);
985  xsudev.enumerate_scan_devices(enumerate);
986 
987  udev_list_entry* devices = xsudev.enumerate_get_list_entry(enumerate);
988  udev_list_entry* dev;
989  for (dev = devices; dev != NULL; dev = xsudev.list_entry_get_next(dev))
990  {
991  const char* path = xsudev.list_entry_get_name(dev);
992  udev_device* device = xsudev.device_new_from_syspath(udevInstance, path);
993  if (!device)
994  return XsUsbHubInfo();
995 
996  const char* devnode = xsudev.device_get_devnode(device);
997  if (!devnode || strcmp(devnode, portInfo.portName_c_str()) != 0)
998  continue;
999 
1000  udev_device* parent = xsudev.device_get_parent_with_subsystem_devtype(device, "usb", "usb_device");
1001  if (!parent)
1002  break;
1003 
1004  const char* devpath = xsudev.device_get_sysattr_value(parent, "devpath");
1005  if (!devpath)
1006  break;
1007 
1008  inf = XsUsbHubInfo(devpath);
1009  break;
1010  }
1011  xsudev.enumerate_unref(enumerate);
1012  xsudev.unref(udevInstance);
1013  return inf;
1014 #endif
1015 }
XS_BID_MASTER
#define XS_BID_MASTER
The bus identifier of the master device.
Definition: xsbusid.h:73
Scanner::fetchBasicInfo
XsResultValue fetchBasicInfo(XsPortInfo &portInfo, uint32_t singleScanTimeout, bool detectRs485)
Fetch basic device information.
Definition: scanner.cpp:144
XsScannerNamespace::gScanLogCallback
XsScanLogCallbackFunc gScanLogCallback
Definition: scanner.cpp:100
FTDI_VENDOR_ID
#define FTDI_VENDOR_ID
Definition: xsportinfo.h:76
XsScannerNamespace::abortPortScan
volatile std::atomic_bool abortPortScan
Definition: scanner.cpp:98
usbcommunicator.h
Scanner::xsScanPort
bool xsScanPort(XsPortInfo &portInfo, XsBaudRate baud, uint32_t singleScanTimeout, bool detectRs485)
Scan a single COM port for connected Xsens devices.
Definition: scanner.cpp:224
XsScannerNamespace
Definition: scanner.cpp:96
XSENS_VENDOR_ID
#define XSENS_VENDOR_ID
Definition: xsportinfo.h:75
XsScannerNamespace::gScanner
Scanner * gScanner
Definition: scanner.cpp:99
OPS_OpenPort
@ OPS_OpenPort
Definition: openportstage.h:78
XBR_115k2
XBR_115k2
115k2 (115200 bps)
Definition: xsbaudrate.h:136
SerialPortCommunicator
A class that uses serial port to communicate.
Definition: serialportcommunicator.h:70
Scanner::~Scanner
virtual ~Scanner()
Destructor.
Definition: scanner.cpp:122
Scanner
Provides static functionality for scanning for Xsens devices.
Definition: scanner.h:83
XRV_TIMEOUT
@ XRV_TIMEOUT
258: A timeout occurred
Definition: xsresultvalue.h:128
XBR_19k2
XBR_19k2
19k2 (19200 bps)
Definition: xsbaudrate.h:131
Communicator::masterDeviceId
XsDeviceId masterDeviceId() const
Definition: communicator.cpp:157
xsens_janitors.h
XsIntArray
A list of XsInt values.
XsVersion
struct XsVersion XsVersion
Definition: xsversion.h:80
xsstringarray.h
xslibusb.h
SerialCommunicator::hardwareRevision
XsVersion hardwareRevision()
Definition: serialcommunicator.cpp:483
enumerateusbdevices.h
XRV_TIMEOUTNODATA
@ XRV_TIMEOUTNODATA
259: Operation aborted because of no data read
Definition: xsresultvalue.h:129
xsportinfo.h
XRV_ERROR
@ XRV_ERROR
256: A generic error occurred
Definition: xsresultvalue.h:126
XRV_OK
@ XRV_OK
0: Operation was performed successfully
Definition: xsresultvalue.h:85
deviceIdFromDevPath
static XsDeviceId deviceIdFromDevPath(std::string const &devpath)
Definition: idfetchhelpers.h:85
journaller.h
XBR_9600
XBR_9600
9k6 (9600 bps)
Definition: xsbaudrate.h:129
OPS_InitStart
@ OPS_InitStart
Definition: openportstage.h:82
XsScanLogCallbackFunc
void(* XsScanLogCallbackFunc)(struct XsString const *)
Defines the callback type that can be supplied to XsScanner_setScanLogCallback.
Definition: xsscanner.h:91
XsResultValue
XsResultValue
Xsens result values.
Definition: xsresultvalue.h:82
Communicator::lastResult
XsResultValue lastResult() const
Get the result value of the last operation.
Definition: communicator.cpp:167
Scanner::isXsensUsbDevice
static bool isXsensUsbDevice(uint16_t vid, uint16_t pid)
Definition: scanner.cpp:383
Scanner::xsFilterResponsiveDevices
bool xsFilterResponsiveDevices(XsPortInfoArray &ports, XsBaudRate baudrate, uint32_t singleScanTimeout, bool detectRs485)
Filter responsive devices.
Definition: scanner.cpp:339
udev.h
XsPortInfo
struct XsPortInfo XsPortInfo
Definition: xsportinfo.h:83
DeviceCommunicator::writeMessage
bool writeMessage(const XsMessage &message) override
Write message to the device.
Definition: devicecommunicator.cpp:248
XsBaudRate
enum XsBaudRate XsBaudRate
Communication speed.
Definition: xsbaud.h:81
uint32_t
unsigned int uint32_t
Definition: pstdint.h:485
XsPortInfo
Contains a descriptor for opening a communication port to an Xsens device.
Definition: xsportinfo.h:128
Scanner::xsEnumerateSerialPorts
bool xsEnumerateSerialPorts(XsPortInfoArray &ports, bool ignoreNonXsensDevices)
Enumerate the serial ports.
Definition: scanner.cpp:406
Scanner::xsEnumerateBluetoothDevices
virtual bool xsEnumerateBluetoothDevices(XsPortInfoArray &ports)
Enumerates a bluetooth device.
Definition: scanner.cpp:599
XsPortLinesOptions
XsPortLinesOptions
Definition: xsportinfo.h:109
XBR_38k4
XBR_38k4
38k4 (38400 bps)
Definition: xsbaudrate.h:133
xsbusid.h
d
d
Scanner::xsScanPorts
virtual bool xsScanPorts(XsPortInfoArray &ports, XsBaudRate baudrate, uint32_t singleScanTimeout, bool ignoreNonXsensDevices, bool detectRs485)
Scan serial ports for connected Xsens devices.
Definition: scanner.cpp:315
xsens::JanitorFuncStdCall
Function calling janitor class.
Definition: xsens_janitors.h:864
XsVersion
A class to store version information.
Definition: xsversion.h:95
Scanner::xsScanUsbHub
XsUsbHubInfo xsScanUsbHub(const XsPortInfo &portInfo)
Get information about the hub configuration.
Definition: scanner.cpp:879
xsEnumerateUsbDevices
bool xsEnumerateUsbDevices(XsPortInfoArray &ports)
Enumerate Xsens USB devices.
Definition: enumerateusbdevices.cpp:145
SerialCommunicator::closePort
void closePort() override
Closes the port.
Definition: serialcommunicator.cpp:210
Udev
Class for dynamic loading of winusb.
XsMessage
Structure for storing a single message.
Definition: xsmessage.h:202
XsUsbHubInfo
A structure that wraps USB hub information.
Definition: xsusbhubinfo.h:99
XsPortInfoArray
A list of XsPortInfo values.
start
ROSCPP_DECL void start()
XBR_2000k
XBR_2000k
2000k0 (2000000 bps)
Definition: xsbaudrate.h:141
LOGXSSCAN
#define LOGXSSCAN(msg)
Definition: scanner.h:123
XBR_460k8
XBR_460k8
460k8 (460800 bps)
Definition: xsbaudrate.h:138
SerialCommunicator::firmwareRevision
XsVersion firmwareRevision()
Definition: serialcommunicator.cpp:476
Scanner::setScanLogCallback
static void setScanLogCallback(XsScanLogCallbackFunc cb)
Set a callback function for scan log progress and problem reporting.
Definition: scanner.cpp:131
XBR_921k6
XBR_921k6
921k6 (921600 bps)
Definition: xsbaudrate.h:139
XsResultValue_toString
const char * XsResultValue_toString(XsResultValue result)
Retrieve a character string corresponding to the given result code.
Definition: xsresultvalue.c:78
BOOL
int BOOL
Definition: xstypedefs.h:141
JLHEXLOG
#define JLHEXLOG(d)
Definition: journaller.h:335
ILLEGAL_HUB
#define ILLEGAL_HUB
Definition: scanner.cpp:609
idfetchhelpers.h
XPLO_RtsCtsFlowControl
@ XPLO_RtsCtsFlowControl
Definition: xsportinfo.h:125
JLDEBUGG
#define JLDEBUGG(msg)
Definition: journaller.h:280
XsUsbHubInfo
struct XsUsbHubInfo XsUsbHubInfo
Definition: xsusbhubinfo.h:79
DeviceCommunicator::setGotoConfigTimeout
void setGotoConfigTimeout(uint32_t timeout) override
Set the timeout for the gotoConfig function.
Definition: devicecommunicator.cpp:158
XMID_Reset
@ XMID_Reset
Definition: xsxbusmessageid.h:185
int32_t
signed int int32_t
Definition: pstdint.h:515
OPS_Full
@ OPS_Full
Definition: openportstage.h:83
XRV_CONFIGCHECKFAIL
@ XRV_CONFIGCHECKFAIL
293: A configuration-time check of the device failed
Definition: xsresultvalue.h:165
scanner.h
Scanner::xsEnumerateNetworkDevices
virtual bool xsEnumerateNetworkDevices(XsPortInfoArray &ports)
Enumerates a network device.
Definition: scanner.cpp:589
XBR_230k4
XBR_230k4
230k4 (230400 bps)
Definition: xsbaudrate.h:137
SerialCommunicator::openPort
bool openPort(const XsPortInfo &portInfo, OpenPortStage stage=OPS_Full, bool detectRs485=false) override
Open a serial port and return the main device connected to it.
Definition: serialcommunicator.cpp:243
vidFromString
static uint16_t vidFromString(std::string const &string)
Definition: idfetchhelpers.h:95
serialportcommunicator.h
Scanner::Accessor::scanner
Scanner & scanner() const
Definition: scanner.cpp:110
XBR_Invalid
XBR_Invalid
Not a valid baud rate.
Definition: xsbaudrate.h:126
pidFromString
static uint16_t pidFromString(std::string const &string)
Definition: idfetchhelpers.h:113
XBR_57k6
XBR_57k6
57k6 (57600 bps)
Definition: xsbaudrate.h:134
xsintarray.h


xsens_mti_driver
Author(s):
autogenerated on Sun Sep 3 2023 02:43:20