123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- // Copyright 2014 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "services/device/usb/usb_service_impl.h"
- #include <stdint.h>
- #include <list>
- #include <memory>
- #include <set>
- #include <utility>
- #include "base/barrier_closure.h"
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/containers/contains.h"
- #include "base/location.h"
- #include "base/memory/ref_counted_memory.h"
- #include "base/memory/weak_ptr.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/task/sequenced_task_runner.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/task/thread_pool.h"
- #include "base/threading/scoped_blocking_call.h"
- #include "build/build_config.h"
- #include "components/device_event_log/device_event_log.h"
- #include "services/device/usb/usb_device_handle.h"
- #include "services/device/usb/usb_error.h"
- #include "services/device/usb/webusb_descriptors.h"
- #include "third_party/libusb/src/libusb/libusb.h"
- namespace device {
- namespace {
- // Standard USB requests and descriptor types:
- const uint16_t kUsbVersion2_1 = 0x0210;
- scoped_refptr<UsbContext> InitializeUsbContextBlocking() {
- PlatformUsbContext platform_context = nullptr;
- int rv = libusb_init(&platform_context);
- if (rv == LIBUSB_SUCCESS && platform_context) {
- return base::MakeRefCounted<UsbContext>(platform_context);
- }
- USB_LOG(DEBUG) << "Failed to initialize libusb: "
- << ConvertPlatformUsbErrorToString(rv);
- return nullptr;
- }
- absl::optional<std::vector<ScopedLibusbDeviceRef>> GetDeviceListBlocking(
- scoped_refptr<UsbContext> usb_context) {
- base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
- base::BlockingType::MAY_BLOCK);
- libusb_device** platform_devices = NULL;
- const ssize_t device_count =
- libusb_get_device_list(usb_context->context(), &platform_devices);
- if (device_count < 0) {
- USB_LOG(ERROR) << "Failed to get device list: "
- << ConvertPlatformUsbErrorToString(device_count);
- return absl::nullopt;
- }
- std::vector<ScopedLibusbDeviceRef> scoped_devices;
- scoped_devices.reserve(device_count);
- for (ssize_t i = 0; i < device_count; ++i)
- scoped_devices.emplace_back(platform_devices[i], usb_context);
- // Free the list but don't unref the devices because ownership has been
- // been transfered to the elements of |scoped_devices|.
- libusb_free_device_list(platform_devices, false);
- return scoped_devices;
- }
- void CloseHandleAndRunContinuation(scoped_refptr<UsbDeviceHandle> device_handle,
- base::OnceClosure continuation) {
- device_handle->Close();
- std::move(continuation).Run();
- }
- void SaveStringsAndRunContinuation(
- scoped_refptr<UsbDeviceImpl> device,
- uint8_t manufacturer,
- uint8_t product,
- uint8_t serial_number,
- base::OnceClosure continuation,
- std::unique_ptr<std::map<uint8_t, std::u16string>> string_map) {
- if (manufacturer != 0)
- device->set_manufacturer_string((*string_map)[manufacturer]);
- if (product != 0)
- device->set_product_string((*string_map)[product]);
- if (serial_number != 0)
- device->set_serial_number((*string_map)[serial_number]);
- std::move(continuation).Run();
- }
- void OnReadBosDescriptor(scoped_refptr<UsbDeviceHandle> device_handle,
- base::OnceClosure barrier,
- const GURL& landing_page) {
- scoped_refptr<UsbDeviceImpl> device =
- static_cast<UsbDeviceImpl*>(device_handle->GetDevice().get());
- if (landing_page.is_valid())
- device->set_webusb_landing_page(landing_page);
- std::move(barrier).Run();
- }
- void RunSuccessAndCompletionClosure(base::OnceClosure success_closure,
- base::OnceClosure completion_closure) {
- std::move(success_closure).Run();
- std::move(completion_closure).Run();
- }
- void OnDeviceOpenedReadDescriptors(
- uint8_t manufacturer,
- uint8_t product,
- uint8_t serial_number,
- bool read_bos_descriptors,
- base::OnceClosure success_closure,
- base::OnceClosure failure_closure,
- base::OnceClosure completion_closure,
- scoped_refptr<UsbDeviceHandle> device_handle) {
- if (device_handle) {
- std::unique_ptr<std::map<uint8_t, std::u16string>> string_map(
- new std::map<uint8_t, std::u16string>());
- if (manufacturer != 0)
- (*string_map)[manufacturer] = std::u16string();
- if (product != 0)
- (*string_map)[product] = std::u16string();
- if (serial_number != 0)
- (*string_map)[serial_number] = std::u16string();
- int count = 0;
- if (!string_map->empty())
- count++;
- if (read_bos_descriptors)
- count++;
- DCHECK_GT(count, 0);
- base::OnceClosure success_and_completion_closure = base::BindOnce(
- &RunSuccessAndCompletionClosure, std::move(success_closure),
- std::move(completion_closure));
- base::RepeatingClosure barrier = base::BarrierClosure(
- count, base::BindOnce(&CloseHandleAndRunContinuation, device_handle,
- std::move(success_and_completion_closure)));
- if (!string_map->empty()) {
- scoped_refptr<UsbDeviceImpl> device =
- static_cast<UsbDeviceImpl*>(device_handle->GetDevice().get());
- ReadUsbStringDescriptors(
- device_handle, std::move(string_map),
- base::BindOnce(&SaveStringsAndRunContinuation, device, manufacturer,
- product, serial_number, barrier));
- }
- if (read_bos_descriptors) {
- ReadWebUsbDescriptors(
- device_handle,
- base::BindOnce(&OnReadBosDescriptor, device_handle, barrier));
- }
- } else {
- std::move(failure_closure).Run();
- std::move(completion_closure).Run();
- }
- }
- } // namespace
- UsbServiceImpl::UsbServiceImpl()
- : task_runner_(base::SequencedTaskRunnerHandle::Get()) {
- weak_self_ = weak_factory_.GetWeakPtr();
- base::ThreadPool::PostTaskAndReplyWithResult(
- FROM_HERE, kBlockingTaskTraits,
- base::BindOnce(&InitializeUsbContextBlocking),
- base::BindOnce(&UsbServiceImpl::OnUsbContext,
- weak_factory_.GetWeakPtr()));
- }
- UsbServiceImpl::~UsbServiceImpl() {
- NotifyWillDestroyUsbService();
- if (context_)
- libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_);
- }
- void UsbServiceImpl::GetDevices(GetDevicesCallback callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (usb_unavailable_) {
- task_runner_->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback),
- std::vector<scoped_refptr<UsbDevice>>()));
- return;
- }
- if (enumeration_in_progress_) {
- pending_enumeration_callbacks_.push_back(std::move(callback));
- return;
- }
- UsbService::GetDevices(std::move(callback));
- }
- void UsbServiceImpl::OnUsbContext(scoped_refptr<UsbContext> context) {
- if (!context) {
- usb_unavailable_ = true;
- return;
- }
- context_ = std::move(context);
- int rv = libusb_hotplug_register_callback(
- context_->context(),
- static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
- LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
- static_cast<libusb_hotplug_flag>(0), LIBUSB_HOTPLUG_MATCH_ANY,
- LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
- &UsbServiceImpl::HotplugCallback, this, &hotplug_handle_);
- if (rv != LIBUSB_SUCCESS) {
- usb_unavailable_ = true;
- context_.reset();
- return;
- }
- // This will call any enumeration callbacks queued while initializing.
- RefreshDevices();
- }
- void UsbServiceImpl::RefreshDevices() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(context_);
- DCHECK(!enumeration_in_progress_);
- enumeration_in_progress_ = true;
- DCHECK(devices_being_enumerated_.empty());
- base::ThreadPool::PostTaskAndReplyWithResult(
- FROM_HERE, kBlockingTaskTraits,
- base::BindOnce(&GetDeviceListBlocking, context_),
- base::BindOnce(&UsbServiceImpl::OnDeviceList,
- weak_factory_.GetWeakPtr()));
- }
- void UsbServiceImpl::OnDeviceList(
- absl::optional<std::vector<ScopedLibusbDeviceRef>> devices) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!devices) {
- RefreshDevicesComplete();
- return;
- }
- std::vector<ScopedLibusbDeviceRef> new_devices;
- // Look for new and existing devices.
- for (auto& device : *devices) {
- // Ignore devices that have failed enumeration previously.
- if (base::Contains(ignored_devices_, device.get()))
- continue;
- auto it = platform_devices_.find(device.get());
- if (it == platform_devices_.end()) {
- new_devices.push_back(std::move(device));
- } else {
- // Mark the existing device object visited and remove it from the list so
- // it will not be ignored.
- it->second->set_visited(true);
- device.Reset();
- }
- }
- // Remove devices not seen in this enumeration.
- for (PlatformDeviceMap::iterator it = platform_devices_.begin();
- it != platform_devices_.end();
- /* incremented internally */) {
- PlatformDeviceMap::iterator current = it++;
- const scoped_refptr<UsbDeviceImpl>& device = current->second;
- if (device->was_visited()) {
- device->set_visited(false);
- } else {
- RemoveDevice(device);
- }
- }
- // Remaining devices are being ignored. Clear the old list so that devices
- // that have been removed don't remain in |ignored_devices_| indefinitely.
- ignored_devices_.clear();
- for (auto& device : *devices) {
- if (device.IsValid())
- ignored_devices_.push_back(std::move(device));
- }
- // Enumerate new devices.
- base::RepeatingClosure refresh_complete = base::BarrierClosure(
- new_devices.size(),
- base::BindOnce(&UsbServiceImpl::RefreshDevicesComplete,
- weak_factory_.GetWeakPtr()));
- for (auto& device : new_devices)
- EnumerateDevice(std::move(device), refresh_complete);
- }
- void UsbServiceImpl::RefreshDevicesComplete() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(enumeration_in_progress_);
- enumeration_ready_ = true;
- enumeration_in_progress_ = false;
- devices_being_enumerated_.clear();
- if (!pending_enumeration_callbacks_.empty()) {
- std::vector<scoped_refptr<UsbDevice>> result;
- result.reserve(devices().size());
- for (const auto& map_entry : devices())
- result.push_back(map_entry.second);
- std::vector<GetDevicesCallback> callbacks;
- callbacks.swap(pending_enumeration_callbacks_);
- for (GetDevicesCallback& callback : callbacks)
- std::move(callback).Run(result);
- }
- }
- void UsbServiceImpl::EnumerateDevice(ScopedLibusbDeviceRef platform_device,
- base::OnceClosure refresh_complete) {
- DCHECK(context_);
- libusb_device_descriptor descriptor;
- int rv = libusb_get_device_descriptor(platform_device.get(), &descriptor);
- if (rv != LIBUSB_SUCCESS) {
- USB_LOG(EVENT) << "Failed to get device descriptor: "
- << ConvertPlatformUsbErrorToString(rv);
- EnumerationFailed(std::move(platform_device));
- std::move(refresh_complete).Run();
- return;
- }
- if (descriptor.bDeviceClass == LIBUSB_CLASS_HUB) {
- // Don't try to enumerate hubs. We never want to connect to a hub.
- EnumerationFailed(std::move(platform_device));
- std::move(refresh_complete).Run();
- return;
- }
- devices_being_enumerated_.insert(platform_device.get());
- auto device = base::MakeRefCounted<UsbDeviceImpl>(std::move(platform_device),
- descriptor);
- base::OnceClosure add_device = base::BindOnce(
- &UsbServiceImpl::AddDevice, weak_factory_.GetWeakPtr(), device);
- bool read_bos_descriptors = descriptor.bcdUSB >= kUsbVersion2_1;
- if (descriptor.iManufacturer == 0 && descriptor.iProduct == 0 &&
- descriptor.iSerialNumber == 0 && !read_bos_descriptors) {
- // Don't bother disturbing the device if it has no descriptors to offer.
- std::move(add_device).Run();
- std::move(refresh_complete).Run();
- } else {
- // Take an additional reference to the libusb_device object that will be
- // owned by this callback.
- libusb_ref_device(device->platform_device());
- base::OnceClosure enumeration_failed = base::BindOnce(
- &UsbServiceImpl::EnumerationFailed, weak_factory_.GetWeakPtr(),
- ScopedLibusbDeviceRef(device->platform_device(), context_));
- device->Open(base::BindOnce(
- &OnDeviceOpenedReadDescriptors, descriptor.iManufacturer,
- descriptor.iProduct, descriptor.iSerialNumber, read_bos_descriptors,
- std::move(add_device), std::move(enumeration_failed),
- std::move(refresh_complete)));
- }
- }
- void UsbServiceImpl::AddDevice(scoped_refptr<UsbDeviceImpl> device) {
- if (!base::Contains(devices_being_enumerated_, device->platform_device())) {
- // Device was removed while being enumerated.
- return;
- }
- DCHECK(!base::Contains(platform_devices_, device->platform_device()));
- platform_devices_[device->platform_device()] = device;
- DCHECK(!base::Contains(devices(), device->guid()));
- devices()[device->guid()] = device;
- USB_LOG(USER) << "USB device added: vendor=" << device->vendor_id() << " \""
- << device->manufacturer_string()
- << "\", product=" << device->product_id() << " \""
- << device->product_string() << "\", serial=\""
- << device->serial_number() << "\", guid=" << device->guid();
- if (enumeration_ready_)
- NotifyDeviceAdded(device);
- }
- void UsbServiceImpl::RemoveDevice(scoped_refptr<UsbDeviceImpl> device) {
- platform_devices_.erase(device->platform_device());
- devices().erase(device->guid());
- USB_LOG(USER) << "USB device removed: guid=" << device->guid();
- NotifyDeviceRemoved(device);
- device->OnDisconnect();
- }
- // static
- int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
- libusb_device* device_raw,
- libusb_hotplug_event event,
- void* user_data) {
- // It is safe to access the UsbServiceImpl* here because libusb takes a lock
- // around registering, deregistering and calling hotplug callback functions
- // and so guarantees that this function will not be called by the event
- // processing thread after it has been deregistered.
- UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data);
- // libusb does not transfer ownership of |device_raw| to this function so a
- // reference must be taken here.
- libusb_ref_device(device_raw);
- ScopedLibusbDeviceRef device(device_raw, self->context_);
- switch (event) {
- case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
- self->task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&UsbServiceImpl::OnPlatformDeviceAdded,
- self->weak_self_, std::move(device)));
- break;
- case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
- self->task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&UsbServiceImpl::OnPlatformDeviceRemoved,
- self->weak_self_, std::move(device)));
- break;
- default:
- NOTREACHED();
- }
- return 0;
- }
- void UsbServiceImpl::OnPlatformDeviceAdded(
- ScopedLibusbDeviceRef platform_device) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(!base::Contains(platform_devices_, platform_device.get()));
- EnumerateDevice(std::move(platform_device), base::DoNothing());
- }
- void UsbServiceImpl::OnPlatformDeviceRemoved(
- ScopedLibusbDeviceRef platform_device) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- auto it = platform_devices_.find(platform_device.get());
- if (it == platform_devices_.end())
- devices_being_enumerated_.erase(platform_device.get());
- else
- RemoveDevice(it->second);
- }
- void UsbServiceImpl::EnumerationFailed(ScopedLibusbDeviceRef platform_device) {
- ignored_devices_.push_back(std::move(platform_device));
- }
- } // namespace device
|