summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgdk@chromium.org <gdk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-27 02:34:05 +0000
committergdk@chromium.org <gdk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-27 02:34:05 +0000
commit230ac66487cb73961907ea61138e33192a83973e (patch)
treea05f5fb92c56a4a348c70b9c2bd26903032b8ddf
parenta88f1c49916211434e3a7ea5fcf1da401cbd6307 (diff)
downloadchromium_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.cc165
-rw-r--r--chrome/browser/usb/usb_device.h115
-rw-r--r--chrome/browser/usb/usb_service.cc86
-rw-r--r--chrome/browser/usb/usb_service.h68
-rw-r--r--chrome/browser/usb/usb_service_factory.cc28
-rw-r--r--chrome/browser/usb/usb_service_factory.h31
-rw-r--r--chrome/chrome_browser.gypi7
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"', {