Home | History | Annotate | Download | only in usb
      1 // Copyright (c) 2012 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 "chrome/browser/usb/usb_service.h"
      6 
      7 #include <set>
      8 #include <vector>
      9 
     10 #include "base/basictypes.h"
     11 #include "base/bind.h"
     12 #include "base/bind_helpers.h"
     13 #include "base/logging.h"
     14 #include "base/memory/ref_counted.h"
     15 #include "base/stl_util.h"
     16 #include "chrome/browser/chrome_notification_types.h"
     17 #include "chrome/browser/usb/usb_context.h"
     18 #include "chrome/browser/usb/usb_device.h"
     19 #include "chrome/browser/usb/usb_device_handle.h"
     20 #include "content/public/browser/browser_thread.h"
     21 #include "content/public/browser/notification_observer.h"
     22 #include "content/public/browser/notification_registrar.h"
     23 #include "content/public/browser/notification_service.h"
     24 #include "third_party/libusb/src/libusb/libusb.h"
     25 
     26 namespace content {
     27 
     28 class NotificationDetails;
     29 class NotificationSource;
     30 
     31 }  // namespace content
     32 
     33 using content::BrowserThread;
     34 using std::vector;
     35 
     36 namespace {
     37 
     38 class ExitObserver : public content::NotificationObserver {
     39  public:
     40   explicit ExitObserver(UsbService* service) : service_(service) {
     41     BrowserThread::PostTask(
     42         BrowserThread::UI, FROM_HERE,
     43         base::Bind(&content::NotificationRegistrar::Add,
     44                    base::Unretained(&registrar_), this,
     45                    chrome::NOTIFICATION_APP_TERMINATING,
     46                    content::NotificationService::AllSources()));
     47   }
     48 
     49  private:
     50   // content::NotificationObserver
     51   virtual void Observe(int type,
     52                        const content::NotificationSource& source,
     53                        const content::NotificationDetails& details) OVERRIDE {
     54     if (type == chrome::NOTIFICATION_APP_TERMINATING) {
     55       BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, service_);
     56       delete this;
     57     }
     58   }
     59   UsbService* service_;
     60   content::NotificationRegistrar registrar_;
     61 };
     62 
     63 }  // namespace
     64 
     65 using content::BrowserThread;
     66 
     67 UsbService::UsbService(PlatformUsbContext context)
     68     : context_(new UsbContext(context)),
     69       next_unique_id_(0) {
     70   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     71   // Will be deleted upon NOTIFICATION_APP_TERMINATING.
     72   new ExitObserver(this);
     73 }
     74 
     75 UsbService::~UsbService() {
     76   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     77   for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
     78     it->second->OnDisconnect();
     79   }
     80 }
     81 
     82 struct InitUsbContextTraits : public LeakySingletonTraits<UsbService> {
     83   // LeakySingletonTraits<UsbService>
     84   static UsbService* New() {
     85     PlatformUsbContext context = NULL;
     86     if (libusb_init(&context) != LIBUSB_SUCCESS)
     87       return NULL;
     88     if (!context)
     89       return NULL;
     90     return new UsbService(context);
     91   }
     92 };
     93 
     94 UsbService* UsbService::GetInstance() {
     95   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     96   // UsbService deletes itself upon APP_TERMINATING.
     97   return Singleton<UsbService, InitUsbContextTraits>::get();
     98 }
     99 
    100 void UsbService::GetDevices(std::vector<scoped_refptr<UsbDevice> >* devices) {
    101   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    102   STLClearObject(devices);
    103   RefreshDevices();
    104 
    105   for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
    106     devices->push_back(it->second);
    107   }
    108 }
    109 
    110 scoped_refptr<UsbDevice> UsbService::GetDeviceById(uint32 unique_id) {
    111   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    112   RefreshDevices();
    113 
    114   for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
    115     if (it->second->unique_id() == unique_id) return it->second;
    116   }
    117   return NULL;
    118 }
    119 
    120 void UsbService::RefreshDevices() {
    121   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    122 
    123   libusb_device** platform_devices = NULL;
    124   const ssize_t device_count =
    125       libusb_get_device_list(context_->context(), &platform_devices);
    126 
    127   std::set<UsbDevice*> connected_devices;
    128   vector<PlatformUsbDevice> disconnected_devices;
    129 
    130   // Populates new devices.
    131   for (ssize_t i = 0; i < device_count; ++i) {
    132     if (!ContainsKey(devices_, platform_devices[i])) {
    133       libusb_device_descriptor descriptor;
    134       // This test is needed. A valid vendor/produce pair is required.
    135       if (0 != libusb_get_device_descriptor(platform_devices[i], &descriptor))
    136         continue;
    137       UsbDevice* new_device = new UsbDevice(context_,
    138                                             platform_devices[i],
    139                                             descriptor.idVendor,
    140                                             descriptor.idProduct,
    141                                             ++next_unique_id_);
    142       devices_[platform_devices[i]] = new_device;
    143       connected_devices.insert(new_device);
    144     } else {
    145       connected_devices.insert(devices_[platform_devices[i]].get());
    146     }
    147   }
    148 
    149   // Find disconnected devices.
    150   for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
    151     if (!ContainsKey(connected_devices, it->second)) {
    152       disconnected_devices.push_back(it->first);
    153     }
    154   }
    155 
    156   // Remove disconnected devices from devices_.
    157   for (size_t i = 0; i < disconnected_devices.size(); ++i) {
    158     // UsbDevice will be destroyed after this. The corresponding
    159     // PlatformUsbDevice will be unref'ed during this process.
    160     devices_.erase(disconnected_devices[i]);
    161   }
    162 
    163   libusb_free_device_list(platform_devices, true);
    164 }
    165