| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 // Copyright (c) 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 "device/hid/hid_connection_linux.h" | 
|  | 6 | 
|  | 7 #include <errno.h> | 
|  | 8 #include <fcntl.h> | 
|  | 9 #include <libudev.h> | 
|  | 10 #include <linux/hidraw.h> | 
|  | 11 #include <string> | 
|  | 12 | 
|  | 13 #include "base/threading/thread_restrictions.h" | 
|  | 14 #include "base/tuple.h" | 
|  | 15 #include "device/hid/hid_service.h" | 
|  | 16 #include "device/hid/hid_service_linux.h" | 
|  | 17 | 
|  | 18 | 
|  | 19 namespace device { | 
|  | 20 | 
|  | 21 namespace { | 
|  | 22 | 
|  | 23 const char kHidrawSubsystem[] = "hidraw"; | 
|  | 24 | 
|  | 25 }  // namespace | 
|  | 26 | 
|  | 27 HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info, | 
|  | 28                                        ScopedUdevDevicePtr udev_raw_device) | 
|  | 29     : HidConnection(device_info), | 
|  | 30       initialized_(false) { | 
|  | 31   DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | 32 | 
|  | 33   udev_device* dev = udev_raw_device.get(); | 
|  | 34   std::string dev_node; | 
|  | 35   if (!FindHidrawDevNode(dev, &dev_node)) { | 
|  | 36     LOG(ERROR) << "Cannot open HID device as hidraw device."; | 
|  | 37     return; | 
|  | 38   } | 
|  | 39 | 
|  | 40   base::PlatformFileError error; | 
|  | 41 | 
|  | 42   int flags = base::PLATFORM_FILE_OPEN | | 
|  | 43               base::PLATFORM_FILE_READ | | 
|  | 44               base::PLATFORM_FILE_WRITE | | 
|  | 45               base::PLATFORM_FILE_EXCLUSIVE_READ | | 
|  | 46               base::PLATFORM_FILE_EXCLUSIVE_WRITE; | 
|  | 47 | 
|  | 48   base::PlatformFile device_file = base::CreatePlatformFile( | 
|  | 49       base::FilePath(dev_node), | 
|  | 50       flags, | 
|  | 51       NULL, | 
|  | 52       &error); | 
|  | 53   if (error || device_file <= 0) { | 
|  | 54     LOG(ERROR) << error; | 
|  | 55     if (device_file) | 
|  | 56       base::ClosePlatformFile(device_file); | 
|  | 57     return; | 
|  | 58   } | 
|  | 59   if (fcntl(device_file, F_SETFL, fcntl(device_file, F_GETFL) | O_NONBLOCK)) { | 
|  | 60     PLOG(ERROR) << "Failed to set non-blocking flag to device file."; | 
|  | 61     return; | 
|  | 62   } | 
|  | 63   device_file_ = device_file; | 
|  | 64 | 
|  | 65   if (!base::MessageLoopForIO::current()->WatchFileDescriptor( | 
|  | 66       device_file_, | 
|  | 67       true, | 
|  | 68       base::MessageLoopForIO::WATCH_READ_WRITE, | 
|  | 69       &device_file_watcher_, | 
|  | 70       this)) { | 
|  | 71     LOG(ERROR) << "Cannot start watching file descriptor."; | 
|  | 72     return; | 
|  | 73   } | 
|  | 74 | 
|  | 75   initialized_ = true; | 
|  | 76 } | 
|  | 77 | 
|  | 78 HidConnectionLinux::~HidConnectionLinux() { | 
|  | 79   DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | 80   Disconnect(); | 
|  | 81 } | 
|  | 82 | 
|  | 83 void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) { | 
|  | 84   DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | 85   DCHECK_EQ(fd, device_file_); | 
|  | 86   DCHECK(initialized_); | 
|  | 87 | 
|  | 88   uint8 buffer[1024] = {0}; | 
|  | 89   int bytes = read(device_file_, buffer, 1024); | 
|  | 90   if (bytes < 0) { | 
|  | 91     if (errno == EAGAIN) { | 
|  | 92       return; | 
|  | 93     } | 
|  | 94     Disconnect(); | 
|  | 95     return; | 
|  | 96   } | 
|  | 97   scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(bytes)); | 
|  | 98   memcpy(io_buffer->data(), buffer, bytes); | 
|  | 99   input_reports_.push(std::make_pair(io_buffer, bytes)); | 
|  | 100 | 
|  | 101   ProcessReadQueue(); | 
|  | 102 } | 
|  | 103 | 
|  | 104 void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {} | 
|  | 105 | 
|  | 106 void HidConnectionLinux::Disconnect() { | 
|  | 107   DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | 108   if (!initialized_) | 
|  | 109     return; | 
|  | 110 | 
|  | 111   initialized_ = false; | 
|  | 112   device_file_watcher_.StopWatchingFileDescriptor(); | 
|  | 113   close(device_file_); | 
|  | 114   while (!read_queue_.empty()) { | 
|  | 115     PendingRequest callback = read_queue_.front(); | 
|  | 116     read_queue_.pop(); | 
|  | 117     callback.c.Run(false, 0); | 
|  | 118   } | 
|  | 119 } | 
|  | 120 | 
|  | 121 void HidConnectionLinux::Read(scoped_refptr<net::IOBuffer> buffer, | 
|  | 122                               size_t size, | 
|  | 123                               const IOCallback& callback) { | 
|  | 124   DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | 125   if (!initialized_) { | 
|  | 126     DCHECK(read_queue_.empty()); | 
|  | 127     // There might be unread reports. | 
|  | 128     if (!input_reports_.empty()){ | 
|  | 129       read_queue_.push(MakeTuple(buffer, size, callback)); | 
|  | 130       ProcessReadQueue(); | 
|  | 131     } | 
|  | 132     callback.Run(false, 0); | 
|  | 133     return; | 
|  | 134   } else { | 
|  | 135     read_queue_.push(MakeTuple(buffer, size, callback)); | 
|  | 136     ProcessReadQueue(); | 
|  | 137   } | 
|  | 138 } | 
|  | 139 | 
|  | 140 void HidConnectionLinux::Write(scoped_refptr<net::IOBuffer> buffer, | 
|  | 141                                size_t size, | 
|  | 142                                const IOCallback& callback) { | 
|  | 143   DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | 144   if (!initialized_) { | 
|  | 145     callback.Run(false, 0); | 
|  | 146     return; | 
|  | 147   } else { | 
|  | 148     int bytes = write(device_file_, buffer->data(), size); | 
|  | 149     if (bytes < 0) { | 
|  | 150       Disconnect(); | 
|  | 151       callback.Run(false, 0); | 
|  | 152     } else { | 
|  | 153       callback.Run(true, bytes); | 
|  | 154     } | 
|  | 155   } | 
|  | 156 } | 
|  | 157 | 
|  | 158 void HidConnectionLinux::GetFeatureReport(scoped_refptr<net::IOBuffer> buffer, | 
|  | 159                                           size_t size, | 
|  | 160                                           const IOCallback& callback) { | 
|  | 161   DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | 162   if (!initialized_) { | 
|  | 163     callback.Run(false, 0); | 
|  | 164     return; | 
|  | 165   } | 
|  | 166   NOTIMPLEMENTED(); | 
|  | 167 } | 
|  | 168 | 
|  | 169 void HidConnectionLinux::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer, | 
|  | 170                                            size_t size, | 
|  | 171                                            const IOCallback& callback) { | 
|  | 172   DCHECK(thread_checker_.CalledOnValidThread()); | 
|  | 173   if (!initialized_) { | 
|  | 174     callback.Run(false, 0); | 
|  | 175     return; | 
|  | 176   } | 
|  | 177   NOTIMPLEMENTED(); | 
|  | 178 } | 
|  | 179 | 
|  | 180 void HidConnectionLinux::ProcessReadQueue() { | 
|  | 181   while(read_queue_.size() && input_reports_.size()) { | 
|  | 182     PendingRequest request = read_queue_.front(); | 
|  | 183     read_queue_.pop(); | 
|  | 184     PendingReport report = input_reports_.front(); | 
|  | 185     if (report.second > request.b) { | 
|  | 186       request.c.Run(false, report.second); | 
|  | 187     } else { | 
|  | 188       memcpy(request.a->data(), report.first->data(), report.second); | 
|  | 189       input_reports_.pop(); | 
|  | 190       request.c.Run(true, report.second); | 
|  | 191     } | 
|  | 192   } | 
|  | 193 } | 
|  | 194 | 
|  | 195 bool HidConnectionLinux::FindHidrawDevNode(udev_device* parent, | 
|  | 196                                            std::string* result) { | 
|  | 197   udev* udev = udev_device_get_udev(parent); | 
|  | 198   if (!udev) | 
|  | 199       return false; | 
|  | 200 | 
|  | 201   ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev)); | 
|  | 202   if (!enumerate) | 
|  | 203     return false; | 
|  | 204 | 
|  | 205   if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) { | 
|  | 206     return false; | 
|  | 207   } | 
|  | 208   if (udev_enumerate_scan_devices(enumerate.get())) { | 
|  | 209     return false; | 
|  | 210   } | 
|  | 211 | 
|  | 212   const char* parent_path = udev_device_get_devpath(parent); | 
|  | 213   udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get()); | 
|  | 214   for (udev_list_entry* i = devices; i != NULL; | 
|  | 215       i = udev_list_entry_get_next(i)) { | 
|  | 216     ScopedUdevDevicePtr hid_dev( | 
|  | 217         udev_device_new_from_syspath(udev, udev_list_entry_get_name(i))); | 
|  | 218     const char* raw_path = udev_device_get_devnode(hid_dev.get()); | 
|  | 219     if (strncmp(parent_path, | 
|  | 220                 udev_device_get_devpath(hid_dev.get()), | 
|  | 221                 strlen(parent_path)) == 0 && | 
|  | 222         raw_path) { | 
|  | 223       *result = raw_path; | 
|  | 224       return true; | 
|  | 225     } | 
|  | 226   } | 
|  | 227 | 
|  | 228   return false; | 
|  | 229 } | 
|  | 230 | 
|  | 231 }  // namespace device | 
| OLD | NEW | 
|---|