blob: d3655c58aa728f1ef250eb6952cfb71517a14dec [file] [log] [blame] [edit]
// 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_device_event_notifier.h"
#include <limits>
#include <base/logging.h>
#include <base/string_number_conversions.h>
#include <base/stringprintf.h>
#include "mist/event_dispatcher.h"
#include "mist/udev.h"
#include "mist/udev_device.h"
#include "mist/udev_monitor.h"
#include "mist/usb_device_event_observer.h"
using base::StringPrintf;
using std::string;
namespace mist {
namespace {
const char kAttributeIdProduct[] = "idProduct";
const char kAttributeIdVendor[] = "idVendor";
} // namespace
UsbDeviceEventNotifier::UsbDeviceEventNotifier(EventDispatcher* dispatcher)
: dispatcher_(dispatcher),
udev_monitor_file_descriptor_(UdevMonitor::kInvalidFileDescriptor) {
CHECK(dispatcher_);
}
UsbDeviceEventNotifier::~UsbDeviceEventNotifier() {
if (udev_monitor_file_descriptor_ != UdevMonitor::kInvalidFileDescriptor) {
dispatcher_->StopWatchingFileDescriptor(udev_monitor_file_descriptor_);
udev_monitor_file_descriptor_ = UdevMonitor::kInvalidFileDescriptor;
}
}
bool UsbDeviceEventNotifier::Initialize() {
udev_.reset(new Udev());
CHECK(udev_);
if (!udev_->Initialize()) {
LOG(ERROR) << "Could not create udev library context.";
return false;
}
udev_monitor_.reset(udev_->CreateMonitorFromNetlink("udev"));
if (!udev_monitor_) {
LOG(ERROR) << "Could not create udev monitor.";
return false;
}
if (!udev_monitor_->FilterAddMatchSubsystemDeviceType("usb", "usb_device")) {
LOG(ERROR) << "Could not add udev monitor filter.";
return false;
}
if (!udev_monitor_->EnableReceiving()) {
LOG(ERROR) << "Could not enable udev monitoring.";
return false;
}
udev_monitor_file_descriptor_ = udev_monitor_->GetFileDescriptor();
if (udev_monitor_file_descriptor_ == UdevMonitor::kInvalidFileDescriptor) {
LOG(ERROR) << "Could not get udev monitor file descriptor.";
return false;
}
if (!dispatcher_->StartWatchingFileDescriptor(
udev_monitor_file_descriptor_, MessageLoopForIO::WATCH_READ, this)) {
LOG(ERROR) << "Could not watch udev monitor file descriptor.";
return false;
}
return true;
}
void UsbDeviceEventNotifier::AddObserver(UsbDeviceEventObserver* observer) {
CHECK(observer);
observer_list_.AddObserver(observer);
}
void UsbDeviceEventNotifier::RemoveObserver(UsbDeviceEventObserver* observer) {
CHECK(observer);
observer_list_.RemoveObserver(observer);
}
void UsbDeviceEventNotifier::OnFileCanReadWithoutBlocking(int file_descriptor) {
VLOG(3) << StringPrintf("File descriptor %d available for read.",
file_descriptor);
scoped_ptr<UdevDevice> device(udev_monitor_->ReceiveDevice());
if (!device) {
LOG(WARNING) << "Ignore device event with no associated udev device.";
return;
}
VLOG(1) << StringPrintf("udev (SysPath=%s, Node=%s, Subsystem=%s, "
"DevType=%s, Action=%s, VendorId=%s, ProductId=%s)",
device->GetSysPath(),
device->GetDeviceNode(),
device->GetSubsystem(),
device->GetDeviceType(),
device->GetAction(),
device->GetSysAttributeValue(kAttributeIdVendor),
device->GetSysAttributeValue(kAttributeIdProduct));
string sys_path = ConvertNullToEmptyString(device->GetSysPath());
if (sys_path.empty()) {
LOG(WARNING) << "Ignore device event with no device sysfs path.";
return;
}
string action = ConvertNullToEmptyString(device->GetAction());
if (action == "add") {
string vendor_id_string = ConvertNullToEmptyString(
device->GetSysAttributeValue(kAttributeIdVendor));
uint16 vendor_id = 0;
if (!ConvertIdStringToValue(vendor_id_string, &vendor_id)) {
LOG(WARNING) << StringPrintf("Invalid USB vendor ID '%s'.",
vendor_id_string.c_str());
return;
}
string product_id_string = ConvertNullToEmptyString(
device->GetSysAttributeValue(kAttributeIdProduct));
uint16 product_id = 0;
if (!ConvertIdStringToValue(product_id_string, &product_id)) {
LOG(WARNING) << StringPrintf("Invalid USB product ID '%s'.",
product_id_string.c_str());
return;
}
FOR_EACH_OBSERVER(UsbDeviceEventObserver, observer_list_,
OnUsbDeviceAdded(sys_path, vendor_id, product_id));
return;
}
if (action == "remove") {
FOR_EACH_OBSERVER(UsbDeviceEventObserver, observer_list_,
OnUsbDeviceRemoved(sys_path));
}
}
void UsbDeviceEventNotifier::OnFileCanWriteWithoutBlocking(
int file_descriptor) {
NOTREACHED();
}
// static
std::string UsbDeviceEventNotifier::ConvertNullToEmptyString(const char* str) {
return str ? str : string();
}
// static
bool UsbDeviceEventNotifier::ConvertIdStringToValue(const string& id_string,
uint16* id) {
int value = -1;
if (id_string.size() != 4 ||
!base::HexStringToInt(id_string, &value) ||
value < 0 ||
value > std::numeric_limits<uint16>::max()) {
return false;
}
*id = static_cast<uint16>(value);
return true;
}
} // namespace mist