OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
USBSerialDevInfo_Linux.h
Go to the documentation of this file.
1 
12 // Copyright 2015 Sensics, Inc.
13 //
14 // Licensed under the Apache License, Version 2.0 (the "License");
15 // you may not use this file except in compliance with the License.
16 // You may obtain a copy of the License at
17 //
18 // http://www.apache.org/licenses/LICENSE-2.0
19 //
20 // Unless required by applicable law or agreed to in writing, software
21 // distributed under the License is distributed on an "AS IS" BASIS,
22 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 // See the License for the specific language governing permissions and
24 // limitations under the License.
25 
26 // Internal Includes
27 #include "USBSerialDevInfo.h"
28 
29 // Library/third-party includes
30 #include <boost/filesystem.hpp>
31 #include <boost/range/iterator_range.hpp>
32 #include <boost/algorithm/string.hpp>
33 
34 // Standard includes
35 #include <iostream>
36 #include <vector>
37 #include <string>
38 #include <fstream>
39 
40 #include <fcntl.h> // for O_NONBLOCK
41 #include <linux/serial.h> // for serial_info
42 #include <sys/ioctl.h> // for ioctl
43 #include <unistd.h> // for open, close
44 
45 namespace osvr {
46 namespace usbserial {
47 
48  namespace {
49 
60  boost::optional<USBSerialDevice>
61  make_USBSerialDevice(const std::string &device) {
62  // Vendor ID stored in file /sys/class/tty/DEVICE/device/uevent
63  //
64  // PRODUCT=2341/1/1
65  //
66  // Vendor ID / Product ID / who cares?
67  //
68  // Values are in hexadecimal
69 
70  boost::filesystem::path uevent_file =
71  "/sys/class/tty/" + device + "/device/uevent";
72  if (!boost::filesystem::exists(uevent_file)) {
73  return boost::none;
74  }
75 
76  std::ifstream uevent_input(uevent_file.generic_string());
77  std::string line;
78  while (std::getline(uevent_input, line)) {
79  if (!boost::starts_with(line, "PRODUCT="))
80  continue;
81 
82  // Extract the vendor and product IDs
83  const auto first = line.find("=");
84  const auto second = line.find("/", first);
85  const auto third = line.find("/", second + 1);
86 
87  const std::string vendor_id_str =
88  line.substr(first + 1, second - first - 1);
89  const std::string product_id_str =
90  line.substr(second + 1, third - second - 1);
91 
92  const uint16_t detected_vendor_id =
93  std::stoi(vendor_id_str.c_str(), 0, 16);
94  const uint16_t detected_product_id =
95  std::stoi(product_id_str.c_str(), 0, 16);
96 
97  // Construct the USBSerialDevice
98  const std::string device_name =
99  (boost::filesystem::path("/dev") / device).generic_string();
100  USBSerialDevice usb_serial_device(detected_vendor_id,
101  detected_product_id,
102  device_name, device_name);
103  return usb_serial_device;
104  }
105 
106  return boost::none;
107  }
108 
116  bool serial8250_device_connected(const std::string &port) {
117  const int fd = open(port.c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY);
118  if (-1 == fd) {
119  return false;
120  }
121 
122  struct serial_struct serial_info;
123  const int ret = ioctl(fd, TIOCGSERIAL, &serial_info);
124  if (-1 == ret) {
125  close(fd);
126  return false;
127  }
128 
129  if (PORT_UNKNOWN == serial_info.type) {
130  close(fd);
131  return false;
132  }
133 
134  return true;
135  }
136 
148  bool matches_ids(const USBSerialDevice &device,
149  const boost::optional<uint16_t> &vendorID,
150  const boost::optional<uint16_t> &productID) {
151  const bool vendor_matches =
152  (!vendorID || *vendorID == device.getVID());
153  const bool product_matches =
154  (!productID || *productID == device.getPID());
155 
156  return (vendor_matches && product_matches);
157  }
158 
159  } // end namespace
160 
161  std::vector<USBSerialDevice>
162  getSerialDeviceList(boost::optional<uint16_t> vendorID,
163  boost::optional<uint16_t> productID) {
164 
165  std::vector<USBSerialDevice> devices;
166 
167  // Get a list of all TTY devices in /sys/class/tty
168  const boost::filesystem::path sys_class_tty = "/sys/class/tty";
169  for (const auto &path_name : boost::make_iterator_range(
170  boost::filesystem::directory_iterator(sys_class_tty),
171  boost::filesystem::directory_iterator())) {
172  // Filter out entries that don't have a .../device/driver file
173  const boost::filesystem::path device_candidate = path_name;
174  const boost::filesystem::path driver_file =
175  device_candidate / "device" / "driver";
176  if (!boost::filesystem::exists(driver_file)) {
177  continue;
178  }
179 
180  // Device name is /dev/ttySomething
181  const boost::filesystem::path basename =
182  boost::filesystem::basename(device_candidate);
183 
184  auto usb_serial_device =
185  make_USBSerialDevice(basename.generic_string());
186  if (!usb_serial_device)
187  continue;
188 
189  // Check to see if it matches the vendor and product ID
190  if (!matches_ids(*usb_serial_device, vendorID, productID))
191  continue;
192 
193  // If the driver is serial8250, check to see if a device is actually
194  // connected to the port
195  if (boost::filesystem::exists(driver_file / "serial8250")) {
196  const bool is_connected = serial8250_device_connected(
197  "/dev/" + basename.generic_string());
198  if (!is_connected) {
199  continue;
200  }
201  }
202 
203  devices.push_back(*usb_serial_device);
204  }
205 
206  return devices;
207  }
208 
209 } // namespace usbserial
210 } // namespace osvr