lib/ligo/device.rb in ligo-0.1.0.beta vs lib/ligo/device.rb in ligo-0.1.0
- old
+ new
@@ -17,17 +17,53 @@
module Ligo
require 'ligo/constants'
+ # 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](https://github.com/larskanis/libusb), written by Lars Kanis and
+ # released under the LGPLv3.
+ # @author Renaud AUBIN
+ # @api public
class Device < LIBUSB::Device
include Logging
- # TODO: Document the attr!
- attr_reader :pDev, :pDevDesc
- attr_reader :aoap_version, :accessory, :in, :out, :handle
+ # @api private
+ attr_reader :pDev
+ # @api private
+ attr_reader :pDevDesc
+
+ # Returns the version of the AOA protocol that this device supports
+ # @return [Fixnum] the version of the AOA protocol that this device
+ # supports.
+ attr_reader :aoap_version
+
+ # Returns the associated {Accessory}
+ # @return [Accessory, nil] the associated accessory if any or nil.
+ attr_reader :accessory
+
+ # Returns the accessory mode input endpoint
+ # @return [LIBUSB::Endpoint, nil] the input endpoint or nil if the device is
+ # not in accessory mode.
+ attr_reader :in
+
+ # Returns the accessory mode output endpoint
+ # @return [LIBUSB::Endpoint, nil] the output endpoint or nil if the device
+ # is not in accessory mode.
+ attr_reader :out
+
+ # Returns the device handle
+ # @todo Improve the :handle doc
+ # @return [LIBUSB::DevHandle, nil] the device handle or nil.
+ attr_reader :handle
+
+ # @api private
def initialize context, pDev
@aoap_version = 0
@accessory, @in, @out, @handle = nil, nil, nil, nil
super context, pDev
end
@@ -46,25 +82,31 @@
# close
raise Interrupt, msg
end
end
+ # Opens an handle and claim the default interface for further operations
+ # @return [LIBUSB::DevHandle] the handle to operate on.
+ # @raise
def open_and_claim
@handle = open
@handle.claim_interface(0)
@handle.clear_halt(@in)
@handle
end
+ # Finalizes the device (release and close)
+ # @return
+ # @raise [LIBUSB::ERROR_TIMEOUT] in case of timeout.
def finalize
if @handle
@handle.release_interface(0)
@handle.close
end
end
- # Simple write method (blocking until timeout).
+ # Simple write method (blocking until timeout)
# @param [Fixnum] buffer_size
# The number of bytes expected to be received.
# @param [Fixnum] timeout
# The timeout in ms (default: 1000). 0 for an infinite timeout.
# @return [String] the received buffer (at most buffer_size bytes).
@@ -72,12 +114,13 @@
def read(buffer_size, timeout = 1000)
handle.bulk_transfer(endpoint: @in,
dataIn: buffer_size,
timeout: timeout)
end
+ alias_method :recv, :read
- # Simple write method (blocking until timeout).
+ # Simple write method (blocking until timeout)
# @param [String] buffer
# The buffer to be sent.
# @param [Fixnum] timeout
# The timeout in ms (default: 1000). 0 for an infinite timeout.
# @return [Fixnum] the number of bytes actually sent.
@@ -85,36 +128,13 @@
def write(buffer, timeout = 1000)
handle.bulk_transfer(endpoint: @out,
dataOut: buffer,
timeout: timeout)
end
+ alias_method :send, :write
- # Simple recv method.
- # @param [Fixnum] buffer_size
- # The buffer size of the received buffer.
- # @return [String] the received buffer (at most buffer_size bytes).
- def recv(buffer_size)
- begin
- handle.bulk_transfer(endpoint: @in,
- dataIn: buffer_size)
- rescue LIBUSB::ERROR_TIMEOUT
- nil
- # maybe we should implement a internal thread, a sleep and a retry
- end
- end
-
- # Simple send method.
- # @param [String] data
- # The data to be sent.
- # @return [Fixnum] the number of bytes sent.
- def send(data)
- # TODO: Add timeout param?
- handle.bulk_transfer(endpoint: @out, dataOut: data)
- end
-
- # Associate an AOAP compatible device with a virtual accessory and switch the Android device
- # to accessory mode.
+ # Associates with an accessory and switch to accessory mode
#
# Prepare an OAP compatible device to interact with a given {Ligo::Accessory}:
# * Switch the current assigned device to accessory mode
# * Set the I/O endpoints
# @param [Ligo::Accessory] accessory
@@ -151,12 +171,15 @@
end
end
true
end
+ # Switches to accessory mode
+ #
# Send identifying string information to the device and request the device start up in accessory
# mode.
+ # @return [true, false] true for success, false otherwise.
def start_accessory_mode
logger.debug 'start_accessory_mode'
sn = self.serial_number
self.open_interface(0) do |handle|
@@ -167,10 +190,12 @@
end
wait_and_retrieve_by_serial(sn)
end
+ # Sends a `set configuration` control transfer
+ #
# Set the device's configuration to a value of 1 with a SET_CONFIGURATION (0x09) device
# request.
# @return [true, false] true for success, false otherwise.
def set_configuration
logger.debug 'set_configuration'
@@ -191,41 +216,43 @@
wait_and_retrieve_by_serial(sn)
res == 0
end
end
- # Check if the current {Ligo::Device} is in accessory mode.
- # @return [true, false] true if the {Ligo::Device} is in accessory mode,
- # false otherwise.
+ # Check if the current {Device} is in accessory mode
+ # @return [true, false] true if the {Device} is in accessory mode, false
+ # otherwise.
def accessory_mode?
self.idVendor == GOOGLE_VID
end
- # Check if the current {Ligo::Device} supports AOAP.
+ # Check if the current {Device} supports AOAP
# @return [true, false] true if the {Ligo::Device} supports AOAP, false
# otherwise.
def aoap?
@aoap_version = self.get_protocol
logger.info "#{self.inspect} supports AOAP version #{@aoap_version}."
@aoap_version >= 1
end
- # Check if the current {Ligo::Device} is in UMS mode.
- # @return [true, false] true if the {Ligo::Device} is in UMS mode, false
- # otherwise.
+ # Check if the current {Device} is in UMS mode
+ # @return [true, false] true if the {Device} is in UMS mode, false otherwise
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
+ # Sends a `get protocol` control transfer
+ #
# Send a 51 control request ("Get Protocol") to figure out if the device
- # supports the Android accessory protocol.
+ # supports the Android accessory protocol. We assume here that the device
+ # has not been opened.
# @return [Fixnum] the AOAP protocol version supported by the device (0 for
# no AOAP support).
def get_protocol
logger.debug 'get_protocol'
res, version = 0, 0
@@ -241,11 +268,15 @@
end
(res.size == 2 && version >= 1 ) ? version : 0
end
- # Send identifying string information to the device.
+ # Sends identifying string information to the device
+ #
+ # We assume here that the device has already been opened.
+ # @api private
+ # @return
def send_accessory_id
logger.debug 'send_accessory_id'
req_type = LIBUSB::ENDPOINT_OUT | LIBUSB::REQUEST_TYPE_VENDOR
@accessory.each do |k,v|
# Ensure the string is terminated by a null char
@@ -258,21 +289,24 @@
logger.error "Failed to send #{k} string" unless r == s.size
end
end
private :send_accessory_id
- # Request the device start up in accessory mode
+ # Sends AOA protocol start command to the device
+ # @api private
+ # @return [Fixnum]
def send_start
logger.debug 'send_start'
req_type = LIBUSB::ENDPOINT_OUT | LIBUSB::REQUEST_TYPE_VENDOR
res = @handle.control_transfer(bmRequestType: req_type,
bRequest: COMMAND_START, wValue: 0x0,
wIndex: 0x0, dataOut: nil)
end
private :send_start
- # Internal use only.
+ # @api private
+ # @return [true, false] true for success, false otherwise.
def wait_and_retrieve_by_serial(sn)
sleep REENUMERATION_DELAY
# The device should now reappear on the usb bus with the Google vendor id.
# We retrieve it by using its serial number.
device = @context.devices(idVendor: GOOGLE_VID).collect do |d|
@@ -281,17 +315,19 @@
if device
# Retrieve new pointers (check if the old ones should be dereferenced)
@pDev = device.pDev
@pDevDesc = device.pDevDesc
+ true
else
logger.error ['Failed to retrieve the device after switching to ',
'accessory mode. This may be due to a lack of proper ',
'permissions ⇒ check your udev rules.', "\n",
'The Google vendor id rule may look like:', "\n",
'SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ',
'MODE="0666", GROUP="plugdev"'
].join
+ false
end
end
private :wait_and_retrieve_by_serial
end