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/usb/usb_service.h" 6 7 #include <map> 8 #include <set> 9 10 #include "base/lazy_instance.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/single_thread_task_runner.h" 13 #include "base/stl_util.h" 14 #include "device/usb/usb_context.h" 15 #include "device/usb/usb_device_impl.h" 16 #include "device/usb/usb_error.h" 17 #include "third_party/libusb/src/libusb/libusb.h" 18 19 namespace device { 20 21 namespace { 22 23 base::LazyInstance<scoped_ptr<UsbService> >::Leaky g_usb_service_instance = 24 LAZY_INSTANCE_INITIALIZER; 25 26 } // namespace 27 28 typedef struct libusb_device* PlatformUsbDevice; 29 typedef struct libusb_context* PlatformUsbContext; 30 31 class UsbServiceImpl : public UsbService, 32 private base::MessageLoop::DestructionObserver { 33 public: 34 explicit UsbServiceImpl( 35 PlatformUsbContext context, 36 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); 37 virtual ~UsbServiceImpl(); 38 39 private: 40 // device::UsbService implementation 41 virtual scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) OVERRIDE; 42 virtual void GetDevices( 43 std::vector<scoped_refptr<UsbDevice> >* devices) OVERRIDE; 44 45 // base::MessageLoop::DestructionObserver implementation. 46 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; 47 48 // Enumerate USB devices from OS and update devices_ map. 49 void RefreshDevices(); 50 51 scoped_refptr<UsbContext> context_; 52 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; 53 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; 54 55 // TODO(reillyg): Figure out a better solution. 56 uint32 next_unique_id_; 57 58 // The map from unique IDs to UsbDevices. 59 typedef std::map<uint32, scoped_refptr<UsbDeviceImpl> > DeviceMap; 60 DeviceMap devices_; 61 62 // The map from PlatformUsbDevices to UsbDevices. 63 typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl> > 64 PlatformDeviceMap; 65 PlatformDeviceMap platform_devices_; 66 67 DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl); 68 }; 69 70 scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { 71 DCHECK(CalledOnValidThread()); 72 RefreshDevices(); 73 DeviceMap::iterator it = devices_.find(unique_id); 74 if (it != devices_.end()) { 75 return it->second; 76 } 77 return NULL; 78 } 79 80 void UsbServiceImpl::GetDevices( 81 std::vector<scoped_refptr<UsbDevice> >* devices) { 82 DCHECK(CalledOnValidThread()); 83 STLClearObject(devices); 84 RefreshDevices(); 85 86 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { 87 devices->push_back(it->second); 88 } 89 } 90 91 void UsbServiceImpl::WillDestroyCurrentMessageLoop() { 92 DCHECK(CalledOnValidThread()); 93 g_usb_service_instance.Get().reset(NULL); 94 } 95 96 UsbServiceImpl::UsbServiceImpl( 97 PlatformUsbContext context, 98 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) 99 : context_(new UsbContext(context)), 100 ui_task_runner_(ui_task_runner), 101 next_unique_id_(0) { 102 base::MessageLoop::current()->AddDestructionObserver(this); 103 } 104 105 UsbServiceImpl::~UsbServiceImpl() { 106 base::MessageLoop::current()->RemoveDestructionObserver(this); 107 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { 108 it->second->OnDisconnect(); 109 } 110 } 111 112 void UsbServiceImpl::RefreshDevices() { 113 DCHECK(CalledOnValidThread()); 114 115 libusb_device** platform_devices = NULL; 116 const ssize_t device_count = 117 libusb_get_device_list(context_->context(), &platform_devices); 118 if (device_count < 0) { 119 VLOG(1) << "Failed to get device list: " 120 << ConvertPlatformUsbErrorToString(device_count); 121 } 122 123 std::set<UsbDevice*> connected_devices; 124 std::vector<PlatformUsbDevice> disconnected_devices; 125 126 // Populates new devices. 127 for (ssize_t i = 0; i < device_count; ++i) { 128 if (!ContainsKey(platform_devices_, platform_devices[i])) { 129 libusb_device_descriptor descriptor; 130 const int rv = 131 libusb_get_device_descriptor(platform_devices[i], &descriptor); 132 // This test is needed. A valid vendor/produce pair is required. 133 if (rv != LIBUSB_SUCCESS) { 134 VLOG(1) << "Failed to get device descriptor: " 135 << ConvertPlatformUsbErrorToString(rv); 136 continue; 137 } 138 139 uint32 unique_id; 140 do { 141 unique_id = ++next_unique_id_; 142 } while (devices_.find(unique_id) != devices_.end()); 143 144 scoped_refptr<UsbDeviceImpl> new_device( 145 new UsbDeviceImpl(context_, 146 ui_task_runner_, 147 platform_devices[i], 148 descriptor.idVendor, 149 descriptor.idProduct, 150 unique_id)); 151 platform_devices_[platform_devices[i]] = new_device; 152 devices_[unique_id] = new_device; 153 connected_devices.insert(new_device.get()); 154 } else { 155 connected_devices.insert(platform_devices_[platform_devices[i]].get()); 156 } 157 } 158 159 // Find disconnected devices. 160 for (PlatformDeviceMap::iterator it = platform_devices_.begin(); 161 it != platform_devices_.end(); 162 ++it) { 163 if (!ContainsKey(connected_devices, it->second.get())) { 164 disconnected_devices.push_back(it->first); 165 devices_.erase(it->second->unique_id()); 166 it->second->OnDisconnect(); 167 } 168 } 169 170 // Remove disconnected devices from platform_devices_. 171 for (size_t i = 0; i < disconnected_devices.size(); ++i) { 172 // UsbDevice will be destroyed after this. The corresponding 173 // PlatformUsbDevice will be unref'ed during this process. 174 platform_devices_.erase(disconnected_devices[i]); 175 } 176 177 libusb_free_device_list(platform_devices, true); 178 } 179 180 // static 181 UsbService* UsbService::GetInstance( 182 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { 183 UsbService* instance = g_usb_service_instance.Get().get(); 184 if (!instance) { 185 PlatformUsbContext context = NULL; 186 187 const int rv = libusb_init(&context); 188 if (rv != LIBUSB_SUCCESS) { 189 VLOG(1) << "Failed to initialize libusb: " 190 << ConvertPlatformUsbErrorToString(rv); 191 return NULL; 192 } 193 if (!context) 194 return NULL; 195 196 instance = new UsbServiceImpl(context, ui_task_runner); 197 g_usb_service_instance.Get().reset(instance); 198 } 199 return instance; 200 } 201 202 // static 203 void UsbService::SetInstanceForTest(UsbService* instance) { 204 g_usb_service_instance.Get().reset(instance); 205 } 206 207 } // namespace device 208