| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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/usb/usb_service.h" | 5 #include "device/usb/usb_service.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <set> | 8 #include <set> |
| 9 | 9 |
| 10 #include "base/bind.h" |
| 10 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
| 11 #include "base/message_loop/message_loop.h" | 12 #include "base/message_loop/message_loop.h" |
| 12 #include "base/single_thread_task_runner.h" | 13 #include "base/single_thread_task_runner.h" |
| 13 #include "base/stl_util.h" | 14 #include "base/stl_util.h" |
| 15 #include "base/thread_task_runner_handle.h" |
| 14 #include "device/usb/usb_context.h" | 16 #include "device/usb/usb_context.h" |
| 15 #include "device/usb/usb_device_impl.h" | 17 #include "device/usb/usb_device_impl.h" |
| 16 #include "device/usb/usb_error.h" | 18 #include "device/usb/usb_error.h" |
| 17 #include "third_party/libusb/src/libusb/libusb.h" | 19 #include "third_party/libusb/src/libusb/libusb.h" |
| 18 | 20 |
| 19 namespace device { | 21 namespace device { |
| 20 | 22 |
| 21 namespace { | 23 namespace { |
| 22 | 24 |
| 23 base::LazyInstance<scoped_ptr<UsbService> >::Leaky g_usb_service_instance = | 25 base::LazyInstance<scoped_ptr<UsbService> >::Leaky g_usb_service_instance = |
| (...skipping 16 matching lines...) Expand all Loading... |
| 40 // device::UsbService implementation | 42 // device::UsbService implementation |
| 41 scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override; | 43 scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override; |
| 42 void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override; | 44 void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override; |
| 43 | 45 |
| 44 // base::MessageLoop::DestructionObserver implementation. | 46 // base::MessageLoop::DestructionObserver implementation. |
| 45 void WillDestroyCurrentMessageLoop() override; | 47 void WillDestroyCurrentMessageLoop() override; |
| 46 | 48 |
| 47 // Enumerate USB devices from OS and update devices_ map. | 49 // Enumerate USB devices from OS and update devices_ map. |
| 48 void RefreshDevices(); | 50 void RefreshDevices(); |
| 49 | 51 |
| 52 // Adds a new UsbDevice to the devices_ map based on the given libusb device. |
| 53 scoped_refptr<UsbDeviceImpl> AddDevice(PlatformUsbDevice platform_device); |
| 54 |
| 55 // Handle hotplug events from libusb. |
| 56 static int LIBUSB_CALL HotplugCallback(libusb_context* context, |
| 57 PlatformUsbDevice device, |
| 58 libusb_hotplug_event event, |
| 59 void* user_data); |
| 60 // These functions release a reference to the provided platform device. |
| 61 void OnDeviceAdded(PlatformUsbDevice platform_device); |
| 62 void OnDeviceRemoved(PlatformUsbDevice platform_device); |
| 63 |
| 50 scoped_refptr<UsbContext> context_; | 64 scoped_refptr<UsbContext> context_; |
| 51 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | 65 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| 52 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; | 66 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; |
| 53 | 67 |
| 54 // TODO(reillyg): Figure out a better solution. | 68 // TODO(reillyg): Figure out a better solution for device IDs. |
| 55 uint32 next_unique_id_; | 69 uint32 next_unique_id_; |
| 56 | 70 |
| 71 // When available the device list will be updated when new devices are |
| 72 // connected instead of only when a full enumeration is requested. |
| 73 // TODO(reillyg): Support this on all platforms. crbug.com/411715 |
| 74 bool hotplug_enabled_; |
| 75 libusb_hotplug_callback_handle hotplug_handle_; |
| 76 |
| 57 // The map from unique IDs to UsbDevices. | 77 // The map from unique IDs to UsbDevices. |
| 58 typedef std::map<uint32, scoped_refptr<UsbDeviceImpl> > DeviceMap; | 78 typedef std::map<uint32, scoped_refptr<UsbDeviceImpl> > DeviceMap; |
| 59 DeviceMap devices_; | 79 DeviceMap devices_; |
| 60 | 80 |
| 61 // The map from PlatformUsbDevices to UsbDevices. | 81 // The map from PlatformUsbDevices to UsbDevices. |
| 62 typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl> > | 82 typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl> > |
| 63 PlatformDeviceMap; | 83 PlatformDeviceMap; |
| 64 PlatformDeviceMap platform_devices_; | 84 PlatformDeviceMap platform_devices_; |
| 65 | 85 |
| 66 DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl); | 86 DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl); |
| 67 }; | 87 }; |
| 68 | 88 |
| 69 scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { | 89 scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { |
| 70 DCHECK(CalledOnValidThread()); | 90 DCHECK(CalledOnValidThread()); |
| 71 RefreshDevices(); | 91 RefreshDevices(); |
| 72 DeviceMap::iterator it = devices_.find(unique_id); | 92 DeviceMap::iterator it = devices_.find(unique_id); |
| 73 if (it != devices_.end()) { | 93 if (it != devices_.end()) { |
| 74 return it->second; | 94 return it->second; |
| 75 } | 95 } |
| 76 return NULL; | 96 return NULL; |
| 77 } | 97 } |
| 78 | 98 |
| 79 void UsbServiceImpl::GetDevices( | 99 void UsbServiceImpl::GetDevices( |
| 80 std::vector<scoped_refptr<UsbDevice> >* devices) { | 100 std::vector<scoped_refptr<UsbDevice> >* devices) { |
| 81 DCHECK(CalledOnValidThread()); | 101 DCHECK(CalledOnValidThread()); |
| 82 STLClearObject(devices); | 102 STLClearObject(devices); |
| 83 RefreshDevices(); | |
| 84 | 103 |
| 85 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { | 104 if (!hotplug_enabled_) { |
| 86 devices->push_back(it->second); | 105 RefreshDevices(); |
| 106 } |
| 107 |
| 108 for (const auto& map_entry : devices_) { |
| 109 devices->push_back(map_entry.second); |
| 87 } | 110 } |
| 88 } | 111 } |
| 89 | 112 |
| 90 void UsbServiceImpl::WillDestroyCurrentMessageLoop() { | 113 void UsbServiceImpl::WillDestroyCurrentMessageLoop() { |
| 91 DCHECK(CalledOnValidThread()); | 114 DCHECK(CalledOnValidThread()); |
| 92 g_usb_service_instance.Get().reset(NULL); | 115 g_usb_service_instance.Get().reset(NULL); |
| 93 } | 116 } |
| 94 | 117 |
| 95 UsbServiceImpl::UsbServiceImpl( | 118 UsbServiceImpl::UsbServiceImpl( |
| 96 PlatformUsbContext context, | 119 PlatformUsbContext context, |
| 97 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) | 120 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) |
| 98 : context_(new UsbContext(context)), | 121 : context_(new UsbContext(context)), |
| 99 ui_task_runner_(ui_task_runner), | 122 ui_task_runner_(ui_task_runner), |
| 100 next_unique_id_(0) { | 123 next_unique_id_(0), |
| 124 hotplug_enabled_(false) { |
| 101 base::MessageLoop::current()->AddDestructionObserver(this); | 125 base::MessageLoop::current()->AddDestructionObserver(this); |
| 126 task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| 127 int rv = libusb_hotplug_register_callback( |
| 128 context_->context(), |
| 129 static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | |
| 130 LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), |
| 131 LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, |
| 132 LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, |
| 133 &UsbServiceImpl::HotplugCallback, this, &hotplug_handle_); |
| 134 if (rv == LIBUSB_SUCCESS) { |
| 135 hotplug_enabled_ = true; |
| 136 } |
| 102 } | 137 } |
| 103 | 138 |
| 104 UsbServiceImpl::~UsbServiceImpl() { | 139 UsbServiceImpl::~UsbServiceImpl() { |
| 105 base::MessageLoop::current()->RemoveDestructionObserver(this); | 140 base::MessageLoop::current()->RemoveDestructionObserver(this); |
| 106 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { | 141 if (hotplug_enabled_) { |
| 107 it->second->OnDisconnect(); | 142 libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_); |
| 143 } |
| 144 for (const auto& map_entry : devices_) { |
| 145 map_entry.second->OnDisconnect(); |
| 108 } | 146 } |
| 109 } | 147 } |
| 110 | 148 |
| 111 void UsbServiceImpl::RefreshDevices() { | 149 void UsbServiceImpl::RefreshDevices() { |
| 112 DCHECK(CalledOnValidThread()); | 150 DCHECK(CalledOnValidThread()); |
| 113 | 151 |
| 114 libusb_device** platform_devices = NULL; | 152 libusb_device** platform_devices = NULL; |
| 115 const ssize_t device_count = | 153 const ssize_t device_count = |
| 116 libusb_get_device_list(context_->context(), &platform_devices); | 154 libusb_get_device_list(context_->context(), &platform_devices); |
| 117 if (device_count < 0) { | 155 if (device_count < 0) { |
| 118 VLOG(1) << "Failed to get device list: " | 156 VLOG(1) << "Failed to get device list: " |
| 119 << ConvertPlatformUsbErrorToString(device_count); | 157 << ConvertPlatformUsbErrorToString(device_count); |
| 120 } | 158 } |
| 121 | 159 |
| 122 std::set<UsbDevice*> connected_devices; | 160 std::set<UsbDevice*> connected_devices; |
| 123 std::vector<PlatformUsbDevice> disconnected_devices; | 161 std::vector<PlatformUsbDevice> disconnected_devices; |
| 124 | 162 |
| 125 // Populates new devices. | 163 // Populates new devices. |
| 126 for (ssize_t i = 0; i < device_count; ++i) { | 164 for (ssize_t i = 0; i < device_count; ++i) { |
| 127 if (!ContainsKey(platform_devices_, platform_devices[i])) { | 165 if (!ContainsKey(platform_devices_, platform_devices[i])) { |
| 128 libusb_device_descriptor descriptor; | 166 scoped_refptr<UsbDeviceImpl> new_device = AddDevice(platform_devices[i]); |
| 129 const int rv = | 167 if (new_device) { |
| 130 libusb_get_device_descriptor(platform_devices[i], &descriptor); | 168 connected_devices.insert(new_device.get()); |
| 131 // This test is needed. A valid vendor/produce pair is required. | |
| 132 if (rv != LIBUSB_SUCCESS) { | |
| 133 VLOG(1) << "Failed to get device descriptor: " | |
| 134 << ConvertPlatformUsbErrorToString(rv); | |
| 135 continue; | |
| 136 } | 169 } |
| 137 | |
| 138 uint32 unique_id; | |
| 139 do { | |
| 140 unique_id = ++next_unique_id_; | |
| 141 } while (devices_.find(unique_id) != devices_.end()); | |
| 142 | |
| 143 scoped_refptr<UsbDeviceImpl> new_device( | |
| 144 new UsbDeviceImpl(context_, | |
| 145 ui_task_runner_, | |
| 146 platform_devices[i], | |
| 147 descriptor.idVendor, | |
| 148 descriptor.idProduct, | |
| 149 unique_id)); | |
| 150 platform_devices_[platform_devices[i]] = new_device; | |
| 151 devices_[unique_id] = new_device; | |
| 152 connected_devices.insert(new_device.get()); | |
| 153 } else { | 170 } else { |
| 154 connected_devices.insert(platform_devices_[platform_devices[i]].get()); | 171 connected_devices.insert(platform_devices_[platform_devices[i]].get()); |
| 155 } | 172 } |
| 156 } | 173 } |
| 157 | 174 |
| 158 // Find disconnected devices. | 175 // Find disconnected devices. |
| 159 for (PlatformDeviceMap::iterator it = platform_devices_.begin(); | 176 for (PlatformDeviceMap::iterator it = platform_devices_.begin(); |
| 160 it != platform_devices_.end(); | 177 it != platform_devices_.end(); |
| 161 ++it) { | 178 ++it) { |
| 162 if (!ContainsKey(connected_devices, it->second.get())) { | 179 if (!ContainsKey(connected_devices, it->second.get())) { |
| 163 disconnected_devices.push_back(it->first); | 180 disconnected_devices.push_back(it->first); |
| 164 devices_.erase(it->second->unique_id()); | 181 devices_.erase(it->second->unique_id()); |
| 165 it->second->OnDisconnect(); | 182 it->second->OnDisconnect(); |
| 166 } | 183 } |
| 167 } | 184 } |
| 168 | 185 |
| 169 // Remove disconnected devices from platform_devices_. | 186 // Remove disconnected devices from platform_devices_. |
| 170 for (size_t i = 0; i < disconnected_devices.size(); ++i) { | 187 for (size_t i = 0; i < disconnected_devices.size(); ++i) { |
| 171 // UsbDevice will be destroyed after this. The corresponding | 188 // UsbDevice will be destroyed after this. The corresponding |
| 172 // PlatformUsbDevice will be unref'ed during this process. | 189 // PlatformUsbDevice will be unref'ed during this process. |
| 173 platform_devices_.erase(disconnected_devices[i]); | 190 platform_devices_.erase(disconnected_devices[i]); |
| 174 } | 191 } |
| 175 | 192 |
| 176 libusb_free_device_list(platform_devices, true); | 193 libusb_free_device_list(platform_devices, true); |
| 177 } | 194 } |
| 178 | 195 |
| 196 scoped_refptr<UsbDeviceImpl> UsbServiceImpl::AddDevice( |
| 197 PlatformUsbDevice platform_device) { |
| 198 libusb_device_descriptor descriptor; |
| 199 int rv = libusb_get_device_descriptor(platform_device, &descriptor); |
| 200 if (rv == LIBUSB_SUCCESS) { |
| 201 uint32 unique_id; |
| 202 do { |
| 203 unique_id = ++next_unique_id_; |
| 204 } while (devices_.find(unique_id) != devices_.end()); |
| 205 |
| 206 scoped_refptr<UsbDeviceImpl> new_device(new UsbDeviceImpl( |
| 207 context_, ui_task_runner_, platform_device, descriptor.idVendor, |
| 208 descriptor.idProduct, unique_id)); |
| 209 platform_devices_[platform_device] = new_device; |
| 210 devices_[unique_id] = new_device; |
| 211 return new_device; |
| 212 } else { |
| 213 VLOG(1) << "Failed to get device descriptor: " |
| 214 << ConvertPlatformUsbErrorToString(rv); |
| 215 return nullptr; |
| 216 } |
| 217 } |
| 218 |
| 219 // static |
| 220 int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, |
| 221 PlatformUsbDevice device, |
| 222 libusb_hotplug_event event, |
| 223 void* user_data) { |
| 224 // It is safe to access the UsbServiceImpl* here because libusb takes a lock |
| 225 // around registering, deregistering and calling hotplug callback functions |
| 226 // and so guarantees that this function will not be called by the event |
| 227 // processing thread after it has been deregistered. |
| 228 UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data); |
| 229 switch (event) { |
| 230 case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: |
| 231 libusb_ref_device(device); // Released in OnDeviceAdded. |
| 232 if (self->task_runner_->BelongsToCurrentThread()) { |
| 233 self->OnDeviceAdded(device); |
| 234 } else { |
| 235 self->task_runner_->PostTask( |
| 236 FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceAdded, |
| 237 base::Unretained(self), device)); |
| 238 } |
| 239 break; |
| 240 case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: |
| 241 libusb_ref_device(device); // Released in OnDeviceRemoved. |
| 242 if (self->task_runner_->BelongsToCurrentThread()) { |
| 243 self->OnDeviceRemoved(device); |
| 244 } else { |
| 245 self->task_runner_->PostTask( |
| 246 FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceRemoved, |
| 247 base::Unretained(self), device)); |
| 248 } |
| 249 break; |
| 250 default: |
| 251 NOTREACHED(); |
| 252 } |
| 253 |
| 254 return 0; |
| 255 } |
| 256 |
| 257 void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device) { |
| 258 DCHECK(CalledOnValidThread()); |
| 259 DCHECK(!ContainsKey(platform_devices_, platform_device)); |
| 260 |
| 261 AddDevice(platform_device); |
| 262 libusb_unref_device(platform_device); |
| 263 } |
| 264 |
| 265 void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device) { |
| 266 DCHECK(CalledOnValidThread()); |
| 267 |
| 268 PlatformDeviceMap::iterator it = platform_devices_.find(platform_device); |
| 269 if (it != platform_devices_.end()) { |
| 270 scoped_refptr<UsbDeviceImpl> device = it->second; |
| 271 DeviceMap::iterator dev_it = devices_.find(device->unique_id()); |
| 272 if (dev_it != devices_.end()) { |
| 273 devices_.erase(dev_it); |
| 274 } else { |
| 275 NOTREACHED(); |
| 276 } |
| 277 device->OnDisconnect(); |
| 278 platform_devices_.erase(it); |
| 279 } else { |
| 280 NOTREACHED(); |
| 281 } |
| 282 |
| 283 libusb_unref_device(platform_device); |
| 284 } |
| 285 |
| 179 // static | 286 // static |
| 180 UsbService* UsbService::GetInstance( | 287 UsbService* UsbService::GetInstance( |
| 181 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { | 288 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { |
| 182 UsbService* instance = g_usb_service_instance.Get().get(); | 289 UsbService* instance = g_usb_service_instance.Get().get(); |
| 183 if (!instance) { | 290 if (!instance) { |
| 184 PlatformUsbContext context = NULL; | 291 PlatformUsbContext context = NULL; |
| 185 | 292 |
| 186 const int rv = libusb_init(&context); | 293 const int rv = libusb_init(&context); |
| 187 if (rv != LIBUSB_SUCCESS) { | 294 if (rv != LIBUSB_SUCCESS) { |
| 188 VLOG(1) << "Failed to initialize libusb: " | 295 VLOG(1) << "Failed to initialize libusb: " |
| 189 << ConvertPlatformUsbErrorToString(rv); | 296 << ConvertPlatformUsbErrorToString(rv); |
| 190 return NULL; | 297 return NULL; |
| 191 } | 298 } |
| 192 if (!context) | 299 if (!context) |
| 193 return NULL; | 300 return NULL; |
| 194 | 301 |
| 195 instance = new UsbServiceImpl(context, ui_task_runner); | 302 instance = new UsbServiceImpl(context, ui_task_runner); |
| 196 g_usb_service_instance.Get().reset(instance); | 303 g_usb_service_instance.Get().reset(instance); |
| 197 } | 304 } |
| 198 return instance; | 305 return instance; |
| 199 } | 306 } |
| 200 | 307 |
| 201 // static | 308 // static |
| 202 void UsbService::SetInstanceForTest(UsbService* instance) { | 309 void UsbService::SetInstanceForTest(UsbService* instance) { |
| 203 g_usb_service_instance.Get().reset(instance); | 310 g_usb_service_instance.Get().reset(instance); |
| 204 } | 311 } |
| 205 | 312 |
| 206 } // namespace device | 313 } // namespace device |
| OLD | NEW |