diff options
author | gdk@chromium.org <gdk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-27 02:34:05 +0000 |
---|---|---|
committer | gdk@chromium.org <gdk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-27 02:34:05 +0000 |
commit | 230ac66487cb73961907ea61138e33192a83973e (patch) | |
tree | a05f5fb92c56a4a348c70b9c2bd26903032b8ddf | |
parent | a88f1c49916211434e3a7ea5fcf1da401cbd6307 (diff) | |
download | chromium_src-230ac66487cb73961907ea61138e33192a83973e.zip chromium_src-230ac66487cb73961907ea61138e33192a83973e.tar.gz chromium_src-230ac66487cb73961907ea61138e33192a83973e.tar.bz2 |
Adding UsbService and UsbDevice constructs
UsbService and UsbDevice allow for platform-independent access to the system's
USB subsystem.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/10161035
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@134216 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/usb/usb_device.cc | 165 | ||||
-rw-r--r-- | chrome/browser/usb/usb_device.h | 115 | ||||
-rw-r--r-- | chrome/browser/usb/usb_service.cc | 86 | ||||
-rw-r--r-- | chrome/browser/usb/usb_service.h | 68 | ||||
-rw-r--r-- | chrome/browser/usb/usb_service_factory.cc | 28 | ||||
-rw-r--r-- | chrome/browser/usb/usb_service_factory.h | 31 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 7 |
7 files changed, 500 insertions, 0 deletions
diff --git a/chrome/browser/usb/usb_device.cc b/chrome/browser/usb/usb_device.cc new file mode 100644 index 0000000..9417f9d --- /dev/null +++ b/chrome/browser/usb/usb_device.cc @@ -0,0 +1,165 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/usb/usb_device.h" + +#include "base/stl_util.h" +#include "base/synchronization/lock.h" +#include "chrome/browser/usb/usb_service.h" +#include "third_party/libusb/libusb/libusb.h" + +namespace { + +static uint8 ConvertTransferDirection( + const UsbDevice::TransferDirection direction) { + switch (direction) { + case UsbDevice::INBOUND: + return LIBUSB_ENDPOINT_IN; + case UsbDevice::OUTBOUND: + return LIBUSB_ENDPOINT_OUT; + } + NOTREACHED(); + return LIBUSB_ENDPOINT_OUT; +} + +static uint8 CreateRequestType(const UsbDevice::TransferDirection direction, + const UsbDevice::TransferRequestType request_type, + const UsbDevice::TransferRecipient recipient) { + uint8 result = ConvertTransferDirection(direction); + + switch (request_type) { + case UsbDevice::STANDARD: + result |= LIBUSB_REQUEST_TYPE_STANDARD; + break; + case UsbDevice::CLASS: + result |= LIBUSB_REQUEST_TYPE_CLASS; + break; + case UsbDevice::VENDOR: + result |= LIBUSB_REQUEST_TYPE_VENDOR; + break; + case UsbDevice::RESERVED: + result |= LIBUSB_REQUEST_TYPE_RESERVED; + break; + } + + switch (recipient) { + case UsbDevice::DEVICE: + result |= LIBUSB_RECIPIENT_DEVICE; + break; + case UsbDevice::INTERFACE: + result |= LIBUSB_RECIPIENT_INTERFACE; + break; + case UsbDevice::ENDPOINT: + result |= LIBUSB_RECIPIENT_ENDPOINT; + break; + case UsbDevice::OTHER: + result |= LIBUSB_RECIPIENT_OTHER; + break; + } + + return result; +} + +static void HandleTransferCompletion(struct libusb_transfer* transfer) { + UsbDevice* const device = reinterpret_cast<UsbDevice*>(transfer->user_data); + device->TransferComplete(transfer); +} + +} // namespace + +UsbDevice::Transfer::Transfer() {} + +UsbDevice::Transfer::~Transfer() {} + +UsbDevice::UsbDevice(UsbService* service, PlatformUsbDeviceHandle handle) + : service_(service), handle_(handle) { + DCHECK(handle) << "Cannot create device with NULL handle."; +} + +UsbDevice::~UsbDevice() {} + +void UsbDevice::Close() { + CheckDevice(); + service_->CloseDevice(this); + handle_ = NULL; +} + +void UsbDevice::TransferComplete(PlatformUsbTransferHandle handle) { + base::AutoLock lock(lock_); + + DCHECK(ContainsKey(transfers_, handle)) << "Missing transfer completed"; + Transfer* const transfer = &transfers_[handle]; + if (transfer->buffer.get()) { + transfer->callback.Run(handle->status != LIBUSB_TRANSFER_COMPLETED); + } + + transfers_.erase(handle); + libusb_free_transfer(handle); +} + +void UsbDevice::ControlTransfer(const TransferDirection direction, + const TransferRequestType request_type, const TransferRecipient recipient, + const uint8 request, const uint16 value, const uint16 index, + net::IOBuffer* buffer, const size_t length, const unsigned int timeout, + const net::CompletionCallback& callback) { + CheckDevice(); + + struct libusb_transfer* const transfer = libusb_alloc_transfer(0); + const uint8 converted_type = CreateRequestType(direction, request_type, + recipient); + libusb_fill_control_setup(reinterpret_cast<uint8*>(buffer->data()), + converted_type, request, value, index, length); + libusb_fill_control_transfer(transfer, handle_, reinterpret_cast<uint8*>( + buffer->data()), reinterpret_cast<libusb_transfer_cb_fn>( + &HandleTransferCompletion), this, timeout); + AddTransfer(transfer, buffer, callback); + libusb_submit_transfer(transfer); +} + +void UsbDevice::BulkTransfer(const TransferDirection direction, + const uint8 endpoint, net::IOBuffer* buffer, const size_t length, + const unsigned int timeout, const net::CompletionCallback& callback) { + CheckDevice(); + + struct libusb_transfer* const transfer = libusb_alloc_transfer(0); + const uint8 new_endpoint = ConvertTransferDirection(direction) | endpoint; + libusb_fill_bulk_transfer(transfer, handle_, new_endpoint, + reinterpret_cast<uint8*>(buffer->data()), length, + reinterpret_cast<libusb_transfer_cb_fn>(&HandleTransferCompletion), this, + timeout); + AddTransfer(transfer, buffer, callback); + libusb_submit_transfer(transfer); +} + +void UsbDevice::InterruptTransfer(const TransferDirection direction, + const uint8 endpoint, net::IOBuffer* buffer, const size_t length, + const unsigned int timeout, const net::CompletionCallback& callback) { + CheckDevice(); + + struct libusb_transfer* const transfer = libusb_alloc_transfer(0); + const uint8 new_endpoint = ConvertTransferDirection(direction) | endpoint; + libusb_fill_interrupt_transfer(transfer, handle_, new_endpoint, + reinterpret_cast<uint8*>(buffer->data()), length, + reinterpret_cast<libusb_transfer_cb_fn>(&HandleTransferCompletion), this, + timeout); + AddTransfer(transfer, buffer, callback); + libusb_submit_transfer(transfer); +} + +void UsbDevice::CheckDevice() { + DCHECK(handle_) << "Device is already closed."; +} + +void UsbDevice::AddTransfer(PlatformUsbTransferHandle handle, + net::IOBuffer* buffer, + const net::CompletionCallback& callback) { + Transfer transfer; + transfer.buffer = buffer; + transfer.callback = callback; + + { + base::AutoLock lock(lock_); + transfers_[handle] = transfer; + } +} diff --git a/chrome/browser/usb/usb_device.h b/chrome/browser/usb/usb_device.h new file mode 100644 index 0000000..ffb38d3 --- /dev/null +++ b/chrome/browser/usb/usb_device.h @@ -0,0 +1,115 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_USB_USB_DEVICE_H_ +#define CHROME_BROWSER_USB_USB_DEVICE_H_ +#pragma once + +#include <map> + +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "net/base/completion_callback.h" +#include "net/base/io_buffer.h" +#include "third_party/libusb/libusb/libusb.h" + +typedef libusb_device* PlatformUsbDevice; +typedef libusb_device_handle* PlatformUsbDeviceHandle; +typedef libusb_transfer* PlatformUsbTransferHandle; + +class UsbService; + +namespace net { +class IOBuffer; +} // namespace net + +// A UsbDevice wraps the platform's underlying representation of what a USB +// device actually is, and provides accessors for performing many of the +// standard USB operations. +class UsbDevice : public base::RefCounted<UsbDevice> { + public: + enum TransferDirection { INBOUND, OUTBOUND }; + enum TransferRequestType { STANDARD, CLASS, VENDOR, RESERVED }; + enum TransferRecipient { DEVICE, INTERFACE, ENDPOINT, OTHER }; + + // Usually you will not want to directly create a UsbDevice, favoring to let + // the UsbService take care of the logistics of getting a platform device + // handle and handling events for it. + UsbDevice(UsbService* service, PlatformUsbDeviceHandle handle); + + PlatformUsbDeviceHandle handle() { return handle_; } + + // Close the USB device and release the underlying platform device. + void Close(); + + void ControlTransfer(const TransferDirection direction, + const TransferRequestType request_type, + const TransferRecipient recipient, + const uint8 request, + const uint16 value, + const uint16 index, + net::IOBuffer* buffer, + const size_t length, + const unsigned int timeout, + const net::CompletionCallback& callback); + + void BulkTransfer(const TransferDirection direction, + const uint8 endpoint, + net::IOBuffer* buffer, + const size_t length, + const unsigned int timeout, + const net::CompletionCallback& callback); + + void InterruptTransfer(const TransferDirection direction, + const uint8 endpoint, + net::IOBuffer* buffer, + const size_t length, + const unsigned int timeout, + const net::CompletionCallback& callback); + + // Normal code should not call this function. It is called by the platform's + // callback mechanism in such a way that it cannot be made private. Invokes + // the callbacks associated with a given transfer, and removes it from the + // in-flight transfer set. + void TransferComplete(PlatformUsbTransferHandle transfer); + + private: + struct Transfer { + Transfer(); + ~Transfer(); + + scoped_refptr<net::IOBuffer> buffer; + net::CompletionCallback callback; + }; + + friend class base::RefCounted<UsbDevice>; + virtual ~UsbDevice(); + + // Checks that the device has not yet been closed. + void CheckDevice(); + + // Starts tracking the USB transfer associated with a platform transfer + // handle. Retains the buffer and copies the completion callback until the + // transfer finishes, whereupon it invokes the callback then releases the + // buffer. + void AddTransfer(PlatformUsbTransferHandle handle, net::IOBuffer* buffer, + const net::CompletionCallback& callback); + + // The UsbService isn't referenced here to prevent a dependency cycle between + // the service and the devices. Since a service owns every device, and is + // responsible for its destruction, there is no case where a UsbDevice can + // have outlived its originating UsbService. + UsbService* const service_; + PlatformUsbDeviceHandle handle_; + + // transfers_ tracks all in-flight transfers associated with this device, + // allowing the device to retain the buffer and callback associated with a + // transfer until such time that it completes. It is protected by lock_. + base::Lock lock_; + std::map<PlatformUsbTransferHandle, Transfer> transfers_; + + DISALLOW_EVIL_CONSTRUCTORS(UsbDevice); +}; + +#endif // CHROME_BROWSER_USB_USB_DEVICE_H_ diff --git a/chrome/browser/usb/usb_service.cc b/chrome/browser/usb/usb_service.cc new file mode 100644 index 0000000..ad172d8 --- /dev/null +++ b/chrome/browser/usb/usb_service.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/usb/usb_service.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/stl_util.h" +#include "chrome/browser/usb/usb_device.h" +#include "third_party/libusb/libusb/libusb.h" + +UsbService::UsbService() : running_(true), thread_("UsbThread") { + libusb_init(&context_); + thread_.Start(); + PostHandleEventTask(); +} + +UsbService::~UsbService() {} + +// TODO(gdk): There is currently no clean way to indicate to the event handler +// thread that it must break out of the handling loop before the event timeout, +// therefore we currently are at the whim of the event handler timeout before +// the message handling thread can be joined. +void UsbService::Cleanup() { + running_ = false; + + if (!devices_.empty()) { + libusb_close(devices_.begin()->second->handle()); + } else { + LOG(WARNING) << "UsbService cannot force the USB event-handler thread to " + << "exit because there are no open devices with which to " + << "manipulate it. It maybe take up to 60 (!) seconds for the " + << "thread to join from this point."; + } + + thread_.message_loop()->PostTask(FROM_HERE, base::Bind( + &UsbService::PlatformShutdown, base::Unretained(this))); +} + +UsbDevice* UsbService::FindDevice(const uint16 vendor_id, + const uint16 product_id) { + const std::pair<uint16, uint16> key = std::make_pair(vendor_id, product_id); + if (ContainsKey(devices_, key)) { + return devices_[key]; + } + + libusb_device_handle* const handle = libusb_open_device_with_vid_pid( + context_, vendor_id, product_id); + if (!handle) { + return NULL; + } + + UsbDevice* const device = new UsbDevice(this, handle); + devices_[key] = device; + + return device; +} + +void UsbService::CloseDevice(scoped_refptr<UsbDevice> device) { + DCHECK(running_) << "Cannot close device after service has stopped running."; + + for (DeviceMap::iterator i = devices_.begin(); i != devices_.end(); ++i) { + if (i->second.get() == device.get()) { + devices_.erase(i); + libusb_close(device->handle()); + return; + } + } +} + +void UsbService::PostHandleEventTask() { + thread_.message_loop()->PostTask(FROM_HERE, base::Bind( + &UsbService::HandleEvent, base::Unretained(this))); +} + +void UsbService::HandleEvent() { + libusb_handle_events_completed(context_, NULL); + if (running_) { + PostHandleEventTask(); + } +} + +void UsbService::PlatformShutdown() { + libusb_exit(context_); +} diff --git a/chrome/browser/usb/usb_service.h b/chrome/browser/usb/usb_service.h new file mode 100644 index 0000000..3f11a7d --- /dev/null +++ b/chrome/browser/usb/usb_service.h @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_USB_USB_SERVICE_H_ +#define CHROME_BROWSER_USB_USB_SERVICE_H_ +#pragma once + +#include <map> +#include <utility> + +#include "base/basictypes.h" +#include "base/threading/thread.h" +#include "chrome/browser/profiles/profile_keyed_service.h" +#include "chrome/browser/usb/usb_device.h" +#include "third_party/libusb/libusb/libusb.h" + +typedef libusb_context* PlatformUsbContext; + +// The USB service handles creating and managing an event handler thread that is +// used to manage and dispatch USB events. It is also responsbile for device +// discovery on the system, which allows it to re-use device handles to prevent +// competition for the same USB device. +class UsbService : public ProfileKeyedService { + public: + UsbService(); + virtual ~UsbService(); + + // Cleanup must be invoked before the service is destroyed. It interrupts the + // event handling thread and disposes of open devices. + void Cleanup(); + + // Find the (topologically) first USB device identified by vendor_id and + // product_id. The created device is associated with this service, so that + // it can be used to close the device later. + UsbDevice* FindDevice(const uint16 vendor_id, const uint16 product_id); + + // This function should not be called by normal code. It is invoked by a + // UsbDevice's Close function and disposes of the associated platform handle. + void CloseDevice(scoped_refptr<UsbDevice> device); + + private: + // Posts a HandleEvent task to the event handling thread. + void PostHandleEventTask(); + + // Handles a single USB event. If the service is still running after the event + // is handled, posts another HandleEvent callback to the thread. + void HandleEvent(); + + // PlatformShutdown is invoked after event handling has been suspended and is + // used to free the platform resources associated with the service. + void PlatformShutdown(); + + bool running_; + PlatformUsbContext context_; + base::Thread thread_; + + // The devices_ map contains scoped_refptrs to all open devices, indicated by + // their vendor and product id. This allows for reusing an open device without + // creating another platform handle for it. + typedef std::map<std::pair<uint16, uint16>, scoped_refptr<UsbDevice> > + DeviceMap; + DeviceMap devices_; + + DISALLOW_EVIL_CONSTRUCTORS(UsbService); +}; + +#endif // CHROME_BROWSER_USB_USB_SERVICE_H_ diff --git a/chrome/browser/usb/usb_service_factory.cc b/chrome/browser/usb/usb_service_factory.cc new file mode 100644 index 0000000..823d582 --- /dev/null +++ b/chrome/browser/usb/usb_service_factory.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/usb/usb_service_factory.h" + +#include "base/memory/singleton.h" +#include "chrome/browser/profiles/profile_dependency_manager.h" +#include "chrome/browser/usb/usb_service.h" + +UsbServiceFactory* UsbServiceFactory::GetInstance() { + return Singleton<UsbServiceFactory>::get(); +} + +UsbService* UsbServiceFactory::GetForProfile(Profile* profile) { + return static_cast<UsbService*>( + GetInstance()->GetServiceForProfile(profile, true)); +} + +UsbServiceFactory::UsbServiceFactory() : ProfileKeyedServiceFactory( + "UsbService", ProfileDependencyManager::GetInstance()) {} + +UsbServiceFactory::~UsbServiceFactory() {} + +ProfileKeyedService* UsbServiceFactory::BuildServiceInstanceFor( + Profile* profile) const { + return new UsbService(); +} diff --git a/chrome/browser/usb/usb_service_factory.h b/chrome/browser/usb/usb_service_factory.h new file mode 100644 index 0000000..5a19fb0 --- /dev/null +++ b/chrome/browser/usb/usb_service_factory.h @@ -0,0 +1,31 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_USB_USB_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_USB_USB_SERVICE_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "chrome/browser/profiles/profile_keyed_service_factory.h" + +class Profile; +class UsbService; + +class UsbServiceFactory : public ProfileKeyedServiceFactory { + public: + static UsbService* GetForProfile(Profile* profile); + static bool HasUsbService(Profile* profile); + + static UsbServiceFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<UsbServiceFactory>; + + UsbServiceFactory(); + virtual ~UsbServiceFactory(); + + virtual ProfileKeyedService* BuildServiceInstanceFor( + Profile* profile) const OVERRIDE; +}; + +#endif // CHROME_BROWSER_USB_USB_SERVICE_FACTORY_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 2c0702b..cc6cf4c 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -3894,6 +3894,12 @@ 'browser/upgrade_detector.h', 'browser/upgrade_detector_impl.cc', 'browser/upgrade_detector_impl.h', + 'browser/usb/usb_device.cc', + 'browser/usb/usb_device.h', + 'browser/usb/usb_service.cc', + 'browser/usb/usb_service.h', + 'browser/usb/usb_service_factory.cc', + 'browser/usb/usb_service_factory.h', 'browser/user_style_sheet_watcher.cc', 'browser/user_style_sheet_watcher.h', 'browser/user_style_sheet_watcher_factory.cc', @@ -4520,6 +4526,7 @@ ['exclude', '^browser/ui/webui/print_preview/'], ['exclude', '^browser/ui/webui/signin/'], ['exclude', '^browser/ui/webui/sync_promo'], + ['exclude', '^browser/usb/'], ], }], ['OS!="android"', { |