summaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorreillyg <reillyg@chromium.org>2014-09-25 15:02:48 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-25 22:02:59 +0000
commit43c5c906400e24aa59382d47a8654c35c2cb9cc6 (patch)
tree02b7cd45c0427ef2b005a31ef747c3d5fc415d89 /device
parentd0956cf3b7e8662b94e3ef26b4d685ddc6702a0a (diff)
downloadchromium_src-43c5c906400e24aa59382d47a8654c35c2cb9cc6.zip
chromium_src-43c5c906400e24aa59382d47a8654c35c2cb9cc6.tar.gz
chromium_src-43c5c906400e24aa59382d47a8654c35c2cb9cc6.tar.bz2
Move string descriptor getters from UsbDeviceHandle to UsbDevice.
The common string descriptors: iManufacturer, iProduct and iSerialNumber should be accessible without opening the device. On Linux these can be read out of sysfs without having access to the usbfs device node. This is critical on Chrome OS because otherwise the permission broker needs to be asked for permission to access the device. On other platforms we fall back to opening the device temporarily. This will stop being the case on OS X when libusb is no longer used and on Windows this will be part of the initial enumeration process. BUG= TBR=dgozman@chromium.org Review URL: https://codereview.chromium.org/601073002 Cr-Commit-Position: refs/heads/master@{#296802}
Diffstat (limited to 'device')
-rw-r--r--device/hid/hid_connection_unittest.cc2
-rw-r--r--device/test/usb_test_gadget.h2
-rw-r--r--device/test/usb_test_gadget_impl.cc20
-rw-r--r--device/usb/usb_device.h13
-rw-r--r--device/usb/usb_device_filter_unittest.cc3
-rw-r--r--device/usb/usb_device_handle.h7
-rw-r--r--device/usb/usb_device_handle_impl.cc308
-rw-r--r--device/usb/usb_device_handle_impl.h7
-rw-r--r--device/usb/usb_device_impl.cc138
-rw-r--r--device/usb/usb_device_impl.h12
-rw-r--r--device/usb/usb_service_unittest.cc17
11 files changed, 309 insertions, 220 deletions
diff --git a/device/hid/hid_connection_unittest.cc b/device/hid/hid_connection_unittest.cc
index 5c67edf..113b27e 100644
--- a/device/hid/hid_connection_unittest.cc
+++ b/device/hid/hid_connection_unittest.cc
@@ -100,7 +100,7 @@ class HidConnectionTest : public testing::Test {
for (std::vector<HidDeviceInfo>::iterator it = devices.begin();
it != devices.end();
++it) {
- if (it->serial_number == test_gadget_->GetSerial()) {
+ if (it->serial_number == test_gadget_->GetSerialNumber()) {
device_id_ = it->device_id;
break;
}
diff --git a/device/test/usb_test_gadget.h b/device/test/usb_test_gadget.h
index ef97426..2055ae9 100644
--- a/device/test/usb_test_gadget.h
+++ b/device/test/usb_test_gadget.h
@@ -35,7 +35,7 @@ class UsbTestGadget {
virtual bool SetType(Type type) = 0;
virtual UsbDevice* GetDevice() const = 0;
- virtual std::string GetSerial() const = 0;
+ virtual std::string GetSerialNumber() const = 0;
protected:
UsbTestGadget() {}
diff --git a/device/test/usb_test_gadget_impl.cc b/device/test/usb_test_gadget_impl.cc
index a0bf4d3..fa4f3d7 100644
--- a/device/test/usb_test_gadget_impl.cc
+++ b/device/test/usb_test_gadget_impl.cc
@@ -71,7 +71,7 @@ class UsbTestGadgetImpl : public UsbTestGadget {
virtual bool Reconnect() OVERRIDE;
virtual bool SetType(Type type) OVERRIDE;
virtual UsbDevice* GetDevice() const OVERRIDE;
- virtual std::string GetSerial() const OVERRIDE;
+ virtual std::string GetSerialNumber() const OVERRIDE;
protected:
UsbTestGadgetImpl();
@@ -166,7 +166,7 @@ UsbDevice* UsbTestGadgetImpl::GetDevice() const {
return device_.get();
}
-std::string UsbTestGadgetImpl::GetSerial() const {
+std::string UsbTestGadgetImpl::GetSerialNumber() const {
return device_address_;
}
@@ -205,13 +205,8 @@ bool UsbTestGadgetImpl::FindUnclaimed() {
devices.begin(); iter != devices.end(); ++iter) {
const scoped_refptr<UsbDevice> &device = *iter;
if (device->vendor_id() == 0x18D1 && device->product_id() == 0x58F0) {
- scoped_refptr<UsbDeviceHandle> handle = device->Open();
- if (handle.get() == NULL) {
- continue;
- }
-
base::string16 serial_utf16;
- if (!handle->GetSerial(&serial_utf16)) {
+ if (!device->GetSerialNumber(&serial_utf16)) {
continue;
}
@@ -340,7 +335,7 @@ bool UsbTestGadgetImpl::Update() {
bool UsbTestGadgetImpl::FindClaimed() {
CHECK(!device_.get());
- std::string expected_serial = GetSerial();
+ std::string expected_serial = GetSerialNumber();
std::vector<scoped_refptr<UsbDevice> > devices;
usb_service_->GetDevices(&devices);
@@ -362,13 +357,8 @@ bool UsbTestGadgetImpl::FindClaimed() {
continue;
}
- scoped_refptr<UsbDeviceHandle> handle(device->Open());
- if (handle.get() == NULL) {
- continue;
- }
-
base::string16 serial_utf16;
- if (!handle->GetSerial(&serial_utf16)) {
+ if (!device->GetSerialNumber(&serial_utf16)) {
continue;
}
diff --git a/device/usb/usb_device.h b/device/usb/usb_device.h
index f4bd591..7ea964c 100644
--- a/device/usb/usb_device.h
+++ b/device/usb/usb_device.h
@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
+#include "base/strings/string16.h"
namespace device {
@@ -54,6 +55,18 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> {
// Blocking method. Must be called on FILE thread.
virtual const UsbConfigDescriptor& GetConfiguration() = 0;
+ // Gets the manufacturer string of the device, or returns false.
+ // Blocking method. Must be called on FILE thread.
+ virtual bool GetManufacturer(base::string16* manufacturer) = 0;
+
+ // Gets the product string of the device, or returns false.
+ // Blocking method. Must be called on FILE thread.
+ virtual bool GetProduct(base::string16* product) = 0;
+
+ // Gets the serial number string of the device, or returns false.
+ // Blocking method. Must be called on FILE thread.
+ virtual bool GetSerialNumber(base::string16* serial) = 0;
+
void AddObserver(Observer* obs) { observer_list_.AddObserver(obs); }
void RemoveObserver(Observer* obs) { observer_list_.RemoveObserver(obs); }
diff --git a/device/usb/usb_device_filter_unittest.cc b/device/usb/usb_device_filter_unittest.cc
index 90097f6..595519a 100644
--- a/device/usb/usb_device_filter_unittest.cc
+++ b/device/usb/usb_device_filter_unittest.cc
@@ -30,6 +30,9 @@ class MockUsbDevice : public UsbDevice {
MOCK_METHOD2(RequestUsbAccess, void(int, const base::Callback<void(bool)>&));
#endif
MOCK_METHOD0(GetConfiguration, const UsbConfigDescriptor&());
+ MOCK_METHOD1(GetManufacturer, bool(base::string16*));
+ MOCK_METHOD1(GetProduct, bool(base::string16*));
+ MOCK_METHOD1(GetSerialNumber, bool(base::string16*));
private:
virtual ~MockUsbDevice() {}
diff --git a/device/usb/usb_device_handle.h b/device/usb/usb_device_handle.h
index d0b7484..812512a 100644
--- a/device/usb/usb_device_handle.h
+++ b/device/usb/usb_device_handle.h
@@ -56,9 +56,10 @@ class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> {
virtual bool SetInterfaceAlternateSetting(int interface_number,
int alternate_setting) = 0;
virtual bool ResetDevice() = 0;
- virtual bool GetManufacturer(base::string16* manufacturer) = 0;
- virtual bool GetProduct(base::string16* product) = 0;
- virtual bool GetSerial(base::string16* serial) = 0;
+
+ // Gets the string descriptor with the given index from the device, or returns
+ // false. This method is blocking and must be called on the FILE thread.
+ virtual bool GetStringDescriptor(uint8 string_id, base::string16* string) = 0;
// Async IO. Can be called on any thread.
virtual void ControlTransfer(UsbEndpointDirection direction,
diff --git a/device/usb/usb_device_handle_impl.cc b/device/usb/usb_device_handle_impl.cc
index 2d8c690..50c2609 100644
--- a/device/usb/usb_device_handle_impl.cc
+++ b/device/usb/usb_device_handle_impl.cc
@@ -211,96 +211,6 @@ void UsbDeviceHandleImpl::Close() {
device_->Close(this);
}
-/* static */
-void LIBUSB_CALL UsbDeviceHandleImpl::PlatformTransferCallback(
- PlatformUsbTransferHandle transfer) {
- UsbDeviceHandleImpl* device_handle =
- reinterpret_cast<UsbDeviceHandleImpl*>(transfer->user_data);
- device_handle->task_runner_->PostTask(
- FROM_HERE,
- base::Bind(
- &UsbDeviceHandleImpl::CompleteTransfer, device_handle, transfer));
-}
-
-void UsbDeviceHandleImpl::CompleteTransfer(PlatformUsbTransferHandle handle) {
- DCHECK(ContainsKey(transfers_, handle)) << "Missing transfer completed";
-
- Transfer transfer = transfers_[handle];
- transfers_.erase(handle);
-
- DCHECK_GE(handle->actual_length, 0) << "Negative actual length received";
- size_t actual_length =
- static_cast<size_t>(std::max(handle->actual_length, 0));
-
- DCHECK(transfer.length >= actual_length)
- << "data too big for our buffer (libusb failure?)";
-
- switch (transfer.transfer_type) {
- case USB_TRANSFER_CONTROL:
- // If the transfer is a control transfer we do not expose the control
- // setup header to the caller. This logic strips off the header if
- // present before invoking the callback provided with the transfer.
- if (actual_length > 0) {
- CHECK(transfer.length >= LIBUSB_CONTROL_SETUP_SIZE)
- << "buffer was not correctly set: too small for the control header";
-
- if (transfer.length >= (LIBUSB_CONTROL_SETUP_SIZE + actual_length)) {
- // If the payload is zero bytes long, pad out the allocated buffer
- // size to one byte so that an IOBuffer of that size can be allocated.
- scoped_refptr<net::IOBuffer> resized_buffer =
- new net::IOBuffer(static_cast<int>(
- std::max(actual_length, static_cast<size_t>(1))));
- memcpy(resized_buffer->data(),
- transfer.buffer->data() + LIBUSB_CONTROL_SETUP_SIZE,
- actual_length);
- transfer.buffer = resized_buffer;
- }
- }
- break;
-
- case USB_TRANSFER_ISOCHRONOUS:
- // Isochronous replies might carry data in the different isoc packets even
- // if the transfer actual_data value is zero. Furthermore, not all of the
- // received packets might contain data, so we need to calculate how many
- // data bytes we are effectively providing and pack the results.
- if (actual_length == 0) {
- size_t packet_buffer_start = 0;
- for (int i = 0; i < handle->num_iso_packets; ++i) {
- PlatformUsbIsoPacketDescriptor packet = &handle->iso_packet_desc[i];
- if (packet->actual_length > 0) {
- // We don't need to copy as long as all packets until now provide
- // all the data the packet can hold.
- if (actual_length < packet_buffer_start) {
- CHECK(packet_buffer_start + packet->actual_length <=
- transfer.length);
- memmove(transfer.buffer->data() + actual_length,
- transfer.buffer->data() + packet_buffer_start,
- packet->actual_length);
- }
- actual_length += packet->actual_length;
- }
-
- packet_buffer_start += packet->length;
- }
- }
- break;
-
- case USB_TRANSFER_BULK:
- case USB_TRANSFER_INTERRUPT:
- break;
-
- default:
- NOTREACHED() << "Invalid usb transfer type";
- break;
- }
-
- transfer.Complete(ConvertTransferStatus(handle->status), actual_length);
- libusb_free_transfer(handle);
-
- // Must release interface first before actually delete this.
- transfer.claimed_interface = NULL;
-}
-
bool UsbDeviceHandleImpl::ClaimInterface(const int interface_number) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!device_)
@@ -375,40 +285,6 @@ bool UsbDeviceHandleImpl::ResetDevice() {
return rv == LIBUSB_SUCCESS;
}
-bool UsbDeviceHandleImpl::GetSupportedLanguages() {
- if (!languages_.empty()) {
- return true;
- }
-
- // The 1-byte length field limits the descriptor to 256-bytes (128 uint16s).
- uint16 languages[128];
- int size = libusb_get_string_descriptor(
- handle_,
- 0,
- 0,
- reinterpret_cast<unsigned char*>(&languages[0]),
- sizeof(languages));
- if (size < 0) {
- VLOG(1) << "Failed to get list of supported languages: "
- << ConvertPlatformUsbErrorToString(size);
- return false;
- } else if (size < 2) {
- VLOG(1) << "String descriptor zero has no header.";
- return false;
- // The first 2 bytes of the descriptor are the total length and type tag.
- } else if ((languages[0] & 0xff) != size) {
- VLOG(1) << "String descriptor zero size mismatch: " << (languages[0] & 0xff)
- << " != " << size;
- return false;
- } else if ((languages[0] >> 8) != LIBUSB_DT_STRING) {
- VLOG(1) << "String descriptor zero is not a string descriptor.";
- return false;
- }
-
- languages_.assign(languages[1], languages[(size - 2) / 2]);
- return true;
-}
-
bool UsbDeviceHandleImpl::GetStringDescriptor(uint8 string_id,
base::string16* string) {
if (!GetSupportedLanguages()) {
@@ -459,66 +335,6 @@ bool UsbDeviceHandleImpl::GetStringDescriptor(uint8 string_id,
return false;
}
-bool UsbDeviceHandleImpl::GetManufacturer(base::string16* manufacturer) {
- DCHECK(thread_checker_.CalledOnValidThread());
- PlatformUsbDevice device = libusb_get_device(handle_);
- libusb_device_descriptor desc;
-
- // This is a non-blocking call as libusb has the descriptor in memory.
- const int rv = libusb_get_device_descriptor(device, &desc);
- if (rv != LIBUSB_SUCCESS) {
- VLOG(1) << "Failed to read device descriptor: "
- << ConvertPlatformUsbErrorToString(rv);
- return false;
- }
-
- if (desc.iManufacturer == 0) {
- return false;
- }
-
- return GetStringDescriptor(desc.iManufacturer, manufacturer);
-}
-
-bool UsbDeviceHandleImpl::GetProduct(base::string16* product) {
- DCHECK(thread_checker_.CalledOnValidThread());
- PlatformUsbDevice device = libusb_get_device(handle_);
- libusb_device_descriptor desc;
-
- // This is a non-blocking call as libusb has the descriptor in memory.
- const int rv = libusb_get_device_descriptor(device, &desc);
- if (rv != LIBUSB_SUCCESS) {
- VLOG(1) << "Failed to read device descriptor: "
- << ConvertPlatformUsbErrorToString(rv);
- return false;
- }
-
- if (desc.iProduct == 0) {
- return false;
- }
-
- return GetStringDescriptor(desc.iProduct, product);
-}
-
-bool UsbDeviceHandleImpl::GetSerial(base::string16* serial) {
- DCHECK(thread_checker_.CalledOnValidThread());
- PlatformUsbDevice device = libusb_get_device(handle_);
- libusb_device_descriptor desc;
-
- // This is a non-blocking call as libusb has the descriptor in memory.
- const int rv = libusb_get_device_descriptor(device, &desc);
- if (rv != LIBUSB_SUCCESS) {
- VLOG(1) << "Failed to read device descriptor: "
- << ConvertPlatformUsbErrorToString(rv);
- return false;
- }
-
- if (desc.iSerialNumber == 0) {
- return false;
- }
-
- return GetStringDescriptor(desc.iSerialNumber, serial);
-}
-
void UsbDeviceHandleImpl::ControlTransfer(
const UsbEndpointDirection direction,
const TransferRequestType request_type,
@@ -749,6 +565,130 @@ void UsbDeviceHandleImpl::SubmitTransfer(
}
}
+/* static */
+void LIBUSB_CALL UsbDeviceHandleImpl::PlatformTransferCallback(
+ PlatformUsbTransferHandle transfer) {
+ UsbDeviceHandleImpl* device_handle =
+ reinterpret_cast<UsbDeviceHandleImpl*>(transfer->user_data);
+ device_handle->task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &UsbDeviceHandleImpl::CompleteTransfer, device_handle, transfer));
+}
+
+void UsbDeviceHandleImpl::CompleteTransfer(PlatformUsbTransferHandle handle) {
+ DCHECK(ContainsKey(transfers_, handle)) << "Missing transfer completed";
+
+ Transfer transfer = transfers_[handle];
+ transfers_.erase(handle);
+
+ DCHECK_GE(handle->actual_length, 0) << "Negative actual length received";
+ size_t actual_length =
+ static_cast<size_t>(std::max(handle->actual_length, 0));
+
+ DCHECK(transfer.length >= actual_length)
+ << "data too big for our buffer (libusb failure?)";
+
+ switch (transfer.transfer_type) {
+ case USB_TRANSFER_CONTROL:
+ // If the transfer is a control transfer we do not expose the control
+ // setup header to the caller. This logic strips off the header if
+ // present before invoking the callback provided with the transfer.
+ if (actual_length > 0) {
+ CHECK(transfer.length >= LIBUSB_CONTROL_SETUP_SIZE)
+ << "buffer was not correctly set: too small for the control header";
+
+ if (transfer.length >= (LIBUSB_CONTROL_SETUP_SIZE + actual_length)) {
+ // If the payload is zero bytes long, pad out the allocated buffer
+ // size to one byte so that an IOBuffer of that size can be allocated.
+ scoped_refptr<net::IOBuffer> resized_buffer =
+ new net::IOBuffer(static_cast<int>(
+ std::max(actual_length, static_cast<size_t>(1))));
+ memcpy(resized_buffer->data(),
+ transfer.buffer->data() + LIBUSB_CONTROL_SETUP_SIZE,
+ actual_length);
+ transfer.buffer = resized_buffer;
+ }
+ }
+ break;
+
+ case USB_TRANSFER_ISOCHRONOUS:
+ // Isochronous replies might carry data in the different isoc packets even
+ // if the transfer actual_data value is zero. Furthermore, not all of the
+ // received packets might contain data, so we need to calculate how many
+ // data bytes we are effectively providing and pack the results.
+ if (actual_length == 0) {
+ size_t packet_buffer_start = 0;
+ for (int i = 0; i < handle->num_iso_packets; ++i) {
+ PlatformUsbIsoPacketDescriptor packet = &handle->iso_packet_desc[i];
+ if (packet->actual_length > 0) {
+ // We don't need to copy as long as all packets until now provide
+ // all the data the packet can hold.
+ if (actual_length < packet_buffer_start) {
+ CHECK(packet_buffer_start + packet->actual_length <=
+ transfer.length);
+ memmove(transfer.buffer->data() + actual_length,
+ transfer.buffer->data() + packet_buffer_start,
+ packet->actual_length);
+ }
+ actual_length += packet->actual_length;
+ }
+
+ packet_buffer_start += packet->length;
+ }
+ }
+ break;
+
+ case USB_TRANSFER_BULK:
+ case USB_TRANSFER_INTERRUPT:
+ break;
+
+ default:
+ NOTREACHED() << "Invalid usb transfer type";
+ break;
+ }
+
+ transfer.Complete(ConvertTransferStatus(handle->status), actual_length);
+ libusb_free_transfer(handle);
+
+ // Must release interface first before actually delete this.
+ transfer.claimed_interface = NULL;
+}
+
+bool UsbDeviceHandleImpl::GetSupportedLanguages() {
+ if (!languages_.empty()) {
+ return true;
+ }
+
+ // The 1-byte length field limits the descriptor to 256-bytes (128 uint16s).
+ uint16 languages[128];
+ int size = libusb_get_string_descriptor(
+ handle_,
+ 0,
+ 0,
+ reinterpret_cast<unsigned char*>(&languages[0]),
+ sizeof(languages));
+ if (size < 0) {
+ VLOG(1) << "Failed to get list of supported languages: "
+ << ConvertPlatformUsbErrorToString(size);
+ return false;
+ } else if (size < 2) {
+ VLOG(1) << "String descriptor zero has no header.";
+ return false;
+ // The first 2 bytes of the descriptor are the total length and type tag.
+ } else if ((languages[0] & 0xff) != size) {
+ VLOG(1) << "String descriptor zero size mismatch: " << (languages[0] & 0xff)
+ << " != " << size;
+ return false;
+ } else if ((languages[0] >> 8) != LIBUSB_DT_STRING) {
+ VLOG(1) << "String descriptor zero is not a string descriptor.";
+ return false;
+ }
+
+ languages_.assign(languages[1], languages[(size - 2) / 2]);
+ return true;
+}
+
void UsbDeviceHandleImpl::InternalClose() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!device_)
diff --git a/device/usb/usb_device_handle_impl.h b/device/usb/usb_device_handle_impl.h
index 11ae94f..effb9ba 100644
--- a/device/usb/usb_device_handle_impl.h
+++ b/device/usb/usb_device_handle_impl.h
@@ -40,9 +40,9 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
virtual bool SetInterfaceAlternateSetting(int interface_number,
int alternate_setting) OVERRIDE;
virtual bool ResetDevice() OVERRIDE;
- virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE;
- virtual bool GetProduct(base::string16* product) OVERRIDE;
- virtual bool GetSerial(base::string16* serial) OVERRIDE;
+ virtual bool GetStringDescriptor(uint8 string_id,
+ base::string16* string) OVERRIDE;
+
virtual void ControlTransfer(UsbEndpointDirection direction,
TransferRequestType request_type,
TransferRecipient recipient,
@@ -132,7 +132,6 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
void CompleteTransfer(PlatformUsbTransferHandle transfer);
bool GetSupportedLanguages();
- bool GetStringDescriptor(uint8 string_id, base::string16* string);
// Informs the object to drop internal references.
void InternalClose();
diff --git a/device/usb/usb_device_impl.cc b/device/usb/usb_device_impl.cc
index a9838dc..cd29e1c 100644
--- a/device/usb/usb_device_impl.cc
+++ b/device/usb/usb_device_impl.cc
@@ -10,6 +10,8 @@
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/thread_task_runner_handle.h"
#include "device/usb/usb_context.h"
#include "device/usb/usb_descriptors.h"
@@ -23,6 +25,10 @@
#include "chromeos/dbus/permission_broker_client.h"
#endif // defined(OS_CHROMEOS)
+#if defined(OS_LINUX)
+#include "device/udev_linux/udev.h"
+#endif // defined(OS_LINUX)
+
namespace device {
namespace {
@@ -123,6 +129,44 @@ UsbDeviceImpl::UsbDeviceImpl(
ui_task_runner_(ui_task_runner) {
CHECK(platform_device) << "platform_device cannot be NULL";
libusb_ref_device(platform_device);
+
+#if defined(OS_LINUX)
+ ScopedUdevPtr udev(udev_new());
+ ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev.get()));
+
+ udev_enumerate_add_match_subsystem(enumerate.get(), "usb");
+ if (udev_enumerate_scan_devices(enumerate.get()) != 0) {
+ return;
+ }
+ std::string bus_number =
+ base::IntToString(libusb_get_bus_number(platform_device));
+ std::string device_address =
+ base::IntToString(libusb_get_device_address(platform_device));
+ udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
+ for (udev_list_entry* i = devices; i != NULL;
+ i = udev_list_entry_get_next(i)) {
+ ScopedUdevDevicePtr device(
+ udev_device_new_from_syspath(udev.get(), udev_list_entry_get_name(i)));
+ if (device) {
+ const char* value = udev_device_get_sysattr_value(device.get(), "busnum");
+ if (!value || bus_number != value) {
+ continue;
+ }
+ value = udev_device_get_sysattr_value(device.get(), "devnum");
+ if (!value || device_address != value) {
+ continue;
+ }
+
+ value = udev_device_get_sysattr_value(device.get(), "manufacturer");
+ manufacturer_ = value ? value : "";
+ value = udev_device_get_sysattr_value(device.get(), "product");
+ product_ = value ? value : "";
+ value = udev_device_get_sysattr_value(device.get(), "serial");
+ serial_number_ = value ? value : "";
+ break;
+ }
+ }
+#endif
}
UsbDeviceImpl::~UsbDeviceImpl() {
@@ -275,6 +319,100 @@ const UsbConfigDescriptor& UsbDeviceImpl::GetConfiguration() {
return current_configuration_;
}
+bool UsbDeviceImpl::GetManufacturer(base::string16* manufacturer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+#if defined(OS_LINUX)
+ if (manufacturer_.empty()) {
+ return false;
+ }
+ *manufacturer = base::UTF8ToUTF16(manufacturer_);
+ return true;
+#else
+ // This is a non-blocking call as libusb has the descriptor in memory.
+ libusb_device_descriptor desc;
+ const int rv = libusb_get_device_descriptor(platform_device_, &desc);
+ if (rv != LIBUSB_SUCCESS) {
+ VLOG(1) << "Failed to read device descriptor: "
+ << ConvertPlatformUsbErrorToString(rv);
+ return false;
+ }
+
+ if (desc.iManufacturer == 0) {
+ return false;
+ }
+
+ scoped_refptr<UsbDeviceHandle> device_handle = Open();
+ if (device_handle.get()) {
+ return device_handle->GetStringDescriptor(desc.iManufacturer, manufacturer);
+ }
+ return false;
+#endif
+}
+
+bool UsbDeviceImpl::GetProduct(base::string16* product) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+#if defined(OS_LINUX)
+ if (product_.empty()) {
+ return false;
+ }
+ *product = base::UTF8ToUTF16(product_);
+ return true;
+#else
+ // This is a non-blocking call as libusb has the descriptor in memory.
+ libusb_device_descriptor desc;
+ const int rv = libusb_get_device_descriptor(platform_device_, &desc);
+ if (rv != LIBUSB_SUCCESS) {
+ VLOG(1) << "Failed to read device descriptor: "
+ << ConvertPlatformUsbErrorToString(rv);
+ return false;
+ }
+
+ if (desc.iProduct == 0) {
+ return false;
+ }
+
+ scoped_refptr<UsbDeviceHandle> device_handle = Open();
+ if (device_handle.get()) {
+ return device_handle->GetStringDescriptor(desc.iProduct, product);
+ }
+ return false;
+#endif
+}
+
+bool UsbDeviceImpl::GetSerialNumber(base::string16* serial_number) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+#if defined(OS_LINUX)
+ if (serial_number_.empty()) {
+ return false;
+ }
+ *serial_number = base::UTF8ToUTF16(serial_number_);
+ return true;
+#else
+ // This is a non-blocking call as libusb has the descriptor in memory.
+ libusb_device_descriptor desc;
+ const int rv = libusb_get_device_descriptor(platform_device_, &desc);
+ if (rv != LIBUSB_SUCCESS) {
+ VLOG(1) << "Failed to read device descriptor: "
+ << ConvertPlatformUsbErrorToString(rv);
+ return false;
+ }
+
+ if (desc.iSerialNumber == 0) {
+ return false;
+ }
+
+ scoped_refptr<UsbDeviceHandle> device_handle = Open();
+ if (device_handle.get()) {
+ return device_handle->GetStringDescriptor(desc.iSerialNumber,
+ serial_number);
+ }
+ return false;
+#endif
+}
+
void UsbDeviceImpl::OnDisconnect() {
DCHECK(thread_checker_.CalledOnValidThread());
HandlesVector handles;
diff --git a/device/usb/usb_device_impl.h b/device/usb/usb_device_impl.h
index e95c8db..f0d22b2 100644
--- a/device/usb/usb_device_impl.h
+++ b/device/usb/usb_device_impl.h
@@ -39,6 +39,9 @@ class UsbDeviceImpl : public UsbDevice {
virtual scoped_refptr<UsbDeviceHandle> Open() OVERRIDE;
virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE;
virtual const UsbConfigDescriptor& GetConfiguration() OVERRIDE;
+ virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE;
+ virtual bool GetProduct(base::string16* product) OVERRIDE;
+ virtual bool GetSerialNumber(base::string16* serial_number) OVERRIDE;
protected:
friend class UsbServiceImpl;
@@ -60,6 +63,15 @@ class UsbDeviceImpl : public UsbDevice {
base::ThreadChecker thread_checker_;
PlatformUsbDevice platform_device_;
+#if defined(OS_LINUX)
+ // On Linux these properties are read from sysfs when the device is enumerated
+ // to avoid hitting the permission broker on Chrome OS for a real string
+ // descriptor request.
+ std::string manufacturer_;
+ std::string product_;
+ std::string serial_number_;
+#endif
+
// The active configuration descriptor is not read immediately but cached for
// later use.
bool current_configuration_cached_;
diff --git a/device/usb/usb_service_unittest.cc b/device/usb/usb_service_unittest.cc
index 5436200..e8affa0 100644
--- a/device/usb/usb_service_unittest.cc
+++ b/device/usb/usb_service_unittest.cc
@@ -29,23 +29,16 @@ TEST_F(UsbServiceTest, ClaimGadget) {
scoped_ptr<UsbTestGadget> gadget = UsbTestGadget::Claim();
ASSERT_TRUE(gadget.get());
- scoped_refptr<UsbDeviceHandle> handle = gadget->GetDevice()->Open();
-
+ scoped_refptr<UsbDevice> device = gadget->GetDevice();
base::string16 utf16;
- ASSERT_TRUE(handle->GetManufacturer(&utf16));
- ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(utf16));
- // Check again to make sure string descriptor caching works.
+ ASSERT_TRUE(device->GetManufacturer(&utf16));
ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(utf16));
- ASSERT_TRUE(handle->GetProduct(&utf16));
- ASSERT_EQ("Test Gadget (default state)", base::UTF16ToUTF8(utf16));
- // Check again to make sure string descriptor caching works.
+ ASSERT_TRUE(device->GetProduct(&utf16));
ASSERT_EQ("Test Gadget (default state)", base::UTF16ToUTF8(utf16));
- ASSERT_TRUE(handle->GetSerial(&utf16));
- ASSERT_EQ(gadget->GetSerial(), base::UTF16ToUTF8(utf16));
- // Check again to make sure string descriptor caching works.
- ASSERT_EQ(gadget->GetSerial(), base::UTF16ToUTF8(utf16));
+ ASSERT_TRUE(device->GetSerialNumber(&utf16));
+ ASSERT_EQ(gadget->GetSerialNumber(), base::UTF16ToUTF8(utf16));
}
TEST_F(UsbServiceTest, DisconnectAndReconnect) {