This source file includes following definitions.
- OnFileCanReadWithoutBlocking
- OnFileCanWriteWithoutBlocking
- Enumerate
- PlatformAddDevice
- PlatformRemoveDevice
#include <libudev.h>
#include <stdint.h>
#include <string>
#include <vector>
#include "base/logging.h"
#include "base/platform_file.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "device/hid/hid_connection_linux.h"
#include "device/hid/hid_device_info.h"
#include "device/hid/hid_service_linux.h"
namespace device {
namespace {
const char kUdevName[] = "udev";
const char kUdevActionAdd[] = "add";
const char kUdevActionRemove[] = "remove";
const char kHIDSubSystem[] = "hid";
const char kHIDID[] = "HID_ID";
const char kHIDName[] = "HID_NAME";
const char kHIDUnique[] = "HID_UNIQ";
}
HidServiceLinux::HidServiceLinux() {
udev_.reset(udev_new());
if (!udev_) {
LOG(ERROR) << "Failed to create udev.";
return;
}
monitor_.reset(udev_monitor_new_from_netlink(udev_.get(), kUdevName));
if (!monitor_) {
LOG(ERROR) << "Failed to create udev monitor.";
return;
}
int ret = udev_monitor_filter_add_match_subsystem_devtype(
monitor_.get(),
kHIDSubSystem,
NULL);
if (ret != 0) {
LOG(ERROR) << "Failed to add udev monitor filter.";
return;
}
ret = udev_monitor_enable_receiving(monitor_.get());
if (ret != 0) {
LOG(ERROR) << "Failed to start udev monitoring.";
return;
}
monitor_fd_ = udev_monitor_get_fd(monitor_.get());
if (monitor_fd_ <= 0) {
LOG(ERROR) << "Failed to start udev monitoring.";
return;
}
if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
monitor_fd_,
true,
base::MessageLoopForIO::WATCH_READ,
&monitor_watcher_,
this))
return;
Enumerate();
}
scoped_refptr<HidConnection> HidServiceLinux::Connect(
const HidDeviceId& device_id) {
HidDeviceInfo device_info;
if (!GetDeviceInfo(device_id, &device_info))
return NULL;
ScopedUdevDevicePtr hid_device(
udev_device_new_from_syspath(udev_.get(), device_info.device_id.c_str()));
if (hid_device) {
return new HidConnectionLinux(device_info, hid_device.Pass());
}
return NULL;
}
void HidServiceLinux::OnFileCanReadWithoutBlocking(int fd) {
DCHECK_EQ(monitor_fd_, fd);
ScopedUdevDevicePtr dev(udev_monitor_receive_device(monitor_.get()));
if (!dev)
return;
std::string action(udev_device_get_action(dev.get()));
if (action == kUdevActionAdd) {
PlatformAddDevice(dev.get());
} else if (action == kUdevActionRemove) {
PlatformRemoveDevice(dev.get());
}
}
void HidServiceLinux::OnFileCanWriteWithoutBlocking(int fd) {}
HidServiceLinux::~HidServiceLinux() {
monitor_watcher_.StopWatchingFileDescriptor();
close(monitor_fd_);
}
void HidServiceLinux::Enumerate() {
scoped_ptr<udev_enumerate, UdevEnumerateDeleter> enumerate(
udev_enumerate_new(udev_.get()));
if (!enumerate) {
LOG(ERROR) << "Failed to enumerate devices.";
return;
}
if (udev_enumerate_add_match_subsystem(enumerate.get(), kHIDSubSystem)) {
LOG(ERROR) << "Failed to enumerate devices.";
return;
}
if (udev_enumerate_scan_devices(enumerate.get()) != 0) {
LOG(ERROR) << "Failed to enumerate devices.";
return;
}
udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
for (udev_list_entry* i = devices; i != NULL;
i = udev_list_entry_get_next(i)) {
ScopedUdevDevicePtr hid_dev(
udev_device_new_from_syspath(udev_.get(), udev_list_entry_get_name(i)));
if (hid_dev) {
PlatformAddDevice(hid_dev.get());
}
}
}
void HidServiceLinux::PlatformAddDevice(udev_device* device) {
if (!device)
return;
const char* device_path = udev_device_get_syspath(device);
if (!device_path)
return;
HidDeviceInfo device_info;
device_info.device_id = device_path;
uint32_t int_property = 0;
const char* str_property = NULL;
const char* hid_id = udev_device_get_property_value(device, kHIDID);
if (!hid_id)
return;
std::vector<std::string> parts;
base::SplitString(hid_id, ':', &parts);
if (parts.size() != 3) {
return;
}
if (HexStringToUInt(base::StringPiece(parts[1]), &int_property)) {
device_info.vendor_id = int_property;
}
if (HexStringToUInt(base::StringPiece(parts[2]), &int_property)) {
device_info.product_id = int_property;
}
str_property = udev_device_get_property_value(device, kHIDUnique);
if (str_property != NULL)
device_info.serial_number = str_property;
str_property = udev_device_get_property_value(device, kHIDName);
if (str_property != NULL)
device_info.product_name = str_property;
AddDevice(device_info);
}
void HidServiceLinux::PlatformRemoveDevice(udev_device* raw_dev) {
const char* device_path = NULL;
device_path = udev_device_get_syspath(raw_dev);
if (device_path == NULL)
return;
RemoveDevice(device_path);
}
}