| // Copyright (c) 2013 The Chromium OS 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 "mist/usb_manager.h" |
| |
| #include <libusb.h> |
| #include <poll.h> |
| |
| #include <base/logging.h> |
| #include <base/stringprintf.h> |
| |
| #include "mist/event_dispatcher.h" |
| #include "mist/usb_device.h" |
| #include "mist/usb_device_descriptor.h" |
| |
| using base::StringPrintf; |
| |
| namespace mist { |
| |
| namespace { |
| |
| MessageLoopForIO::Mode ConvertEventFlagsToWatchMode(short events) { // NOLINT |
| if ((events & POLLIN) && (events & POLLOUT)) |
| return MessageLoopForIO::WATCH_READ_WRITE; |
| |
| if (events & POLLIN) |
| return MessageLoopForIO::WATCH_READ; |
| |
| if (events & POLLOUT) |
| return MessageLoopForIO::WATCH_WRITE; |
| |
| return MessageLoopForIO::WATCH_READ_WRITE; |
| } |
| |
| } // namespace |
| |
| UsbManager::UsbManager(EventDispatcher* dispatcher) |
| : dispatcher_(dispatcher), |
| context_(NULL) {} |
| |
| UsbManager::~UsbManager() { |
| if (context_) { |
| libusb_exit(context_); |
| context_ = NULL; |
| } |
| } |
| |
| bool UsbManager::Initialize() { |
| CHECK(!context_); |
| |
| int result = libusb_init(&context_); |
| if (!error_.SetFromLibUsbError(static_cast<libusb_error>(result))) { |
| LOG(ERROR) << "Could not initialize libusb: " << error_; |
| return false; |
| } |
| |
| if (!StartWatchingPollFileDescriptors()) { |
| error_.set_type(UsbError::kErrorNotSupported); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void UsbManager::SetDebugLevel(int level) { |
| CHECK(context_); |
| |
| libusb_set_debug(context_, level); |
| } |
| |
| UsbDevice* UsbManager::GetDevice(uint8 bus_number, |
| uint8 device_address, |
| uint16 vendor_id, |
| uint16 product_id) { |
| ScopedVector<UsbDevice> devices; |
| if (!GetDevices(&devices)) |
| return NULL; |
| |
| for (ScopedVector<UsbDevice>::iterator it = devices.begin(); |
| it != devices.end(); |
| ++it) { |
| UsbDevice* device = *it; |
| if (device->GetBusNumber() != bus_number || |
| device->GetDeviceAddress() != device_address) |
| continue; |
| |
| scoped_ptr<UsbDeviceDescriptor> device_descriptor( |
| (*it)->GetDeviceDescriptor()); |
| VLOG(2) << *device_descriptor; |
| if (device_descriptor->GetVendorId() == vendor_id && |
| device_descriptor->GetProductId() == product_id) { |
| devices.weak_erase(it); |
| return device; |
| } |
| } |
| |
| error_.set_type(UsbError::kErrorNotFound); |
| return NULL; |
| } |
| |
| bool UsbManager::GetDevices(ScopedVector<UsbDevice>* devices) { |
| CHECK(context_); |
| CHECK(devices); |
| |
| devices->clear(); |
| |
| libusb_device** device_list = NULL; |
| ssize_t result = libusb_get_device_list(context_, &device_list); |
| if (result < 0) |
| return error_.SetFromLibUsbError(static_cast<libusb_error>(result)); |
| |
| for (ssize_t i = 0; i < result; ++i) { |
| devices->push_back(new UsbDevice(device_list[i])); |
| } |
| |
| // UsbDevice holds a reference count of a libusb_device struct. Thus, |
| // decrement the reference count of the libusb_device struct in the list by |
| // one when freeing the list. |
| libusb_free_device_list(device_list, 1); |
| return true; |
| } |
| |
| void UsbManager::OnPollFileDescriptorAdded(int file_descriptor, |
| short events, // NOLINT |
| void* user_data) { |
| CHECK(user_data); |
| |
| VLOG(2) << StringPrintf("Poll file descriptor %d on events 0x%016x added.", |
| file_descriptor, |
| events); |
| UsbManager* manager = reinterpret_cast<UsbManager*>(user_data); |
| manager->dispatcher_->StartWatchingFileDescriptor( |
| file_descriptor, ConvertEventFlagsToWatchMode(events), manager); |
| } |
| |
| void UsbManager::OnPollFileDescriptorRemoved(int file_descriptor, |
| void* user_data) { |
| CHECK(user_data); |
| |
| VLOG(2) << StringPrintf("Poll file descriptor %d removed.", file_descriptor); |
| UsbManager* manager = reinterpret_cast<UsbManager*>(user_data); |
| manager->dispatcher_->StopWatchingFileDescriptor(file_descriptor); |
| } |
| |
| bool UsbManager::StartWatchingPollFileDescriptors() { |
| CHECK(context_); |
| |
| libusb_set_pollfd_notifiers( |
| context_, &OnPollFileDescriptorAdded, &OnPollFileDescriptorRemoved, this); |
| |
| scoped_ptr<const libusb_pollfd*, base::FreeDeleter> pollfd_list( |
| libusb_get_pollfds(context_)); |
| if (!pollfd_list) { |
| LOG(ERROR) << "Could not get file descriptors for monitoring USB events."; |
| return false; |
| } |
| |
| for (const libusb_pollfd** fd_ptr = pollfd_list.get(); *fd_ptr; ++fd_ptr) { |
| const libusb_pollfd& pollfd = *(*fd_ptr); |
| VLOG(2) << StringPrintf("Poll file descriptor %d for events 0x%016x added.", |
| pollfd.fd, |
| pollfd.events); |
| if (!dispatcher_->StartWatchingFileDescriptor( |
| pollfd.fd, ConvertEventFlagsToWatchMode(pollfd.events), this)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void UsbManager::HandleEventsNonBlocking() { |
| CHECK(context_); |
| |
| timeval zero_tv = {0}; |
| int result = libusb_handle_events_timeout_completed(context_, &zero_tv, NULL); |
| UsbError error(static_cast<libusb_error>(result)); |
| LOG_IF(ERROR, !error.IsSuccess()) << "Could not handle USB events: " << error; |
| } |
| |
| void UsbManager::OnFileCanReadWithoutBlocking(int file_descriptor) { |
| VLOG(3) << StringPrintf("File descriptor %d available for read.", |
| file_descriptor); |
| HandleEventsNonBlocking(); |
| } |
| |
| void UsbManager::OnFileCanWriteWithoutBlocking(int file_descriptor) { |
| VLOG(3) << StringPrintf("File descriptor %d available for write.", |
| file_descriptor); |
| HandleEventsNonBlocking(); |
| } |
| |
| } // namespace mist |