Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / ui / events / ozone / evdev / device_manager_udev.cc
1 // Copyright 2014 The Chromium 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 "ui/events/ozone/evdev/device_manager_udev.h"
6
7 #include <libudev.h>
8
9 #include "base/debug/trace_event.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_pump_ozone.h"
13 #include "base/strings/stringprintf.h"
14 #include "ui/events/ozone/evdev/device_manager_evdev.h"
15 #include "ui/events/ozone/evdev/event_factory_evdev.h"
16 #include "ui/events/ozone/evdev/scoped_udev.h"
17
18 namespace ui {
19
20 namespace {
21
22 const char kSubsystemInput[] = "input";
23
24 // Severity levels from syslog.h. We can't include it directly as it
25 // conflicts with base/logging.h
26 enum {
27   SYS_LOG_EMERG = 0,
28   SYS_LOG_ALERT = 1,
29   SYS_LOG_CRIT = 2,
30   SYS_LOG_ERR = 3,
31   SYS_LOG_WARNING = 4,
32   SYS_LOG_NOTICE = 5,
33   SYS_LOG_INFO = 6,
34   SYS_LOG_DEBUG = 7,
35 };
36
37 // Log handler for messages generated from libudev.
38 void UdevLog(struct udev* udev,
39              int priority,
40              const char* file,
41              int line,
42              const char* fn,
43              const char* format,
44              va_list args) {
45   if (priority <= SYS_LOG_ERR)
46     LOG(ERROR) << "libudev: " << fn << ": " << base::StringPrintV(format, args);
47   else if (priority <= SYS_LOG_INFO)
48     VLOG(1) << "libudev: " << fn << ": " << base::StringPrintV(format, args);
49   else  // SYS_LOG_DEBUG
50     VLOG(2) << "libudev: " << fn << ": " << base::StringPrintV(format, args);
51 }
52
53 // Create libudev context.
54 scoped_udev UdevCreate() {
55   struct udev* udev = udev_new();
56   if (udev) {
57     udev_set_log_fn(udev, UdevLog);
58     udev_set_log_priority(udev, SYS_LOG_DEBUG);
59   }
60   return scoped_udev(udev);
61 }
62
63 // Start monitoring input device changes.
64 scoped_udev_monitor UdevCreateMonitor(struct udev* udev) {
65   struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev, "udev");
66   if (monitor) {
67     udev_monitor_filter_add_match_subsystem_devtype(
68         monitor, kSubsystemInput, NULL);
69
70     if (udev_monitor_enable_receiving(monitor))
71       LOG(ERROR) << "failed to start receiving events from udev";
72   }
73
74   return scoped_udev_monitor(monitor);
75 }
76
77 // Enumerate all input devices using udev. Calls device_callback per device.
78 bool UdevEnumerateInputDevices(struct udev* udev,
79                                const EvdevDeviceCallback& device_callback) {
80   scoped_udev_enumerate enumerate(udev_enumerate_new(udev));
81   if (!enumerate)
82     return false;
83
84   // Build list of devices with subsystem "input".
85   udev_enumerate_add_match_subsystem(enumerate.get(), kSubsystemInput);
86   udev_enumerate_scan_devices(enumerate.get());
87
88   struct udev_list_entry* devices =
89       udev_enumerate_get_list_entry(enumerate.get());
90   struct udev_list_entry* entry;
91
92   // Run callback per device in the list.
93   udev_list_entry_foreach(entry, devices) {
94     const char* name = udev_list_entry_get_name(entry);
95
96     scoped_udev_device device(udev_device_new_from_syspath(udev, name));
97     if (!device)
98       continue;
99
100     const char* path = udev_device_get_devnode(device.get());
101     if (!path)
102       continue;
103
104     // Found input device node; attach.
105     device_callback.Run(base::FilePath(path));
106   }
107
108   return true;
109 }
110
111 // Device enumerator & monitor using udev.
112 //
113 // This class enumerates input devices attached to the system using udev.
114 //
115 // It also creates & monitors a udev netlink socket and issues callbacks for
116 // any changes to the set of attached devices.
117 //
118 // TODO(spang): Figure out how to test this.
119 class DeviceManagerUdev : public DeviceManagerEvdev,
120                           base::MessagePumpLibevent::Watcher {
121  public:
122   DeviceManagerUdev() {}
123   virtual ~DeviceManagerUdev() { Stop(); }
124
125   // Enumerate existing devices & start watching for device changes.
126   virtual void ScanAndStartMonitoring(const EvdevDeviceCallback& device_added,
127                                       const EvdevDeviceCallback& device_removed)
128       OVERRIDE {
129     udev_ = UdevCreate();
130     if (!udev_) {
131       LOG(ERROR) << "failed to initialize libudev";
132       return;
133     }
134
135     if (!StartMonitoring(device_added, device_removed))
136       LOG(ERROR) << "failed to start monitoring device changes via udev";
137
138     if (!UdevEnumerateInputDevices(udev_.get(), device_added))
139       LOG(ERROR) << "failed to enumerate input devices via udev";
140   }
141
142   virtual void Stop() OVERRIDE {
143     controller_.StopWatchingFileDescriptor();
144     device_added_.Reset();
145     device_removed_.Reset();
146   }
147
148   virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
149     // The netlink socket should never become disconnected. There's no need
150     // to handle broken connections here.
151     TRACE_EVENT1("ozone", "UdevDeviceChange", "socket", fd);
152
153     scoped_udev_device device(udev_monitor_receive_device(udev_monitor_.get()));
154     if (!device)
155       return;
156
157     const char* path = udev_device_get_devnode(device.get());
158     const char* action = udev_device_get_action(device.get());
159     if (!path || !action)
160       return;
161
162     if (!strcmp(action, "add") || !strcmp(action, "change"))
163       device_added_.Run(base::FilePath(path));
164     else if (!strcmp(action, "remove"))
165       device_removed_.Run(base::FilePath(path));
166   }
167
168   virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE { NOTREACHED(); }
169
170  private:
171   bool StartMonitoring(const EvdevDeviceCallback& device_added,
172                        const EvdevDeviceCallback& device_removed) {
173     udev_monitor_ = UdevCreateMonitor(udev_.get());
174     if (!udev_monitor_)
175       return false;
176
177     // Grab monitor socket.
178     int fd = udev_monitor_get_fd(udev_monitor_.get());
179     if (fd < 0)
180       return false;
181
182     // Save callbacks.
183     device_added_ = device_added;
184     device_removed_ = device_removed;
185
186     // Watch for incoming events on monitor socket.
187     return base::MessagePumpOzone::Current()->WatchFileDescriptor(
188         fd, true, base::MessagePumpOzone::WATCH_READ, &controller_, this);
189   }
190
191   // Udev daemon connection.
192   scoped_udev udev_;
193
194   // Udev device change monitor.
195   scoped_udev_monitor udev_monitor_;
196
197   // Callbacks for device changes.
198   EvdevDeviceCallback device_added_;
199   EvdevDeviceCallback device_removed_;
200
201   // Watcher for uevent netlink socket.
202   base::MessagePumpLibevent::FileDescriptorWatcher controller_;
203
204   DISALLOW_COPY_AND_ASSIGN(DeviceManagerUdev);
205 };
206
207 }  // namespace
208
209 scoped_ptr<DeviceManagerEvdev> CreateDeviceManagerUdev() {
210   return make_scoped_ptr<DeviceManagerEvdev>(new DeviceManagerUdev());
211 }
212
213 }  // namespace ui