Class: Ligo::Device
- Inherits:
-
LIBUSB::Device
- Object
- LIBUSB::Device
- Ligo::Device
- Includes:
- Logging
- Defined in:
- lib/ligo/device.rb
Overview
This class provides a convenient wrapper class around LIBUSB::Device and
implements the Android Open Accessory Protocol to interact with compatible
devices.
This class is a derivative work of LIBUSB::Device as included in
LIBUSB, written by Lars Kanis and
released under the LGPLv3.
Instance Attribute Summary collapse
-
#accessory ⇒ Accessory?
readonly
Returns the associated Accessory.
-
#aoap_version ⇒ Fixnum
readonly
Returns the version of the AOA protocol that this device supports.
-
#handle ⇒ LIBUSB::DevHandle?
readonly
Returns the device handle.
-
#in ⇒ LIBUSB::Endpoint?
readonly
Returns the accessory mode input endpoint.
-
#out ⇒ LIBUSB::Endpoint?
readonly
Returns the accessory mode output endpoint.
- #pDev ⇒ Object readonly private
- #pDevDesc ⇒ Object readonly private
Instance Method Summary collapse
-
#accessory_mode? ⇒ true, false
Check if the current Device is in accessory mode.
-
#aoap? ⇒ true, false
Check if the current Device supports AOAP.
-
#attach_accessory(accessory) ⇒ true, false
Associates with an accessory and switch to accessory mode.
-
#finalize ⇒ Object
Finalizes the device (release and close).
-
#get_device(sn) ⇒ LIBUSB::Device
private
Retrieves an AOAP device by its serial number.
-
#get_protocol ⇒ Fixnum
Sends a
get protocolcontrol transfer. -
#initialize(context, pDev) ⇒ Device
constructor
private
A new instance of Device.
-
#open_and_claim ⇒ LIBUSB::DevHandle
Opens an handle and claim the default interface for further operations.
- #process(&block) ⇒ Object
-
#read(buffer_size, timeout = 1000) ⇒ String
(also: #recv)
Simple write method (blocking until timeout).
-
#set_configuration ⇒ true, false
Sends a
set configurationcontrol transfer. -
#start_accessory_mode ⇒ true, false
Switches to accessory mode.
-
#uas? ⇒ true, false
Check if the current Device is in UMS mode.
-
#write(buffer, timeout = 1000) ⇒ Fixnum
(also: #send)
Simple write method (blocking until timeout).
Methods included from Logging
configure_logger_for, configure_logger_output, #logger, logger_for
Constructor Details
#initialize(context, pDev) ⇒ Device
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of Device.
65 66 67 68 69 |
# File 'lib/ligo/device.rb', line 65 def initialize context, pDev @aoap_version = 0 @accessory, @in, @out, @handle = nil, nil, nil, nil super context, pDev end |
Instance Attribute Details
#accessory ⇒ Accessory? (readonly)
Returns the associated Accessory
47 48 49 |
# File 'lib/ligo/device.rb', line 47 def accessory @accessory end |
#aoap_version ⇒ Fixnum (readonly)
Returns the version of the AOA protocol that this device supports
43 44 45 |
# File 'lib/ligo/device.rb', line 43 def aoap_version @aoap_version end |
#handle ⇒ LIBUSB::DevHandle? (readonly)
Improve the :handle doc
Returns the device handle
62 63 64 |
# File 'lib/ligo/device.rb', line 62 def handle @handle end |
#in ⇒ LIBUSB::Endpoint? (readonly)
Returns the accessory mode input endpoint
52 53 54 |
# File 'lib/ligo/device.rb', line 52 def in @in end |
#out ⇒ LIBUSB::Endpoint? (readonly)
Returns the accessory mode output endpoint
57 58 59 |
# File 'lib/ligo/device.rb', line 57 def out @out end |
#pDev ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
35 36 37 |
# File 'lib/ligo/device.rb', line 35 def pDev @pDev end |
#pDevDesc ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
38 39 40 |
# File 'lib/ligo/device.rb', line 38 def pDevDesc @pDevDesc end |
Instance Method Details
#accessory_mode? ⇒ true, false
Check if the current Ligo::Device is in accessory mode
222 223 224 |
# File 'lib/ligo/device.rb', line 222 def accessory_mode? (self.idVendor == GOOGLE_VID) && (GOOGLE_PIDS.include? self.idProduct) end |
#aoap? ⇒ true, false
Check if the current Ligo::Device supports AOAP
229 230 231 232 233 234 235 236 237 238 |
# File 'lib/ligo/device.rb', line 229 def aoap? @aoap_version = self.get_protocol aoap_supported = (@aoap_version >= 1) if aoap_supported logger.info "#{self.inspect} supports AOA Protocol version #{@aoap_version}." else logger.info "#{self.inspect} doesn't support AOA Protocol." end aoap_supported end |
#attach_accessory(accessory) ⇒ true, false
Associates with an accessory and switch to accessory mode
Prepare an OAP compatible device to interact with a given Accessory:
- Switch the current assigned device to accessory mode
- Set the I/O endpoints
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/ligo/device.rb', line 143 def attach_accessory(accessory) logger.debug "attach_accessory(#{accessory})" @accessory = accessory if accessory_mode? # if the device is already in accessory mode, we send # set_configuration to force an usb attached event on the device begin set_configuration rescue LIBUSB::ERROR_NO_DEVICE logger.debug ' set_configuration raises LIBUSB::ERROR_NO_DEVICE - Retry' sleep REENUMERATION_DELAY # Set configuration may fail retry end else # the device is not in accessory mode, start_accessory_mode is # sufficient to get an usb attached event on the device return false unless start_accessory_mode end # Find out the in/out endpoints self.interfaces.first.endpoints.each do |ep| if ep.bEndpointAddress & 0b10000000 == 0 @out = ep if @out.nil? else @in = ep if @in.nil? end end true end |
#finalize ⇒ Object
Finalizes the device (release and close)
100 101 102 103 104 105 |
# File 'lib/ligo/device.rb', line 100 def finalize if @handle @handle.release_interface(0) @handle.close end end |
#get_device(sn) ⇒ LIBUSB::Device
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Retrieves an AOAP device by its serial number
316 317 318 319 320 |
# File 'lib/ligo/device.rb', line 316 def get_device(sn) device = @context.devices(idVendor: GOOGLE_VID).collect do |d| d.serial_number == sn ? d : nil end.compact.first end |
#get_protocol ⇒ Fixnum
Sends a get protocol control transfer
Send a 51 control request ("Get Protocol") to figure out if the device supports the Android accessory protocol. We assume here that the device has not been opened.
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/ligo/device.rb', line 259 def get_protocol logger.debug 'get_protocol' res, version = 0, 0 self.open do |h| h.detach_kernel_driver(0) if self.uas? && h.kernel_driver_active?(0) req_type = LIBUSB::ENDPOINT_IN | LIBUSB::REQUEST_TYPE_VENDOR res = h.control_transfer(bmRequestType: req_type, bRequest: COMMAND_GETPROTOCOL, wValue: 0x0, wIndex: 0x0, dataIn: 2) version = res.unpack('S')[0] end (res.size == 2 && version >= 1 ) ? version : 0 rescue LIBUSB::ERROR_NOT_SUPPORTED, LIBUSB::ERROR_PIPE 0 end |
#open_and_claim ⇒ LIBUSB::DevHandle
Opens an handle and claim the default interface for further operations
90 91 92 93 94 95 |
# File 'lib/ligo/device.rb', line 90 def open_and_claim @handle = open @handle.claim_interface(0) @handle.clear_halt(@in) @handle end |
#process(&block) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/ligo/device.rb', line 71 def process(&block) begin self.open_interface(0) do |handle| @handle = handle yield handle @handle = nil end # close rescue LIBUSB::ERROR_NO_DEVICE msg = 'The target device has been disconnected' logger.debug msg # close raise Interrupt, msg end end |
#read(buffer_size, timeout = 1000) ⇒ String Also known as: recv
Simple write method (blocking until timeout)
114 115 116 117 118 |
# File 'lib/ligo/device.rb', line 114 def read(buffer_size, timeout = 1000) handle.bulk_transfer(endpoint: @in, dataIn: buffer_size, timeout: timeout) end |
#set_configuration ⇒ true, false
Sends a set configuration control transfer
Set the device's configuration to a value of 1 with a SET_CONFIGURATION (0x09) device request.
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/ligo/device.rb', line 200 def set_configuration logger.debug 'set_configuration' res = nil sn = self.serial_number device = get_device(sn) begin device.open_interface(0) do |handle| req_type = LIBUSB::ENDPOINT_OUT | LIBUSB::REQUEST_TYPE_STANDARD res = handle.control_transfer(bmRequestType: req_type, bRequest: LIBUSB::REQUEST_SET_CONFIGURATION, wValue: 1, wIndex: 0x0, dataOut: nil) end wait_and_retrieve_by_serial(sn) res == 0 end end |
#start_accessory_mode ⇒ true, false
Switches to accessory mode
Send identifying string information to the device and request the device start up in accessory mode.
181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/ligo/device.rb', line 181 def start_accessory_mode logger.debug 'start_accessory_mode' sn = self.serial_number self.open do |handle| @handle = handle send_accessory_id send_start @handle = nil end wait_and_retrieve_by_serial(sn) end |
#uas? ⇒ true, false
Check if the current Ligo::Device is in UMS mode
242 243 244 245 246 247 248 249 250 |
# File 'lib/ligo/device.rb', line 242 def uas? if RUBY_PLATFORM=~/linux/i # http://cateee.net/lkddb/web-lkddb/USB_UAS.html (self.settings[0].bInterfaceClass == 0x08) && (self.settings[0].bInterfaceSubClass == 0x06) else false end end |
#write(buffer, timeout = 1000) ⇒ Fixnum Also known as: send
Simple write method (blocking until timeout)
128 129 130 131 132 |
# File 'lib/ligo/device.rb', line 128 def write(buffer, timeout = 1000) handle.bulk_transfer(endpoint: @out, dataOut: buffer, timeout: timeout) end |