Commit 43c5c906 authored by reillyg's avatar reillyg Committed by Commit bot

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}
parent d0956cf3
...@@ -158,11 +158,7 @@ bool SavedDevicesService::SavedDevices::IsRegistered( ...@@ -158,11 +158,7 @@ bool SavedDevicesService::SavedDevices::IsRegistered(
continue; continue;
} }
if (!have_serial_number) { if (!have_serial_number) {
scoped_refptr<UsbDeviceHandle> device_handle = device->Open(); if (!device->GetSerialNumber(&serial_number)) {
if (!device_handle.get()) {
break;
}
if (!device_handle->GetSerial(&serial_number)) {
break; break;
} }
have_serial_number = true; have_serial_number = true;
......
...@@ -25,88 +25,27 @@ using device::UsbEndpointDirection; ...@@ -25,88 +25,27 @@ using device::UsbEndpointDirection;
using device::UsbTransferCallback; using device::UsbTransferCallback;
using testing::Return; using testing::Return;
class MockUsbDeviceHandle : public UsbDeviceHandle {
public:
MockUsbDeviceHandle(const std::string& serial_number)
: UsbDeviceHandle(), serial_number_(serial_number) {}
MOCK_CONST_METHOD0(GetDevice, scoped_refptr<UsbDevice>());
MOCK_METHOD0(Close, void());
MOCK_METHOD10(ControlTransfer,
void(UsbEndpointDirection direction,
TransferRequestType request_type,
TransferRecipient recipient,
uint8 request,
uint16 value,
uint16 index,
net::IOBuffer* buffer,
size_t length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD6(BulkTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
net::IOBuffer* buffer,
size_t length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD6(InterruptTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
net::IOBuffer* buffer,
size_t length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD8(IsochronousTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
net::IOBuffer* buffer,
size_t length,
unsigned int packets,
unsigned int packet_length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD0(ResetDevice, bool());
MOCK_METHOD1(ClaimInterface, bool(int interface_number));
MOCK_METHOD1(ReleaseInterface, bool(int interface_number));
MOCK_METHOD2(SetInterfaceAlternateSetting,
bool(int interface_number, int alternate_setting));
MOCK_METHOD1(GetManufacturer, bool(base::string16* manufacturer));
MOCK_METHOD1(GetProduct, bool(base::string16* product));
bool GetSerial(base::string16* serial) OVERRIDE {
if (serial_number_.empty()) {
return false;
}
*serial = base::UTF8ToUTF16(serial_number_);
return true;
}
private:
virtual ~MockUsbDeviceHandle() {}
const std::string serial_number_;
};
class MockUsbDevice : public UsbDevice { class MockUsbDevice : public UsbDevice {
public: public:
MockUsbDevice(const std::string& serial_number, uint32 unique_id) MockUsbDevice(const std::string& serial_number, uint32 unique_id)
: UsbDevice(0, 0, unique_id), serial_number_(serial_number) {} : UsbDevice(0, 0, unique_id), serial_number_(serial_number) {}
MOCK_METHOD0(Open, scoped_refptr<UsbDeviceHandle>());
MOCK_METHOD1(Close, bool(scoped_refptr<UsbDeviceHandle>)); MOCK_METHOD1(Close, bool(scoped_refptr<UsbDeviceHandle>));
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
MOCK_METHOD2(RequestUsbAccess, void(int, const base::Callback<void(bool)>&)); MOCK_METHOD2(RequestUsbAccess, void(int, const base::Callback<void(bool)>&));
#endif #endif
MOCK_METHOD0(GetConfiguration, const device::UsbConfigDescriptor&()); MOCK_METHOD0(GetConfiguration, const device::UsbConfigDescriptor&());
MOCK_METHOD1(GetManufacturer, bool(base::string16*));
MOCK_METHOD1(GetProduct, bool(base::string16*));
bool GetSerialNumber(base::string16* serial) OVERRIDE {
if (serial_number_.empty()) {
return false;
}
scoped_refptr<UsbDeviceHandle> Open() OVERRIDE { *serial = base::UTF8ToUTF16(serial_number_);
return new MockUsbDeviceHandle(serial_number_); return true;
} }
void NotifyDisconnect() { UsbDevice::NotifyDisconnect(); } void NotifyDisconnect() { UsbDevice::NotifyDisconnect(); }
......
...@@ -144,20 +144,8 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { ...@@ -144,20 +144,8 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
} }
virtual bool ResetDevice() OVERRIDE { return true; } virtual bool ResetDevice() OVERRIDE { return true; }
virtual bool GetStringDescriptor(uint8_t string_id, base::string16* content) {
virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE { return false;
*manufacturer = base::UTF8ToUTF16(kDeviceManufacturer);
return true;
}
virtual bool GetProduct(base::string16* product) OVERRIDE {
*product = base::UTF8ToUTF16(kDeviceModel);
return true;
}
virtual bool GetSerial(base::string16* serial) OVERRIDE {
*serial = base::UTF8ToUTF16(kDeviceSerial);
return true;
} }
// Async IO. Can be called on any thread. // Async IO. Can be called on any thread.
...@@ -379,6 +367,21 @@ class MockUsbDevice : public UsbDevice { ...@@ -379,6 +367,21 @@ class MockUsbDevice : public UsbDevice {
return config_desc_; return config_desc_;
} }
virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE {
*manufacturer = base::UTF8ToUTF16(kDeviceManufacturer);
return true;
}
virtual bool GetProduct(base::string16* product) OVERRIDE {
*product = base::UTF8ToUTF16(kDeviceModel);
return true;
}
virtual bool GetSerialNumber(base::string16* serial) OVERRIDE {
*serial = base::UTF8ToUTF16(kDeviceSerial);
return true;
}
virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE { virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE {
return true; return true;
} }
......
...@@ -96,7 +96,7 @@ scoped_refptr<AndroidUsbDevice> ClaimInterface( ...@@ -96,7 +96,7 @@ scoped_refptr<AndroidUsbDevice> ClaimInterface(
return NULL; return NULL;
base::string16 serial; base::string16 serial;
if (!usb_handle->GetSerial(&serial) || serial.empty()) if (!usb_handle->GetDevice()->GetSerialNumber(&serial) || serial.empty())
return NULL; return NULL;
return new AndroidUsbDevice(rsa_key, return new AndroidUsbDevice(rsa_key,
......
...@@ -100,7 +100,7 @@ class HidConnectionTest : public testing::Test { ...@@ -100,7 +100,7 @@ class HidConnectionTest : public testing::Test {
for (std::vector<HidDeviceInfo>::iterator it = devices.begin(); for (std::vector<HidDeviceInfo>::iterator it = devices.begin();
it != devices.end(); it != devices.end();
++it) { ++it) {
if (it->serial_number == test_gadget_->GetSerial()) { if (it->serial_number == test_gadget_->GetSerialNumber()) {
device_id_ = it->device_id; device_id_ = it->device_id;
break; break;
} }
......
...@@ -35,7 +35,7 @@ class UsbTestGadget { ...@@ -35,7 +35,7 @@ class UsbTestGadget {
virtual bool SetType(Type type) = 0; virtual bool SetType(Type type) = 0;
virtual UsbDevice* GetDevice() const = 0; virtual UsbDevice* GetDevice() const = 0;
virtual std::string GetSerial() const = 0; virtual std::string GetSerialNumber() const = 0;
protected: protected:
UsbTestGadget() {} UsbTestGadget() {}
......
...@@ -71,7 +71,7 @@ class UsbTestGadgetImpl : public UsbTestGadget { ...@@ -71,7 +71,7 @@ class UsbTestGadgetImpl : public UsbTestGadget {
virtual bool Reconnect() OVERRIDE; virtual bool Reconnect() OVERRIDE;
virtual bool SetType(Type type) OVERRIDE; virtual bool SetType(Type type) OVERRIDE;
virtual UsbDevice* GetDevice() const OVERRIDE; virtual UsbDevice* GetDevice() const OVERRIDE;
virtual std::string GetSerial() const OVERRIDE; virtual std::string GetSerialNumber() const OVERRIDE;
protected: protected:
UsbTestGadgetImpl(); UsbTestGadgetImpl();
...@@ -166,7 +166,7 @@ UsbDevice* UsbTestGadgetImpl::GetDevice() const { ...@@ -166,7 +166,7 @@ UsbDevice* UsbTestGadgetImpl::GetDevice() const {
return device_.get(); return device_.get();
} }
std::string UsbTestGadgetImpl::GetSerial() const { std::string UsbTestGadgetImpl::GetSerialNumber() const {
return device_address_; return device_address_;
} }
...@@ -205,13 +205,8 @@ bool UsbTestGadgetImpl::FindUnclaimed() { ...@@ -205,13 +205,8 @@ bool UsbTestGadgetImpl::FindUnclaimed() {
devices.begin(); iter != devices.end(); ++iter) { devices.begin(); iter != devices.end(); ++iter) {
const scoped_refptr<UsbDevice> &device = *iter; const scoped_refptr<UsbDevice> &device = *iter;
if (device->vendor_id() == 0x18D1 && device->product_id() == 0x58F0) { if (device->vendor_id() == 0x18D1 && device->product_id() == 0x58F0) {
scoped_refptr<UsbDeviceHandle> handle = device->Open();
if (handle.get() == NULL) {
continue;
}
base::string16 serial_utf16; base::string16 serial_utf16;
if (!handle->GetSerial(&serial_utf16)) { if (!device->GetSerialNumber(&serial_utf16)) {
continue; continue;
} }
...@@ -340,7 +335,7 @@ bool UsbTestGadgetImpl::Update() { ...@@ -340,7 +335,7 @@ bool UsbTestGadgetImpl::Update() {
bool UsbTestGadgetImpl::FindClaimed() { bool UsbTestGadgetImpl::FindClaimed() {
CHECK(!device_.get()); CHECK(!device_.get());
std::string expected_serial = GetSerial(); std::string expected_serial = GetSerialNumber();
std::vector<scoped_refptr<UsbDevice> > devices; std::vector<scoped_refptr<UsbDevice> > devices;
usb_service_->GetDevices(&devices); usb_service_->GetDevices(&devices);
...@@ -362,13 +357,8 @@ bool UsbTestGadgetImpl::FindClaimed() { ...@@ -362,13 +357,8 @@ bool UsbTestGadgetImpl::FindClaimed() {
continue; continue;
} }
scoped_refptr<UsbDeviceHandle> handle(device->Open());
if (handle.get() == NULL) {
continue;
}
base::string16 serial_utf16; base::string16 serial_utf16;
if (!handle->GetSerial(&serial_utf16)) { if (!device->GetSerialNumber(&serial_utf16)) {
continue; continue;
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/observer_list.h" #include "base/observer_list.h"
#include "base/strings/string16.h"
namespace device { namespace device {
...@@ -54,6 +55,18 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> { ...@@ -54,6 +55,18 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> {
// Blocking method. Must be called on FILE thread. // Blocking method. Must be called on FILE thread.
virtual const UsbConfigDescriptor& GetConfiguration() = 0; 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 AddObserver(Observer* obs) { observer_list_.AddObserver(obs); }
void RemoveObserver(Observer* obs) { observer_list_.RemoveObserver(obs); } void RemoveObserver(Observer* obs) { observer_list_.RemoveObserver(obs); }
......
...@@ -30,6 +30,9 @@ class MockUsbDevice : public UsbDevice { ...@@ -30,6 +30,9 @@ class MockUsbDevice : public UsbDevice {
MOCK_METHOD2(RequestUsbAccess, void(int, const base::Callback<void(bool)>&)); MOCK_METHOD2(RequestUsbAccess, void(int, const base::Callback<void(bool)>&));
#endif #endif
MOCK_METHOD0(GetConfiguration, const UsbConfigDescriptor&()); MOCK_METHOD0(GetConfiguration, const UsbConfigDescriptor&());
MOCK_METHOD1(GetManufacturer, bool(base::string16*));
MOCK_METHOD1(GetProduct, bool(base::string16*));
MOCK_METHOD1(GetSerialNumber, bool(base::string16*));
private: private:
virtual ~MockUsbDevice() {} virtual ~MockUsbDevice() {}
......
...@@ -56,9 +56,10 @@ class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> { ...@@ -56,9 +56,10 @@ class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> {
virtual bool SetInterfaceAlternateSetting(int interface_number, virtual bool SetInterfaceAlternateSetting(int interface_number,
int alternate_setting) = 0; int alternate_setting) = 0;
virtual bool ResetDevice() = 0; virtual bool ResetDevice() = 0;
virtual bool GetManufacturer(base::string16* manufacturer) = 0;
virtual bool GetProduct(base::string16* product) = 0; // Gets the string descriptor with the given index from the device, or returns
virtual bool GetSerial(base::string16* serial) = 0; // 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. // Async IO. Can be called on any thread.
virtual void ControlTransfer(UsbEndpointDirection direction, virtual void ControlTransfer(UsbEndpointDirection direction,
......
...@@ -211,96 +211,6 @@ void UsbDeviceHandleImpl::Close() { ...@@ -211,96 +211,6 @@ void UsbDeviceHandleImpl::Close() {
device_->Close(this); 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) { bool UsbDeviceHandleImpl::ClaimInterface(const int interface_number) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (!device_) if (!device_)
...@@ -375,40 +285,6 @@ bool UsbDeviceHandleImpl::ResetDevice() { ...@@ -375,40 +285,6 @@ bool UsbDeviceHandleImpl::ResetDevice() {
return rv == LIBUSB_SUCCESS; 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, bool UsbDeviceHandleImpl::GetStringDescriptor(uint8 string_id,
base::string16* string) { base::string16* string) {
if (!GetSupportedLanguages()) { if (!GetSupportedLanguages()) {
...@@ -459,66 +335,6 @@ bool UsbDeviceHandleImpl::GetStringDescriptor(uint8 string_id, ...@@ -459,66 +335,6 @@ bool UsbDeviceHandleImpl::GetStringDescriptor(uint8 string_id,
return false; 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( void UsbDeviceHandleImpl::ControlTransfer(
const UsbEndpointDirection direction, const UsbEndpointDirection direction,
const TransferRequestType request_type, const TransferRequestType request_type,
...@@ -749,6 +565,130 @@ void UsbDeviceHandleImpl::SubmitTransfer( ...@@ -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() { void UsbDeviceHandleImpl::InternalClose() {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (!device_) if (!device_)
......
...@@ -40,9 +40,9 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle { ...@@ -40,9 +40,9 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
virtual bool SetInterfaceAlternateSetting(int interface_number, virtual bool SetInterfaceAlternateSetting(int interface_number,
int alternate_setting) OVERRIDE; int alternate_setting) OVERRIDE;
virtual bool ResetDevice() OVERRIDE; virtual bool ResetDevice() OVERRIDE;
virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE; virtual bool GetStringDescriptor(uint8 string_id,
virtual bool GetProduct(base::string16* product) OVERRIDE; base::string16* string) OVERRIDE;
virtual bool GetSerial(base::string16* serial) OVERRIDE;
virtual void ControlTransfer(UsbEndpointDirection direction, virtual void ControlTransfer(UsbEndpointDirection direction,
TransferRequestType request_type, TransferRequestType request_type,
TransferRecipient recipient, TransferRecipient recipient,
...@@ -132,7 +132,6 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle { ...@@ -132,7 +132,6 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
void CompleteTransfer(PlatformUsbTransferHandle transfer); void CompleteTransfer(PlatformUsbTransferHandle transfer);
bool GetSupportedLanguages(); bool GetSupportedLanguages();
bool GetStringDescriptor(uint8 string_id, base::string16* string);
// Informs the object to drop internal references. // Informs the object to drop internal references.
void InternalClose(); void InternalClose();
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "base/location.h" #include "base/location.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/stl_util.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 "base/thread_task_runner_handle.h"
#include "device/usb/usb_context.h" #include "device/usb/usb_context.h"
#include "device/usb/usb_descriptors.h" #include "device/usb/usb_descriptors.h"
...@@ -23,6 +25,10 @@ ...@@ -23,6 +25,10 @@
#include "chromeos/dbus/permission_broker_client.h" #include "chromeos/dbus/permission_broker_client.h"
#endif // defined(OS_CHROMEOS) #endif // defined(OS_CHROMEOS)
#if defined(OS_LINUX)
#include "device/udev_linux/udev.h"
#endif // defined(OS_LINUX)
namespace device { namespace device {
namespace { namespace {
...@@ -123,6 +129,44 @@ UsbDeviceImpl::UsbDeviceImpl( ...@@ -123,6 +129,44 @@ UsbDeviceImpl::UsbDeviceImpl(
ui_task_runner_(ui_task_runner) { ui_task_runner_(ui_task_runner) {
CHECK(platform_device) << "platform_device cannot be NULL"; CHECK(platform_device) << "platform_device cannot be NULL";
libusb_ref_device(platform_device); 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() { UsbDeviceImpl::~UsbDeviceImpl() {
...@@ -275,6 +319,100 @@ const UsbConfigDescriptor& UsbDeviceImpl::GetConfiguration() { ...@@ -275,6 +319,100 @@ const UsbConfigDescriptor& UsbDeviceImpl::GetConfiguration() {
return current_configuration_; 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() { void UsbDeviceImpl::OnDisconnect() {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
HandlesVector handles; HandlesVector handles;
......
...@@ -39,6 +39,9 @@ class UsbDeviceImpl : public UsbDevice { ...@@ -39,6 +39,9 @@ class UsbDeviceImpl : public UsbDevice {
virtual scoped_refptr<UsbDeviceHandle> Open() OVERRIDE; virtual scoped_refptr<UsbDeviceHandle> Open() OVERRIDE;
virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE; virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE;
virtual const UsbConfigDescriptor& GetConfiguration() 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: protected:
friend class UsbServiceImpl; friend class UsbServiceImpl;
...@@ -60,6 +63,15 @@ class UsbDeviceImpl : public UsbDevice { ...@@ -60,6 +63,15 @@ class UsbDeviceImpl : public UsbDevice {
base::ThreadChecker thread_checker_; base::ThreadChecker thread_checker_;
PlatformUsbDevice platform_device_; 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 // The active configuration descriptor is not read immediately but cached for
// later use. // later use.
bool current_configuration_cached_; bool current_configuration_cached_;
......
...@@ -29,23 +29,16 @@ TEST_F(UsbServiceTest, ClaimGadget) { ...@@ -29,23 +29,16 @@ TEST_F(UsbServiceTest, ClaimGadget) {
scoped_ptr<UsbTestGadget> gadget = UsbTestGadget::Claim(); scoped_ptr<UsbTestGadget> gadget = UsbTestGadget::Claim();
ASSERT_TRUE(gadget.get()); ASSERT_TRUE(gadget.get());
scoped_refptr<UsbDeviceHandle> handle = gadget->GetDevice()->Open(); scoped_refptr<UsbDevice> device = gadget->GetDevice();
base::string16 utf16; base::string16 utf16;
ASSERT_TRUE(handle->GetManufacturer(&utf16)); ASSERT_TRUE(device->GetManufacturer(&utf16));
ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(utf16));
// Check again to make sure string descriptor caching works.
ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(utf16)); ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(utf16));
ASSERT_TRUE(handle->GetProduct(&utf16)); ASSERT_TRUE(device->GetProduct(&utf16));
ASSERT_EQ("Test Gadget (default state)", base::UTF16ToUTF8(utf16));
// Check again to make sure string descriptor caching works.
ASSERT_EQ("Test Gadget (default state)", base::UTF16ToUTF8(utf16)); ASSERT_EQ("Test Gadget (default state)", base::UTF16ToUTF8(utf16));
ASSERT_TRUE(handle->GetSerial(&utf16)); ASSERT_TRUE(device->GetSerialNumber(&utf16));
ASSERT_EQ(gadget->GetSerial(), base::UTF16ToUTF8(utf16)); ASSERT_EQ(gadget->GetSerialNumber(), base::UTF16ToUTF8(utf16));
// Check again to make sure string descriptor caching works.
ASSERT_EQ(gadget->GetSerial(), base::UTF16ToUTF8(utf16));
} }
TEST_F(UsbServiceTest, DisconnectAndReconnect) { TEST_F(UsbServiceTest, DisconnectAndReconnect) {
......
...@@ -87,13 +87,11 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { ...@@ -87,13 +87,11 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
const UsbTransferCallback& callback)); const UsbTransferCallback& callback));
MOCK_METHOD0(ResetDevice, bool()); MOCK_METHOD0(ResetDevice, bool());
MOCK_METHOD2(GetStringDescriptor, bool(uint8_t, base::string16*));
MOCK_METHOD1(ClaimInterface, bool(const int interface_number)); MOCK_METHOD1(ClaimInterface, bool(const int interface_number));
MOCK_METHOD1(ReleaseInterface, bool(const int interface_number)); MOCK_METHOD1(ReleaseInterface, bool(const int interface_number));
MOCK_METHOD2(SetInterfaceAlternateSetting, MOCK_METHOD2(SetInterfaceAlternateSetting,
bool(const int interface_number, const int alternate_setting)); bool(const int interface_number, const int alternate_setting));
MOCK_METHOD1(GetManufacturer, bool(base::string16* manufacturer));
MOCK_METHOD1(GetProduct, bool(base::string16* product));
MOCK_METHOD1(GetSerial, bool(base::string16* serial));
virtual scoped_refptr<UsbDevice> GetDevice() const OVERRIDE { virtual scoped_refptr<UsbDevice> GetDevice() const OVERRIDE {
return device_; return device_;
...@@ -133,6 +131,9 @@ class MockUsbDevice : public UsbDevice { ...@@ -133,6 +131,9 @@ class MockUsbDevice : public UsbDevice {
#endif // OS_CHROMEOS #endif // OS_CHROMEOS
MOCK_METHOD0(GetConfiguration, const UsbConfigDescriptor&()); MOCK_METHOD0(GetConfiguration, const UsbConfigDescriptor&());
MOCK_METHOD1(GetManufacturer, bool(base::string16* manufacturer));
MOCK_METHOD1(GetProduct, bool(base::string16* product));
MOCK_METHOD1(GetSerialNumber, bool(base::string16* serial_number));
private: private:
MockUsbDeviceHandle* mock_handle_; MockUsbDeviceHandle* mock_handle_;
......
...@@ -33,7 +33,6 @@ namespace { ...@@ -33,7 +33,6 @@ namespace {
const char kErrorInitService[] = "Failed to initialize USB service."; const char kErrorInitService[] = "Failed to initialize USB service.";
const char kErrorNoDevice[] = "No such device."; const char kErrorNoDevice[] = "No such device.";
const char kErrorOpen[] = "Failed to open device.";
} // namespace } // namespace
...@@ -120,23 +119,17 @@ void UsbPrivateGetDeviceInfoFunction::AsyncWorkStart() { ...@@ -120,23 +119,17 @@ void UsbPrivateGetDeviceInfoFunction::AsyncWorkStart() {
device_info.product_name.reset(new std::string(name)); device_info.product_name.reset(new std::string(name));
} }
scoped_refptr<UsbDeviceHandle> device_handle = device->Open();
if (!device_handle.get()) {
CompleteWithError(kErrorOpen);
return;
}
base::string16 utf16; base::string16 utf16;
if (device_handle->GetManufacturer(&utf16)) { if (device->GetManufacturer(&utf16)) {
device_info.manufacturer_string.reset( device_info.manufacturer_string.reset(
new std::string(base::UTF16ToUTF8(utf16))); new std::string(base::UTF16ToUTF8(utf16)));
} }
if (device_handle->GetProduct(&utf16)) { if (device->GetProduct(&utf16)) {
device_info.product_string.reset(new std::string(base::UTF16ToUTF8(utf16))); device_info.product_string.reset(new std::string(base::UTF16ToUTF8(utf16)));
} }
if (device_handle->GetSerial(&utf16)) { if (device->GetSerialNumber(&utf16)) {
device_info.serial_string.reset(new std::string(base::UTF16ToUTF8(utf16))); device_info.serial_string.reset(new std::string(base::UTF16ToUTF8(utf16)));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment