blob: 6ff1a31eca36cbba71e8bd041cea5e662019355a [file] [log] [blame]
// Copyright 2020 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/sane_client_impl.h"
#include <optional>
#include <base/check.h>
#include <base/logging.h>
#include <chromeos/dbus/service_constants.h>
#include <re2/re2.h>
#include <sane/saneopts.h>
#include <sane-airscan/airscan.h>
#include "lorgnette/dbus_adaptors/org.chromium.lorgnette.Manager.h"
#include "lorgnette/guess_source.h"
#include "lorgnette/sane_device_impl.h"
#include "lorgnette/scanner_match.h"
static const char* kDbusDomain = brillo::errors::dbus::kDomain;
namespace lorgnette {
// static
std::unique_ptr<SaneClientImpl> SaneClientImpl::Create(
LibsaneWrapper* libsane) {
SANE_Status status = libsane->sane_init(nullptr, nullptr);
if (status != SANE_STATUS_GOOD) {
LOG(ERROR) << "Unable to initialize SANE";
return nullptr;
}
// Cannot use make_unique() with a private constructor.
return std::unique_ptr<SaneClientImpl>(new SaneClientImpl(libsane));
}
SaneClientImpl::~SaneClientImpl() {
libsane_->sane_exit();
}
std::optional<std::vector<ScannerInfo>> SaneClientImpl::ListDevices(
brillo::ErrorPtr* error, bool local_only) {
base::AutoLock auto_lock(lock_);
const SANE_Device** device_list;
SANE_Status status = libsane_->sane_get_devices(
&device_list, local_only ? SANE_TRUE : SANE_FALSE);
if (status != SANE_STATUS_GOOD) {
brillo::Error::AddTo(error, FROM_HERE, kDbusDomain, kManagerServiceError,
"Unable to get device list from SANE");
return std::nullopt;
}
return DeviceListToScannerInfo(device_list);
}
// static
std::optional<std::vector<ScannerInfo>> SaneClientImpl::DeviceListToScannerInfo(
const SANE_Device** device_list) {
if (!device_list) {
LOG(ERROR) << "'device_list' cannot be NULL";
return std::nullopt;
}
std::unordered_set<std::string> names;
std::vector<ScannerInfo> scanners;
for (int i = 0; device_list[i]; i++) {
const SANE_Device* dev = device_list[i];
if (!dev->name || strcmp(dev->name, "") == 0)
continue;
if (names.count(dev->name) != 0) {
LOG(ERROR) << "Duplicate device name: " << dev->name;
return std::nullopt;
}
names.insert(dev->name);
ScannerInfo info;
info.set_name(dev->name);
info.set_manufacturer(dev->vendor ? dev->vendor : "");
info.set_model(dev->model ? dev->model : "");
info.set_type(dev->type ? dev->type : "");
info.set_connection_type(ConnectionTypeForScanner(info));
info.set_secure(info.connection_type() == lorgnette::CONNECTION_USB);
info.set_protocol_type(ProtocolTypeForScanner(info));
info.set_display_name(DisplayNameForScanner(info));
scanners.push_back(info);
}
return scanners;
}
SaneClientImpl::SaneClientImpl(LibsaneWrapper* libsane)
: libsane_(libsane), open_devices_(std::make_shared<DeviceSet>()) {}
std::unique_ptr<SaneDevice> SaneClientImpl::ConnectToDeviceInternal(
brillo::ErrorPtr* error,
SANE_Status* sane_status,
const std::string& device_name) {
LOG(INFO) << "Creating connection to device: " << device_name;
base::AutoLock auto_lock(lock_);
SANE_Handle handle;
{
base::AutoLock auto_lock(open_devices_->first);
if (open_devices_->second.count(device_name) != 0) {
brillo::Error::AddToPrintf(
error, FROM_HERE, kDbusDomain, kManagerServiceError,
"Device '%s' is currently in-use", device_name.c_str());
return nullptr;
}
SANE_Status status = libsane_->sane_open(device_name.c_str(), &handle);
if (status != SANE_STATUS_GOOD) {
brillo::Error::AddToPrintf(error, FROM_HERE, kDbusDomain,
kManagerServiceError,
"Unable to open device '%s': %s",
device_name.c_str(), sane_strstatus(status));
if (sane_status)
*sane_status = status;
return nullptr;
}
open_devices_->second.insert(device_name);
}
// Cannot use make_unique() with a private constructor.
auto device = std::unique_ptr<SaneDeviceImpl>(
new SaneDeviceImpl(libsane_, handle, device_name, open_devices_));
device->LoadOptions(error);
return device;
}
} // namespace lorgnette