blob: 791f619ff78b4c1fd6b4a13cad269ef21eb00c0b [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "lorgnette/usb/usb_device.h"
#include <map>
#include <string>
#include <base/logging.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <chromeos/constants/lorgnette_dlc.h>
#include <libusb.h>
#include <set>
#include "lorgnette/ippusb_device.h"
#include "lorgnette/scanner_match.h"
namespace lorgnette {
namespace {
const char kScannerTypeMFP[] = "multi-function peripheral"; // Matches SANE.
// Scanners requiring the sane-backends-pfu DLC.
std::set<VidPid> kScannersRequiringSaneBackendsPfuDlc = {
{0x04c5, 0x132e}, {0x04c5, 0x15fc}, {0x04c5, 0x15ff}, {0x05ca, 0x0307}};
// Creates a new key in `map` for each scanner in `scanners`, with the value
// `id`.
void SetScannerIds(const std::set<VidPid>& scanners,
const std::string& id,
std::map<VidPid, std::string>* map) {
DCHECK(map);
for (const auto& vidpid : scanners) {
auto itr = map->find(vidpid);
DCHECK(itr == map->end());
(*map)[vidpid] = id;
}
}
} // namespace
UsbDevice::UsbDevice() {
SetScannerIds(kScannersRequiringSaneBackendsPfuDlc, kSaneBackendsPfuDlcId,
&default_dlc_backend_scanners_);
dlc_backend_scanners_ = &default_dlc_backend_scanners_;
}
uint16_t UsbDevice::GetVid() const {
return vid_;
}
uint16_t UsbDevice::GetPid() const {
return pid_;
}
std::string UsbDevice::Description() const {
return vid_pid_;
}
void UsbDevice::Init() {
auto descriptor = GetDeviceDescriptor();
if (!descriptor) {
return;
}
vid_ = descriptor->idVendor;
pid_ = descriptor->idProduct;
vid_pid_ = base::StringPrintf("%04x:%04x", vid_, pid_);
}
bool UsbDevice::SupportsIppUsb() const {
auto maybe_descriptor = GetDeviceDescriptor();
if (!maybe_descriptor.has_value()) {
return false;
}
libusb_device_descriptor& descriptor = maybe_descriptor.value();
// Printers always have a printer class interface defined. They don't define
// a top-level device class.
if (descriptor.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
return false;
}
bool isPrinter = false;
bool isIppUsb = false;
for (uint8_t c = 0; c < descriptor.bNumConfigurations; c++) {
ScopedConfigDescriptor config = GetConfigDescriptor(c);
if (!config) {
continue;
}
isIppUsb = ContainsIppUsbInterface(config.get(), &isPrinter);
if (isIppUsb) {
break;
}
}
if (isPrinter && !isIppUsb) {
LOG(INFO) << "Device " << Description() << " is a printer without IPP-USB";
}
return isIppUsb;
}
std::string UsbDevice::GetSerialNumber() {
auto maybe_descriptor = GetDeviceDescriptor();
if (!maybe_descriptor.has_value()) {
return "";
}
libusb_device_descriptor& descriptor = maybe_descriptor.value();
if (descriptor.iSerialNumber == 0) {
// A valid serial number string descriptor must be at index 1 or later.
return "";
}
auto serial = GetStringDescriptor(descriptor.iSerialNumber);
if (!serial.has_value() || serial->empty()) {
LOG(ERROR) << "Device " << Description() << " is missing serial number";
return "";
}
return serial.value();
}
std::optional<ScannerInfo> UsbDevice::IppUsbScannerInfo() {
auto maybe_descriptor = GetDeviceDescriptor();
if (!maybe_descriptor.has_value()) {
return std::nullopt;
}
libusb_device_descriptor& descriptor = maybe_descriptor.value();
auto mfgr_name = GetStringDescriptor(descriptor.iManufacturer);
if (!mfgr_name.has_value() || mfgr_name->empty()) {
LOG(ERROR) << "Device " << Description() << " is missing manufacturer";
return std::nullopt;
}
auto model_name = GetStringDescriptor(descriptor.iProduct);
if (!model_name.has_value() || model_name->empty()) {
LOG(ERROR) << "Device " << Description() << " is missing product";
return std::nullopt;
}
std::string printer_name;
if (base::StartsWith(*model_name, *mfgr_name,
base::CompareCase::INSENSITIVE_ASCII)) {
printer_name = *model_name;
} else {
printer_name = *mfgr_name + " " + *model_name;
}
std::string device_name = base::StringPrintf(
"ippusb:escl:%s:%04x_%04x/eSCL/", printer_name.c_str(), vid_, pid_);
ScannerInfo info;
info.set_name(device_name);
info.set_manufacturer(*mfgr_name);
info.set_model(*model_name);
info.set_type(kScannerTypeMFP); // Printer that can scan == MFP.
info.set_connection_type(lorgnette::CONNECTION_USB);
info.set_secure(true);
info.set_protocol_type(ProtocolTypeForScanner(info));
info.set_display_name(DisplayNameForScanner(info));
return info;
}
std::map<VidPid, std::string>* UsbDevice::GetDlcBackendScanners() {
return dlc_backend_scanners_;
}
void UsbDevice::SetDlcBackendScanners(
std::map<VidPid, std::string>* dlc_backend_scanners) {
CHECK(dlc_backend_scanners);
dlc_backend_scanners_ = dlc_backend_scanners;
}
std::optional<std::string> UsbDevice::GetNonBundledBackendId() const {
VidPid curr_device = {GetVid(), GetPid()};
auto itr = dlc_backend_scanners_->find(curr_device);
if (itr == dlc_backend_scanners_->end()) {
return std::nullopt;
}
return itr->second;
}
} // namespace lorgnette