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 protocol
control 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 configuration
control 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.
76 77 78 79 80 |
# File 'lib/ligo/device.rb', line 76 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
58 59 60 |
# File 'lib/ligo/device.rb', line 58 def accessory @accessory end |
#aoap_version ⇒ Fixnum (readonly)
Returns the version of the AOA protocol that this device supports
54 55 56 |
# File 'lib/ligo/device.rb', line 54 def aoap_version @aoap_version end |
#handle ⇒ LIBUSB::DevHandle? (readonly)
Improve the :handle doc
Returns the device handle
73 74 75 |
# File 'lib/ligo/device.rb', line 73 def handle @handle end |
#in ⇒ LIBUSB::Endpoint? (readonly)
Returns the accessory mode input endpoint
63 64 65 |
# File 'lib/ligo/device.rb', line 63 def in @in end |
#out ⇒ LIBUSB::Endpoint? (readonly)
Returns the accessory mode output endpoint
68 69 70 |
# File 'lib/ligo/device.rb', line 68 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.
46 47 48 |
# File 'lib/ligo/device.rb', line 46 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.
49 50 51 |
# File 'lib/ligo/device.rb', line 49 def pDevDesc @pDevDesc end |
Instance Method Details
#accessory_mode? ⇒ true, false
Check if the current Ligo::Device is in accessory mode
233 234 235 |
# File 'lib/ligo/device.rb', line 233 def accessory_mode? (self.idVendor == GOOGLE_VID) && (GOOGLE_PIDS.include? self.idProduct) end |
#aoap? ⇒ true, false
Check if the current Ligo::Device supports AOAP
240 241 242 243 244 245 246 247 248 249 |
# File 'lib/ligo/device.rb', line 240 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
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/ligo/device.rb', line 154 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 Ligo::getDelay # 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)
111 112 113 114 115 116 |
# File 'lib/ligo/device.rb', line 111 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
327 328 329 330 331 |
# File 'lib/ligo/device.rb', line 327 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.
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/ligo/device.rb', line 270 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
101 102 103 104 105 106 |
# File 'lib/ligo/device.rb', line 101 def open_and_claim @handle = open @handle.claim_interface(0) @handle.clear_halt(@in) @handle end |
#process(&block) ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/ligo/device.rb', line 82 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)
125 126 127 128 129 |
# File 'lib/ligo/device.rb', line 125 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.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/ligo/device.rb', line 211 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.
192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/ligo/device.rb', line 192 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
253 254 255 256 257 258 259 260 261 |
# File 'lib/ligo/device.rb', line 253 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)
139 140 141 142 143 |
# File 'lib/ligo/device.rb', line 139 def write(buffer, timeout = 1000) handle.bulk_transfer(endpoint: @out, dataOut: buffer, timeout: timeout) end |