| OLD | NEW | 
|---|
|  | (Empty) | 
| 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 "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_parent(enumerate.get(), parent)) { |  | 
| 206     return false; |  | 
| 207   } |  | 
| 208 |  | 
| 209   if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) { |  | 
| 210     return false; |  | 
| 211   } |  | 
| 212   if (udev_enumerate_scan_devices(enumerate.get())) { |  | 
| 213     return false; |  | 
| 214   } |  | 
| 215 |  | 
| 216   udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get()); |  | 
| 217   for (udev_list_entry* i = devices; i != NULL; |  | 
| 218       i = udev_list_entry_get_next(i)) { |  | 
| 219     ScopedUdevDevicePtr hid_dev( |  | 
| 220         udev_device_new_from_syspath(udev, udev_list_entry_get_name(i))); |  | 
| 221     const char* raw_path = udev_device_get_devnode(hid_dev.get()); |  | 
| 222     if (raw_path) { |  | 
| 223       *result = raw_path; |  | 
| 224       return true; |  | 
| 225     } |  | 
| 226   } |  | 
| 227 |  | 
| 228   return false; |  | 
| 229 } |  | 
| 230 |  | 
| 231 }  // namespace device |  | 
| OLD | NEW | 
|---|