diff --git a/src/UsbCameraImpl.cpp b/src/UsbCameraImpl.cpp index 3a25f96d0d..c34753f646 100644 --- a/src/UsbCameraImpl.cpp +++ b/src/UsbCameraImpl.cpp @@ -38,6 +38,7 @@ #include "Handle.h" #include "Log.h" #include "Notifier.h" +#include "UsbUtil.h" using namespace cs; @@ -308,10 +309,59 @@ static int SetStringCtrlIoctl(int fd, int id, int maximum, return DoIoctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls); } +static bool GetDescriptionSysV4L(llvm::StringRef path, std::string* desc) { + llvm::SmallString<64> ifpath{"/sys/class/video4linux/"}; + ifpath += path.substr(5); + ifpath += "/device/interface"; + + int fd = open(ifpath.c_str(), O_RDONLY); + if (fd < 0) return false; + + char readBuf[128]; + ssize_t n = read(fd, readBuf, sizeof(readBuf)); + close(fd); + + if (n <= 0) return false; + + *desc = llvm::StringRef(readBuf, n).rtrim(); + return true; +} + +static bool GetDescriptionIoctl(const char* cpath, std::string* desc) { + int fd = open(cpath, O_RDWR); + if (fd < 0) return false; + + struct v4l2_capability vcap; + std::memset(&vcap, 0, sizeof(vcap)); + if (DoIoctl(fd, VIDIOC_QUERYCAP, &vcap) < 0) { + close(fd); + return false; + } + close(fd); + + llvm::StringRef card{reinterpret_cast(vcap.card)}; + // try to convert "UVC Camera (0000:0000)" into a better name + int vendor = 0; + int product = 0; + if (card.startswith("UVC Camera (") && + !card.substr(12, 4).getAsInteger(16, vendor) && + !card.substr(17, 4).getAsInteger(16, product)) { + llvm::SmallString<64> card2Buf; + llvm::StringRef card2 = GetUsbNameFromId(vendor, product, card2Buf); + if (!card2.empty()) { + *desc = card2; + return true; + } + } + + *desc = card; + return true; +} + static std::string GetDescriptionImpl(const char* cpath) { llvm::StringRef path{cpath}; - int fd; char pathBuf[128]; + std::string rv; // If trying to get by id or path, follow symlink if (path.startswith("/dev/v4l/by-id/")) { @@ -324,48 +374,11 @@ static std::string GetDescriptionImpl(const char* cpath) { if (path.startswith("/dev/video")) { // Sometimes the /sys tree gives a better name. - llvm::SmallString<64> ifpath{"/sys/class/video4linux/"}; - ifpath += path.substr(5); - ifpath += "/device/interface"; - fd = open(ifpath.c_str(), O_RDONLY); - if (fd >= 0) { - char readBuf[128]; - ssize_t n = read(fd, readBuf, sizeof(readBuf)); - if (n > 0) { - close(fd); - return llvm::StringRef(readBuf, n).rtrim(); - } - close(fd); - } + if (GetDescriptionSysV4L(path, &rv)) return rv; } // Otherwise use an ioctl to query the caps and get the card name - fd = open(cpath, O_RDWR); - if (fd >= 0) { - struct v4l2_capability vcap; - std::memset(&vcap, 0, sizeof(vcap)); - if (DoIoctl(fd, VIDIOC_QUERYCAP, &vcap) >= 0) { - close(fd); - llvm::StringRef card{reinterpret_cast(vcap.card)}; - // try to convert "UVC Camera (0000:0000)" into a better name - int vendor = 0; - int product = 0; - if (card.startswith("UVC Camera (") && - !card.substr(12, 4).getAsInteger(16, vendor) && - !card.substr(17, 4).getAsInteger(16, product)) { - switch (vendor) { - case 0x046d: - switch (product) { - case 0x081b: return "Logitech, Inc. Webcam C310"; - case 0x0825: return "Logitech, Inc. Webcam C270"; - } - break; - } - } - return card; - } - close(fd); - } + if (GetDescriptionIoctl(cpath, &rv)) return rv; return std::string{}; } diff --git a/src/UsbUtil.cpp b/src/UsbUtil.cpp new file mode 100644 index 0000000000..1bf8cd341c --- /dev/null +++ b/src/UsbUtil.cpp @@ -0,0 +1,108 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "UsbUtil.h" + +#include + +#include "llvm/Format.h" +#include "llvm/SmallString.h" +#include "llvm/raw_ostream.h" +#include "support/raw_istream.h" + +#include "HttpUtil.h" + +namespace cs { + +static llvm::StringRef GetUsbNameFromFile(int vendor, int product, + llvm::SmallVectorImpl& buf) { + int fd = open("/var/lib/usbutils/usb.ids", O_RDONLY); + if (fd < 0) return llvm::StringRef{}; + + llvm::raw_svector_ostream os{buf}; + wpi::raw_fd_istream is{fd, true}; + + // build vendor and product 4-char hex strings + llvm::SmallString<16> vendorStr, productStr; + llvm::raw_svector_ostream vendorOs{vendorStr}, productOs{productStr}; + vendorOs << llvm::format_hex_no_prefix(vendor, 4); + productOs << llvm::format_hex_no_prefix(product, 4); + + // scan file + llvm::SmallString<128> lineBuf; + bool foundVendor = false; + for (;;) { + bool error = false; + auto line = ReadLine(is, lineBuf, 4096, &error); + if (error) break; + + if (line.empty()) continue; + + // look for vendor at start of line + if (line.startswith(vendorStr)) { + foundVendor = true; + os << line.substr(5).trim() << ' '; + continue; + } + + if (foundVendor) { + // next vendor, but didn't match product? + if (line[0] != '\t') { + os << "Unknown"; + return os.str(); + } + + // look for product + if (line.substr(1).startswith(productStr)) { + os << line.substr(6).trim(); + return os.str(); + } + } + } + + return llvm::StringRef{}; +} + +llvm::StringRef GetUsbNameFromId(int vendor, int product, + llvm::SmallVectorImpl& buf) { + // try reading usb.ids + llvm::StringRef rv = GetUsbNameFromFile(vendor, product, buf); + if (!rv.empty()) return rv; + + // Fall back to internal database + llvm::raw_svector_ostream os{buf}; + switch (vendor) { + case 0x046d: + os << "Logitech, Inc. "; + switch (product) { + case 0x0802: os << "Webcam C200"; break; + case 0x0804: os << "Webcam C250"; break; + case 0x0805: os << "Webcam C300"; break; + case 0x0807: os << "Webcam B500"; break; + case 0x0808: os << "Webcam C600"; break; + case 0x0809: os << "Webcam Pro 9000"; break; + case 0x080a: os << "Portable Webcam C905"; break; + case 0x080f: os << "Webcam C120"; break; + case 0x0819: os << "Webcam C210"; break; + case 0x081b: os << "Webcam C310"; break; + case 0x081d: os << "HD Webcam C510"; break; + case 0x0821: os << "HD Webcam C910"; break; + case 0x0825: os << "Webcam C270"; break; + case 0x0826: os << "HD Webcam C525"; break; + case 0x0828: os << "HD Webcam B990"; break; + case 0x082b: os << "Webcam C170"; break; + case 0x082d: os << "HD Pro Webcam C920"; break; + case 0x0836: os << "B525 HD Webcam"; break; + case 0x0843: os << "Webcam C930e"; break; + } + break; + } + + return os.str(); +} + +} // namespace cs diff --git a/src/UsbUtil.h b/src/UsbUtil.h new file mode 100644 index 0000000000..c41e130417 --- /dev/null +++ b/src/UsbUtil.h @@ -0,0 +1,21 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#ifndef CS_USBUTIL_H_ +#define CS_USBUTIL_H_ + +#include "llvm/SmallVector.h" +#include "llvm/StringRef.h" + +namespace cs { + +llvm::StringRef GetUsbNameFromId(int vendor, int product, + llvm::SmallVectorImpl& buf); + +} // namespace cs + +#endif // CS_USBUTIL_H_