| OLD | NEW | 
|    1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. |    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 |    2 // Use of this source code is governed by a BSD-style license that can be | 
|    3 // found in the LICENSE file. |    3 // found in the LICENSE file. | 
|    4  |    4  | 
|    5 #include "device/hid/hid_connection_mac.h" |    5 #include "device/hid/hid_connection_mac.h" | 
|    6  |    6  | 
|    7 #include "base/bind.h" |    7 #include "base/bind.h" | 
|    8 #include "base/callback.h" |  | 
|    9 #include "base/mac/foundation_util.h" |    8 #include "base/mac/foundation_util.h" | 
 |    9 #include "base/message_loop/message_loop.h" | 
|   10 #include "base/threading/thread_restrictions.h" |   10 #include "base/threading/thread_restrictions.h" | 
|   11 #include "base/tuple.h" |   11 #include "device/hid/hid_connection_mac.h" | 
|   12 #include "device/hid/hid_service.h" |  | 
|   13 #include "device/hid/hid_service_mac.h" |  | 
|   14 #include "net/base/io_buffer.h" |  | 
|   15  |  | 
|   16 #include <CoreFoundation/CoreFoundation.h> |  | 
|   17 #include <IOKit/hid/IOHIDManager.h> |  | 
|   18  |   12  | 
|   19 namespace device { |   13 namespace device { | 
|   20  |   14  | 
|   21 HidConnectionMac::HidConnectionMac(HidServiceMac* service, |   15 HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info) | 
|   22                                    HidDeviceInfo device_info, |  | 
|   23                                    IOHIDDeviceRef device) |  | 
|   24     : HidConnection(device_info), |   16     : HidConnection(device_info), | 
|   25       service_(service), |   17       device_(device_info.device_id, base::scoped_policy::RETAIN) { | 
|   26       device_(device), |  | 
|   27       disconnected_(false) { |  | 
|   28   DCHECK(thread_checker_.CalledOnValidThread()); |   18   DCHECK(thread_checker_.CalledOnValidThread()); | 
|   29  |   19  | 
|   30   message_loop_ = base::MessageLoopProxy::current(); |   20   message_loop_ = base::MessageLoopProxy::current(); | 
|   31  |   21  | 
|   32   CFRetain(device); |   22   DCHECK(device_.get()); | 
|   33   inbound_buffer_.reset((uint8_t*) malloc(device_info.input_report_size + 1)); |   23   inbound_buffer_.reset((uint8_t*)malloc(device_info.input_report_size)); | 
|   34   IOHIDDeviceRegisterInputReportCallback( |   24   IOHIDDeviceRegisterInputReportCallback(device_.get(), | 
|   35       device_.get(), |   25                                          inbound_buffer_.get(), | 
|   36       inbound_buffer_.get(), |   26                                          device_info.input_report_size, | 
|   37       device_info.input_report_size + 1, |   27                                          &HidConnectionMac::InputReportCallback, | 
|   38       &HidConnectionMac::InputReportCallback, |   28                                          this); | 
|   39       this); |  | 
|   40   IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone); |   29   IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone); | 
|   41 } |   30 } | 
 |   31  | 
