Brian Silverman | 395d625 | 2013-09-13 20:58:14 -0700 | [diff] [blame^] | 1 | // Copyright 2012 Google Inc. All Rights Reserved. |
| 2 | |
| 3 | #include <stddef.h> |
| 4 | #include <glog/logging.h> |
| 5 | #include <boost/function.hpp> |
| 6 | #include <boost/scoped_ptr.hpp> |
| 7 | #include <libusb.h> |
| 8 | |
| 9 | #include "glibusb.h" |
| 10 | #include "glibusb_device_internal.h" |
| 11 | #include "glibusb_endpoint.h" |
| 12 | #include "glibusb_endpoint_internal.h" |
| 13 | #include "glibusb_internal.h" |
| 14 | |
| 15 | namespace glibusb { |
| 16 | |
| 17 | namespace { |
| 18 | VendorProductId GetVendorAndProductIdInternal( |
| 19 | struct libusb_device_handle *device_handle) { |
| 20 | CHECK_NOTNULL(device_handle); |
| 21 | libusb_device_descriptor desc; |
| 22 | libusb_device *dev = libusb_get_device(device_handle); |
| 23 | libusb_get_device_descriptor(dev, &desc); |
| 24 | return VendorProductId(desc.idVendor, desc.idProduct); |
| 25 | } |
| 26 | } // namespace |
| 27 | |
| 28 | PhysicalUsbDevice::PhysicalUsbDevice(struct libusb_context *context, |
| 29 | struct libusb_device_handle *handle) |
| 30 | : UsbDevice(GetVendorAndProductIdInternal(handle)), |
| 31 | libusb_context_(CHECK_NOTNULL(context)), |
| 32 | device_handle_(CHECK_NOTNULL(handle)) { |
| 33 | int r = libusb_claim_interface(device_handle_, 0); |
| 34 | // TODO(charliehotel): this must not be FATAL. |
| 35 | CHECK_GE(r, 0) << ": libusb_claim_interface failed, r=" << std::dec << r; |
| 36 | |
| 37 | struct libusb_device *dev = libusb_get_device(device_handle_); |
| 38 | // TODO(charliehotel): this must not be FATAL. |
| 39 | CHECK(dev != NULL) << ": libusb_get_device failed"; |
| 40 | |
| 41 | struct libusb_device_descriptor desc; |
| 42 | r = libusb_get_device_descriptor(dev, &desc); |
| 43 | // TODO(charliehotel): this must not be FATAL. |
| 44 | CHECK_GE(r, 0) << ": libusb_get_device_descriptor failed"; |
| 45 | |
| 46 | VLOG(2) << "vid=0x" << std::hex << desc.idVendor |
| 47 | << ", pid=0x" << desc.idProduct; |
| 48 | VLOG(2) << " # of configurations = " |
| 49 | << static_cast<int>(desc.bNumConfigurations); |
| 50 | |
| 51 | struct libusb_config_descriptor *config; |
| 52 | r = libusb_get_active_config_descriptor(dev, &config); |
| 53 | // TODO(charliehotel): this must not be FATAL. |
| 54 | CHECK_GE(r, 0) << ": libusb_get_active_config_descriptor failed"; |
| 55 | // TODO(charliehotel): this must not be FATAL. |
| 56 | CHECK_NOTNULL(config); |
| 57 | |
| 58 | if (config->bNumInterfaces != 1) { |
| 59 | VLOG(2) << "config->bNumInterfaces=" |
| 60 | << static_cast<int>(config->bNumInterfaces) |
| 61 | << ", expected ony one"; |
| 62 | } |
| 63 | |
| 64 | if (VLOG_IS_ON(2)) { |
| 65 | for (int i = 0; i < config->bNumInterfaces; ++i) { |
| 66 | const struct libusb_interface *interface = config->interface + i; |
| 67 | const struct libusb_interface_descriptor *setting = interface->altsetting; |
| 68 | |
| 69 | VLOG(2) << "bInterfaceNumber=" |
| 70 | << static_cast<int>(setting->bInterfaceNumber); |
| 71 | VLOG(2) << "bAlternateSetting=" |
| 72 | << static_cast<int>(setting->bAlternateSetting); |
| 73 | VLOG(2) << "bNumEndpoints=" |
| 74 | << static_cast<int>(setting->bNumEndpoints); |
| 75 | |
| 76 | for (int j = 0; j < setting->bNumEndpoints; ++j) { |
| 77 | const struct libusb_endpoint_descriptor *endpoint = |
| 78 | setting->endpoint + j; |
| 79 | switch (endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) { |
| 80 | case LIBUSB_TRANSFER_TYPE_CONTROL: |
| 81 | VLOG(2) << "control"; |
| 82 | break; |
| 83 | case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: |
| 84 | VLOG(2) << "iso"; |
| 85 | break; |
| 86 | case LIBUSB_TRANSFER_TYPE_BULK: |
| 87 | VLOG(2) << "bulk"; |
| 88 | break; |
| 89 | case LIBUSB_TRANSFER_TYPE_INTERRUPT: |
| 90 | VLOG(2) << "interrupt"; |
| 91 | break; |
| 92 | default: |
| 93 | LOG(FATAL) << "unknown transfer type"; |
| 94 | } |
| 95 | if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == |
| 96 | LIBUSB_ENDPOINT_IN) { |
| 97 | VLOG(2) << " ep: 0x" |
| 98 | << std::hex << static_cast<int>(endpoint->bEndpointAddress) |
| 99 | << " (in)"; |
| 100 | } else { |
| 101 | VLOG(2) << " ep: 0x" |
| 102 | << std::hex << static_cast<int>(endpoint->bEndpointAddress) |
| 103 | << " (out)"; |
| 104 | } |
| 105 | VLOG(2) << " packet size=" |
| 106 | << std::dec << static_cast<int>(endpoint->wMaxPacketSize); |
| 107 | VLOG(2) << " interval=" |
| 108 | << std::dec << static_cast<int>(endpoint->bInterval); |
| 109 | } |
| 110 | } |
| 111 | } |
| 112 | libusb_free_config_descriptor(config); |
| 113 | } |
| 114 | |
| 115 | PhysicalUsbDevice::~PhysicalUsbDevice() { |
| 116 | CHECK_NOTNULL(device_handle_); |
| 117 | libusb_close(device_handle_); |
| 118 | device_handle_ = nullptr; |
| 119 | } |
| 120 | |
| 121 | bool PhysicalUsbDevice::DoSetAlternateSetting(int setting) { |
| 122 | CHECK_NOTNULL(device_handle_); |
| 123 | int r = libusb_set_interface_alt_setting(device_handle_, 0, setting); |
| 124 | return r == 0; |
| 125 | } |
| 126 | |
| 127 | template <class UsbEndpointType> |
| 128 | UsbEndpointType *PhysicalUsbDevice::MatchEndpoint(EndpointMatcher matcher) { |
| 129 | CHECK_NOTNULL(device_handle_); |
| 130 | struct libusb_config_descriptor *config; |
| 131 | libusb_device *dev = libusb_get_device(device_handle_); |
| 132 | const int r = libusb_get_active_config_descriptor(dev, &config); |
| 133 | // TODO(charliehotel): this must not be FATAL. |
| 134 | CHECK_GE(r, 0) << ": libusb_get_active_config_descriptor failed"; |
| 135 | // TODO(charliehotel): this must not be FATAL. |
| 136 | CHECK_NOTNULL(config); |
| 137 | const struct libusb_interface *interface = config->interface; |
| 138 | const struct libusb_interface_descriptor *setting = interface->altsetting; |
| 139 | for (int j = 0; j < setting->bNumEndpoints; ++j) { |
| 140 | const struct libusb_endpoint_descriptor *descriptor = setting->endpoint + j; |
| 141 | if (matcher(descriptor)) { |
| 142 | UsbEndpointType *ans = new UsbEndpointType( |
| 143 | libusb_context_, device_handle_, descriptor); |
| 144 | libusb_free_config_descriptor(config); |
| 145 | return ans; |
| 146 | } |
| 147 | } |
| 148 | libusb_free_config_descriptor(config); |
| 149 | return NULL; |
| 150 | } |
| 151 | |
| 152 | namespace { |
| 153 | |
| 154 | struct DescriptorHasAddressAndDirection { |
| 155 | DescriptorHasAddressAndDirection(int address, UsbEndpoint::DirectionType direction) |
| 156 | : address_(address), direction_(direction) {} |
| 157 | |
| 158 | // Returns true if the descriptor provided has an address equal to the number |
| 159 | bool operator()(const struct libusb_endpoint_descriptor *descriptor) { |
| 160 | return (DescriptorToAddress(descriptor) == address_ && |
| 161 | DescriptorToDirection(descriptor) == direction_); |
| 162 | } |
| 163 | |
| 164 | int address_; |
| 165 | UsbEndpoint::DirectionType direction_; |
| 166 | }; |
| 167 | |
| 168 | // Returns true if the descriptor has a transfer type and direction equal to the |
| 169 | // provided type and direction. |
| 170 | struct DescriptorIsOfTypeAndDirection { |
| 171 | DescriptorIsOfTypeAndDirection(UsbEndpoint::TransferType transfer_type, |
| 172 | UsbEndpoint::DirectionType direction) |
| 173 | : transfer_type_(transfer_type), direction_(direction) {} |
| 174 | |
| 175 | bool operator()(const struct libusb_endpoint_descriptor *descriptor) { |
| 176 | return (DescriptorToTransfer(descriptor) == transfer_type_ && |
| 177 | DescriptorToDirection(descriptor) == direction_); |
| 178 | } |
| 179 | |
| 180 | UsbEndpoint::TransferType transfer_type_; |
| 181 | UsbEndpoint::DirectionType direction_; |
| 182 | }; |
| 183 | |
| 184 | } // namespace |
| 185 | |
| 186 | UsbInEndpoint *PhysicalUsbDevice::DoInEndpoint(int number) { |
| 187 | CHECK_EQ(number & LIBUSB_ENDPOINT_ADDRESS_MASK, number) |
| 188 | << ": Endpoint out of range."; |
| 189 | |
| 190 | DescriptorHasAddressAndDirection matcher(number, UsbEndpoint::kIn); |
| 191 | return MatchEndpoint<PhysicalUsbInEndpoint>(matcher); |
| 192 | } |
| 193 | |
| 194 | UsbOutEndpoint *PhysicalUsbDevice::DoOutEndpoint(int number) { |
| 195 | CHECK_EQ(number & LIBUSB_ENDPOINT_ADDRESS_MASK, number) |
| 196 | << ": Endpoint out of range."; |
| 197 | |
| 198 | DescriptorHasAddressAndDirection matcher(number, UsbEndpoint::kOut); |
| 199 | return MatchEndpoint<PhysicalUsbOutEndpoint>(matcher); |
| 200 | } |
| 201 | |
| 202 | UsbInEndpoint *PhysicalUsbDevice::DoFindInEndpoint( |
| 203 | UsbEndpoint::TransferType endpoint) { |
| 204 | |
| 205 | DescriptorIsOfTypeAndDirection matcher(endpoint, UsbEndpoint::kIn); |
| 206 | return MatchEndpoint<PhysicalUsbInEndpoint>(matcher); |
| 207 | } |
| 208 | |
| 209 | UsbOutEndpoint *PhysicalUsbDevice::DoFindOutEndpoint( |
| 210 | UsbEndpoint::TransferType endpoint) { |
| 211 | |
| 212 | DescriptorIsOfTypeAndDirection matcher(endpoint, UsbEndpoint::kOut); |
| 213 | return MatchEndpoint<PhysicalUsbOutEndpoint>(matcher); |
| 214 | } |
| 215 | |
| 216 | struct DeviceLocationAndId PhysicalUsbDevice::DoDeviceLocationAndId() { |
| 217 | CHECK_NOTNULL(device_handle_); |
| 218 | libusb_device *device = ::libusb_get_device(device_handle_); |
| 219 | CHECK_NOTNULL(device); |
| 220 | struct DeviceLocationAndId dlid; |
| 221 | dlid.location.bus_number = ::libusb_get_bus_number(device); |
| 222 | dlid.location.device_address = ::libusb_get_device_address(device); |
| 223 | dlid.id = vendor_product_id_; |
| 224 | return dlid; |
| 225 | } |
| 226 | |
| 227 | } // namespace glibusb |