blob: 12d7d9adadca83073f09d74e2a1492493953aec2 [file] [log] [blame]
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +02001// Copyright 2017 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <algorithm>
6#include <map>
7#include <utility>
8#include <vector>
9
10#include <fcntl.h>
11#include <stdio.h>
12#include <unistd.h>
13
14#include <base/bind.h>
15#include <base/callback.h>
Qijiang Fan713061e2021-03-08 15:45:12 +090016#include <base/check.h>
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +020017#include <base/files/file_util.h>
18#include <base/logging.h>
Qijiang Fan886c4692021-02-19 11:54:10 +090019#include <base/notreached.h>
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +020020#include <base/posix/eintr_wrapper.h>
21#include <base/strings/string_number_conversions.h>
22#include <base/strings/string_util.h>
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +020023
24#include "u2fd/uhid_device.h"
25
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +020026namespace u2f {
27
28namespace {
29
30const char kUHidNode[] = "/dev/uhid";
31
32const char* GetUhidEventName(int event) {
33 switch (event) {
34 case UHID_START:
35 return "START";
36 case UHID_STOP:
37 return "STOP";
38 case UHID_OPEN:
39 return "OPEN";
40 case UHID_CLOSE:
41 return "CLOSE";
42 case UHID_OUTPUT:
43 return "OUTPUT";
44 default:
45 NOTREACHED();
46 return "UNKNOWN";
47 }
48}
49
50} // namespace
51
52UHidDevice::UHidDevice(uint32_t vendor_id,
53 uint32_t product_id,
54 const std::string& name,
55 const std::string& phys)
56 : created_(false),
57 vendor_id_(vendor_id),
58 product_id_(product_id),
59 name_(name),
60 phys_(phys) {}
61
62UHidDevice::~UHidDevice() {
63 if (created_)
64 DestroyDev();
65}
66
67bool UHidDevice::Init(uint32_t hid_version, const std::string& report_desc) {
68 DCHECK(!fd_.is_valid());
69 fd_ = base::ScopedFD(HANDLE_EINTR(open(kUHidNode, O_RDWR)));
70 if (!fd_.is_valid()) {
71 PLOG(ERROR) << "Cannot open uhid node at " << kUHidNode;
72 return false;
73 }
74 VLOG(1) << kUHidNode << " opened successfully.";
75
76 if (!CreateDev(hid_version, report_desc)) {
77 LOG(ERROR) << "Cannot create HID device.";
78 return false;
79 }
80 created_ = true;
81
Qijiang Fan356bf272019-11-18 16:13:57 +090082 watcher_ = base::FileDescriptorWatcher::WatchReadable(
83 fd_.get(), base::Bind(&UHidDevice::FdEvent, base::Unretained(this)));
84 if (!watcher_) {
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +020085 LOG(ERROR) << "Unable to watch " << kUHidNode << " events";
86 return false;
87 }
88
89 return true;
90}
91
92void UHidDevice::FdEvent() {
93 struct uhid_event ev;
94
95 ssize_t ret = read(fd_.get(), &ev, sizeof(ev));
96 if (ret < 0) {
97 PLOG(ERROR) << "Cannot read uhid";
98 return;
99 }
100 if (ret != sizeof(ev)) {
101 LOG(ERROR) << "Read " << ret << " byte(s) from " << kUHidNode
102 << "; expected " << sizeof(ev);
103 return;
104 }
105
106 switch (ev.type) {
107 case UHID_START:
108 case UHID_STOP:
109 case UHID_OPEN:
110 case UHID_CLOSE:
111 VLOG(2) << "uhid event " << GetUhidEventName(ev.type);
112 break;
113 case UHID_OUTPUT:
114 VLOG(1) << "uhid event " << GetUhidEventName(ev.type);
115 if (ev.u.output.rtype != UHID_OUTPUT_REPORT)
116 break;
117 VLOG(2) << "HID Report: "
118 << base::HexEncode(ev.u.output.data, ev.u.output.size);
119
120 if (!on_output_report_.is_null()) {
121 std::string report(reinterpret_cast<char*>(ev.u.output.data),
122 ev.u.output.size);
123 on_output_report_.Run(report);
124 }
125 break;
126 default:
127 LOG(WARNING) << "Invalid event from uhid: " << ev.type;
128 }
129}
130
131bool UHidDevice::WriteEvent(const struct uhid_event& ev) {
132 return base::WriteFileDescriptor(
133 fd_.get(), reinterpret_cast<const char*>(&ev), sizeof(ev));
134}
135
136bool UHidDevice::SendReport(const std::string& report) {
137 if (report.size() > UINT16_MAX)
138 return false;
139
140 struct uhid_event ev = {
141 .type = UHID_INPUT2,
142 .u.input2.size = static_cast<uint16_t>(report.size()),
143 };
144
145 if (report.size() > sizeof(ev.u.input2.data))
146 return false;
147
148 std::copy(report.begin(), report.end(), ev.u.input2.data);
149
150 return WriteEvent(ev) == 0;
151}
152
153bool UHidDevice::CreateDev(uint32_t interface_version,
154 const std::string& report_desc) {
155 if (report_desc.size() > UINT16_MAX)
156 return false;
157
158 struct uhid_event ev = {
159 .type = UHID_CREATE2,
160 .u.create2.rd_size = static_cast<uint16_t>(report_desc.size()),
161 .u.create2.bus = BUS_VIRTUAL,
162 .u.create2.vendor = vendor_id_,
163 .u.create2.product = product_id_,
164 .u.create2.version = interface_version,
165 };
166 if (report_desc.size() > sizeof(ev.u.create2.rd_data))
167 return false;
168
Tom Hughes0f7203b2020-08-24 18:29:15 -0700169 base::strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name_.c_str(),
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +0200170 sizeof(ev.u.create2.name));
171 snprintf(reinterpret_cast<char*>(ev.u.create2.phys),
Tom Hughes0f7203b2020-08-24 18:29:15 -0700172 sizeof(ev.u.create2.phys), "%s-%04X:%04X", phys_.c_str(),
173 ev.u.create2.vendor, ev.u.create2.product);
174 memcpy(ev.u.create2.rd_data, report_desc.data(),
Vincent Palatinc6c7e4e2017-06-15 15:45:05 +0200175 std::min(report_desc.size(), sizeof(ev.u.create2.rd_data)));
176
177 return WriteEvent(ev);
178}
179
180void UHidDevice::DestroyDev() {
181 const struct uhid_event ev = {.type = UHID_DESTROY};
182
183 WriteEvent(ev);
184}
185
186void UHidDevice::SetOutputReportHandler(
187 const HidInterface::OutputReportCallback& on_output_report) {
188 on_output_report_ = on_output_report;
189}
190
191} // namespace u2f