|   42 HidConnectionMac::~HidConnectionMac() { |   32 HidConnectionMac::~HidConnectionMac() { | 
|   43   DCHECK(thread_checker_.CalledOnValidThread()); |   33   DCHECK(thread_checker_.CalledOnValidThread()); | 
|   44  |   34  | 
|   45   while (read_queue_.size()) { |   35   while (!pending_reads_.empty()) { | 
|   46     read_queue_.front().c.Run(false, 0); |   36     pending_reads_.front().callback.Run(false, 0); | 
|   47     read_queue_.pop(); |   37     pending_reads_.pop(); | 
|   48   } |   38   } | 
|   49  |   39  | 
|   50   IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone); |   40   IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone); | 
|   51 } |   41 } | 
|   52  |   42  | 
|   53 void HidConnectionMac::InputReportCallback(void * context, |   43 void HidConnectionMac::Read(scoped_refptr<net::IOBufferWithSize> buffer, | 
 |   44                             const IOCallback& callback) { | 
 |   45   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   46   if (!device_) { | 
 |   47     callback.Run(false, 0); | 
 |   48     return; | 
 |   49   } | 
 |   50   PendingHidRead read; | 
 |   51   read.buffer = buffer; | 
 |   52   read.callback = callback; | 
 |   53   pending_reads_.push(read); | 
 |   54   ProcessReadQueue(); | 
 |   55 } | 
 |   56  | 
 |   57 void HidConnectionMac::Write(uint8_t report_id, | 
 |   58                              scoped_refptr<net::IOBufferWithSize> buffer, | 
 |   59                              const IOCallback& callback) { | 
 |   60   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   61   WriteReport(kIOHIDReportTypeOutput, report_id, buffer, callback); | 
 |   62 } | 
 |   63  | 
 |   64 void HidConnectionMac::GetFeatureReport( | 
 |   65     uint8_t report_id, | 
 |   66     scoped_refptr<net::IOBufferWithSize> buffer, | 
 |   67     const IOCallback& callback) { | 
 |   68   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   69   if (device_info().feature_report_size == 0) { | 
 |   70     callback.Run(false, 0); | 
 |   71     return; | 
 |   72   } | 
 |   73  | 
 |   74   if (buffer->size() < device_info().feature_report_size) { | 
 |   75     callback.Run(false, 0); | 
 |   76     return; | 
 |   77   } | 
 |   78  | 
 |   79   uint8_t* feature_report_buffer = reinterpret_cast<uint8_t*>(buffer->data()); | 
 |   80   CFIndex feature_report_size = device_info().feature_report_size; | 
 |   81   IOReturn result = IOHIDDeviceGetReport(device_, | 
 |   82                                          kIOHIDReportTypeFeature, | 
 |   83                                          report_id, | 
 |   84                                          feature_report_buffer, | 
 |   85                                          &feature_report_size); | 
 |   86   if (result == kIOReturnSuccess) | 
 |   87     callback.Run(true, feature_report_size); | 
 |   88   else | 
 |   89     callback.Run(false, 0); | 
 |   90 } | 
 |   91  | 
 |   92 void HidConnectionMac::SendFeatureReport( | 
 |   93     uint8_t report_id, | 
 |   94     scoped_refptr<net::IOBufferWithSize> buffer, | 
 |   95     const IOCallback& callback) { | 
 |   96   DCHECK(thread_checker_.CalledOnValidThread()); | 
 |   97   WriteReport(kIOHIDReportTypeFeature, report_id, buffer, callback); | 
 |   98 } | 
 |   99  | 
 |  100 void HidConnectionMac::InputReportCallback(void* context, | 
|   54                                            IOReturn result, |  101                                            IOReturn result, | 
|   55                                            void * sender, |  102                                            void* sender, | 
|   56                                            IOHIDReportType type, |  103                                            IOHIDReportType type, | 
|   57                                            uint32_t reportID, |  104                                            uint32_t report_id, | 
|   58                                            uint8_t * report, |  105                                            uint8_t* report_bytes, | 
|   59                                            CFIndex reportLength) { |  106                                            CFIndex report_length) { | 
|   60   HidConnectionMac* connection = reinterpret_cast<HidConnectionMac*>(context); |  107   HidConnectionMac* connection = static_cast<HidConnectionMac*>(context); | 
|   61   size_t length = reportLength + (reportID != 0); |  108   // If a report ID was received, inject it into a copy of the received | 
|   62   scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(length)); |  109   // report. This is consistent with how input reports are received on | 
|   63   if (reportID) { |  110   // other platforms. | 
|   64     buffer->data()[0] = reportID; |  111   scoped_refptr<net::IOBufferWithSize> buffer; | 
|   65     memcpy(buffer->data() + 1, report, reportLength); |  112   if (report_id != 0) { | 
 |  113     buffer = new net::IOBufferWithSize(report_length + 1); | 
 |  114     buffer->data()[0] = static_cast<uint8_t>(report_id); | 
 |  115     memcpy(buffer->data() + 1, report_bytes, report_length); | 
|   66   } else { |  116   } else { | 
|   67     memcpy(buffer->data(), report, reportLength); |  117     buffer = new net::IOBufferWithSize(report_length); | 
 |  118     memcpy(buffer->data(), report_bytes, report_length); | 
|   68   } |  119   } | 
|   69   connection->message_loop_->PostTask( |  120   connection->message_loop_->PostTask( | 
|   70       FROM_HERE, |  121       FROM_HERE, | 
|   71       base::Bind(&HidConnectionMac::ProcessInputReport, |  122       base::Bind( | 
|   72                  connection, |  123           &HidConnectionMac::ProcessInputReport, connection, type, buffer)); | 
|   73                  type, |  | 
|   74                  buffer, |  | 
|   75                  length)); |  | 
|   76 } |  124 } | 
|   77  |  125  | 
|   78 void HidConnectionMac::ProcessReadQueue() { |  126 void HidConnectionMac::ProcessReadQueue() { | 
|   79   DCHECK(thread_checker_.CalledOnValidThread()); |  127   DCHECK(thread_checker_.CalledOnValidThread()); | 
|   80  |  128   while (pending_reads_.size() && pending_reports_.size()) { | 
|   81   while(read_queue_.size() && input_reports_.size()) { |  129     PendingHidRead read = pending_reads_.front(); | 
|   82     PendingRead read = read_queue_.front(); |  130     pending_reads_.pop(); | 
|   83     read_queue_.pop(); |  131     PendingHidReport report = pending_reports_.front(); | 
|   84     PendingReport report = input_reports_.front(); |  132     if (read.buffer->size() < report.buffer->size()) { | 
|   85  |  133       read.callback.Run(false, report.buffer->size()); | 
|   86     if (read.b < report.second) { |  | 
|   87       read.c.Run(false, report.second); |  | 
|   88     } else { |  134     } else { | 
|   89       memcpy(read.a->data(), report.first->data(), report.second); |  135       memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size()); | 
|   90       input_reports_.pop(); |  136       pending_reports_.pop(); | 
|   91       read.c.Run(true, report.second); |  137       read.callback.Run(true, report.buffer->size()); | 
|   92     } |  138     } | 
|   93   } |  139   } | 
|   94 } |  140 } | 
|   95  |  141  | 
|   96 void HidConnectionMac::ProcessInputReport(IOHIDReportType type, |  142 void HidConnectionMac::ProcessInputReport( | 
|   97                                           scoped_refptr<net::IOBuffer> report, |  143     IOHIDReportType type, | 
|   98                                           CFIndex reportLength) { |  144     scoped_refptr<net::IOBufferWithSize> buffer) { | 
|   99   DCHECK(thread_checker_.CalledOnValidThread()); |  145   DCHECK(thread_checker_.CalledOnValidThread()); | 
|  100  |  146   PendingHidReport report; | 
|  101   input_reports_.push(std::make_pair(report, reportLength)); |  147   report.buffer = buffer; | 
 |  148   pending_reports_.push(report); | 
|  102   ProcessReadQueue(); |  149   ProcessReadQueue(); | 
|  103 } |  150 } | 
|  104  |  151  | 
|  105 void HidConnectionMac::WriteReport(IOHIDReportType type, |  152 void HidConnectionMac::WriteReport(IOHIDReportType type, | 
|  106                                    scoped_refptr<net::IOBuffer> buffer, |  153                                    uint8_t report_id, | 
|  107                                    size_t size, |  154                                    scoped_refptr<net::IOBufferWithSize> buffer, | 
|  108                                    const IOCallback& callback) { |  155                                    const IOCallback& callback) { | 
|  109   DCHECK(thread_checker_.CalledOnValidThread()); |  156   DCHECK(thread_checker_.CalledOnValidThread()); | 
|  110   if (disconnected_ || !device_) { |  157   if (!device_) { | 
|  111     callback.Run(false, 0); |  158     callback.Run(false, 0); | 
|  112     return; |  159     return; | 
|  113   } |  160   } | 
|  114   const unsigned char* data_to_send = |  161   IOReturn res = | 
|  115       reinterpret_cast<const unsigned char*>(buffer->data()); |  162       IOHIDDeviceSetReport(device_.get(), | 
|  116   size_t length_to_send = size; |  163                            type, | 
|  117   if (data_to_send[0] == 0x0) { |  164                            report_id, | 
|  118       /* Not using numbered Reports. |  165                            reinterpret_cast<uint8_t*>(buffer->data()), | 
|  119        Don't send the report number. */ |  166                            buffer->size()); | 
|  120       ++data_to_send; |  | 
|  121       --length_to_send; |  | 
|  122   } |  | 
|  123   IOReturn res = IOHIDDeviceSetReport(device_.get(), |  | 
|  124                                       type, |  | 
|  125                                       buffer->data()[0], /* Report ID*/ |  | 
|  126                                       data_to_send, |  | 
|  127                                       length_to_send); |  | 
|  128   if (res != kIOReturnSuccess) { |  167   if (res != kIOReturnSuccess) { | 
|  129     callback.Run(false, 0); |  168     callback.Run(false, 0); | 
|  130   } else { |  169   } else { | 
|  131     callback.Run(true, size); |  170     callback.Run(true, buffer->size()); | 
|  132   } |  171   } | 
|  133 } |  172 } | 
|  134  |  173  | 
|  135 void HidConnectionMac::Read(scoped_refptr<net::IOBuffer> buffer, |  | 
|  136                             size_t size, |  | 
|  137                             const IOCallback& callback) { |  | 
|  138   DCHECK(thread_checker_.CalledOnValidThread()); |  | 
|  139   if (disconnected_ || !device_) { |  | 
|  140     callback.Run(false, 0); |  | 
|  141     return; |  | 
|  142   } |  | 
|  143   read_queue_.push(MakeTuple(buffer, size, callback)); |  | 
|  144   ProcessReadQueue(); |  | 
|  145 } |  | 
|  146  |  | 
|  147 void HidConnectionMac::Write(scoped_refptr<net::IOBuffer> buffer, |  | 
|  148                              size_t size, |  | 
|  149                              const IOCallback& callback) { |  | 
|  150   DCHECK(thread_checker_.CalledOnValidThread()); |  | 
|  151   WriteReport(kIOHIDReportTypeOutput, buffer, size, callback); |  | 
|  152 } |  | 
|  153  |  | 
|  154 void HidConnectionMac::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer, |  | 
|  155                                          size_t size, |  | 
|  156                                          const IOCallback& callback) { |  | 
|  157   DCHECK(thread_checker_.CalledOnValidThread()); |  | 
|  158   WriteReport(kIOHIDReportTypeFeature, buffer, size, callback); |  | 
|  159 } |  | 
|  160  |  | 
|  161 void HidConnectionMac::GetFeatureReport(scoped_refptr<net::IOBuffer> buffer, |  | 
|  162                                         size_t size, |  | 
|  163                                         const IOCallback& callback) { |  | 
|  164   DCHECK(thread_checker_.CalledOnValidThread()); |  | 
|  165   if (disconnected_ || !device_ || device_info_.feature_report_size == 0) { |  | 
|  166     callback.Run(false, 0); |  | 
|  167     return; |  | 
|  168   } |  | 
|  169  |  | 
|  170   if (device_info_.feature_report_size != 0 && |  | 
|  171       device_info_.feature_report_size != size) { |  | 
|  172     callback.Run(false, 0); |  | 
|  173     return; |  | 
|  174   } |  | 
|  175  |  | 
|  176   CFIndex len = device_info_.feature_report_size; |  | 
|  177   IOReturn res = IOHIDDeviceGetReport(device_, |  | 
|  178                                       kIOHIDReportTypeFeature, |  | 
|  179                                       0, |  | 
|  180                                       (uint8_t*) buffer->data(), |  | 
|  181                                       &len); |  | 
|  182   if (res == kIOReturnSuccess) |  | 
|  183     callback.Run(true, len); |  | 
|  184   else |  | 
|  185     callback.Run(false, 0); |  | 
|  186 } |  | 
|  187  |  | 
|  188 }  // namespace device |  174 }  // namespace device | 
| OLD | NEW |