Class: LIBUSB::DevHandle

Inherits:
Object
  • Object
show all
Defined in:
lib/libusb/dev_handle.rb,
lib/libusb/eventmachine.rb

Overview

Class representing a handle on a USB device.

A device handle is used to perform I/O and other operations. When finished with a device handle, you should call DevHandle#close .

Defined Under Namespace

Classes: EMTransfer

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(device, pHandle) ⇒ DevHandle

Returns a new instance of DevHandle.


29
30
31
32
33
# File 'lib/libusb/dev_handle.rb', line 29

def initialize device, pHandle
  @device = device
  @pHandle = pHandle
  @bulk_transfer = @control_transfer = @interrupt_transfer = nil
end

Instance Attribute Details

#deviceDevice (readonly)

Returns the device this handle belongs to.

Returns:

  • (Device)

    the device this handle belongs to.


27
28
29
# File 'lib/libusb/dev_handle.rb', line 27

def device
  @device
end

Instance Method Details

#alloc_streams(num_streams, endpoints) ⇒ Fixnum

Allocate up to num_streams usb bulk streams on the specified endpoints. This function takes an array of endpoints rather then a single endpoint because some protocols require that endpoints are setup with similar stream ids. All endpoints passed in must belong to the same interface.

Note this function may return less streams then requested. Also note that the same number of streams are allocated for each endpoint in the endpoint array.

Stream id 0 is reserved, and should not be used to communicate with devices. If #alloc_streams returns with a value of N, you may use stream ids 1 to N.

Available since libusb-1.0.19.

Parameters:

  • num_streams (Fixnum)

    number of streams to try to allocate

  • endpoints (Array<Fixnum>, Array<Endpoint>)

    array of endpoints to allocate streams on

Returns:

  • (Fixnum)

    number of streams allocated

Raises:

  • (NotImplementedError)

See Also:


218
219
220
221
222
223
# File 'lib/libusb/dev_handle.rb', line 218

def alloc_streams(num_streams, endpoints)
  pEndpoints = endpoints_as_ffi_bytes(endpoints)
  res = Call.libusb_alloc_streams(@pHandle, num_streams, pEndpoints, endpoints.length)
  LIBUSB.raise_error res, "in libusb_alloc_streams" unless res>=0
  res
end

#attach_kernel_driver(interface) ⇒ Object

Re-attach an interface's kernel driver, which was previously detached using #detach_kernel_driver.

Parameters:

  • interface (Interface, Fixnum)

    the interface to attach the driver to


280
281
282
283
284
# File 'lib/libusb/dev_handle.rb', line 280

def attach_kernel_driver(interface)
  interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
  res = Call.libusb_attach_kernel_driver(@pHandle, interface)
  LIBUSB.raise_error res, "in libusb_attach_kernel_driver" if res!=0
end

#auto_detach_kernel_driver=Object

Enable/disable libusb's automatic kernel driver detachment.

When this is enabled libusb will automatically detach the kernel driver on an interface when claiming the interface, and attach it when releasing the interface.

Automatic kernel driver detachment is disabled on newly opened device handles by default.

On platforms which do not have CAP_SUPPORTS_DETACH_KERNEL_DRIVER this function will return ERROR_NOT_SUPPORTED, and libusb will continue as if this function was never called.

Available since libusb-1.0.16.

Parameters:

  • enable (Boolean)

    whether to enable or disable auto kernel driver detachment

See Also:


308
309
310
311
# File 'lib/libusb/dev_handle.rb', line 308

def auto_detach_kernel_driver=(enable)
  res = Call.libusb_set_auto_detach_kernel_driver(@pHandle, enable ? 1 : 0)
  LIBUSB.raise_error res, "in libusb_set_auto_detach_kernel_driver" if res!=0
end

#bosBos

Get a Binary Object Store (BOS) descriptor.

This is a BLOCKING function, which will send requests to the device.

Since libusb version 1.0.16.

Returns:


325
326
327
328
329
330
331
# File 'lib/libusb/dev_handle.rb', line 325

def bos
  ctx = device.context.instance_variable_get(:@ctx)
  pp_desc = FFI::MemoryPointer.new :pointer
  res = Call.libusb_get_bos_descriptor(@pHandle, pp_desc)
  LIBUSB.raise_error res, "in libusb_get_bos_descriptor" if res!=0
  Bos.new(ctx, pp_desc.read_pointer)
end

#bulk_transfer(args = {}) {|result| ... } ⇒ Fixnum, ...

Perform a USB bulk transfer.

When called without a block, the transfer is done synchronously - so all events are handled internally and the sent/received data will be returned after completion or an exception will be raised.

When called with a block, the method returns immediately after submitting the transfer. You then have to ensure, that Context#handle_events is called properly. As soon as the transfer is completed, the block is called with the sent/received data in case of success or the exception instance in case of failure.

The direction of the transfer is inferred from the direction bits of the endpoint address.

For bulk reads, the :dataIn param indicates the maximum length of data you are expecting to receive. If less data arrives than expected, this function will return that data.

You should check the returned number of bytes for bulk writes. Not all of the data may have been written.

Also check Error#transferred when dealing with a timeout exception. libusb may have to split your transfer into a number of chunks to satisfy underlying O/S requirements, meaning that the timeout may expire after the first few chunks have completed. libusb is careful not to lose any data that may have been transferred; do not assume that timeout conditions indicate a complete lack of I/O.

Parameters:

  • args (Hash) (defaults to: {})

Options Hash (args):

  • :endpoint (Endpoint, Fixnum)

    the (address of a) valid endpoint to communicate with

  • :dataOut (String)

    the data to send with an outgoing transfer

  • :dataIn (Fixnum)

    the number of bytes expected to receive with an ingoing transfer

  • :timeout (Fixnum)

    timeout (in millseconds) that this function should wait before giving up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.

Yield Parameters:

  • result (String, Integer, LIBUSB::Error)

    result of the transfer is yielded to the block, when the asynchronous transfer has finished

Returns:

  • (Fixnum)

    Number of bytes sent for an outgoing transfer

  • (String)

    Received data for an ingoing transfer

  • (self)

    When called with a block

Raises:


375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/libusb/dev_handle.rb', line 375

def bulk_transfer(args={}, &block)
  timeout = args.delete(:timeout) || 1000
  endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
  endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
  if endpoint&ENDPOINT_IN != 0
    dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for bulk read")
  else
    dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for bulk write")
  end
  raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?

  # reuse transfer struct to speed up transfer
  @bulk_transfer ||= BulkTransfer.new dev_handle: self, allow_device_memory: true
  tr = @bulk_transfer
  tr.endpoint = endpoint
  tr.timeout = timeout
  if dataOut
    tr.buffer = dataOut
  else
    tr.alloc_buffer(dataIn)
  end

  submit_transfer(tr, dataIn, 0, &block)
end

#claim_interface(interface) ⇒ Object

Claim an interface on a given device handle.

You must claim the interface you wish to use before you can perform I/O on any of its endpoints.

It is legal to attempt to claim an already-claimed interface, in which case libusb just returns without doing anything.

Claiming of interfaces is a purely logical operation; it does not cause any requests to be sent over the bus. Interface claiming is used to instruct the underlying operating system that your application wishes to take ownership of the interface.

This is a non-blocking function.

If called with a block, the device handle is passed through to the block and the interface is released when the block has finished.

Parameters:

  • interface (Interface, Fixnum)

    the interface or it's bInterfaceNumber you wish to claim


76
77
78
79
80
81
82
83
84
85
86
# File 'lib/libusb/dev_handle.rb', line 76

def claim_interface(interface)
  interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
  res = Call.libusb_claim_interface(@pHandle, interface)
  LIBUSB.raise_error res, "in libusb_claim_interface" if res!=0
  return self unless block_given?
  begin
    yield self
  ensure
    release_interface(interface)
  end
end

#clear_halt(endpoint) ⇒ Object

Clear the halt/stall condition for an endpoint.

Endpoints with halt status are unable to receive or transmit data until the halt condition is stalled.

You should cancel all pending transfers before attempting to clear the halt condition.

This is a blocking function.

Parameters:

  • endpoint (Endpoint, Fixnum)

    the endpoint in question or it's bEndpointAddress


172
173
174
175
176
# File 'lib/libusb/dev_handle.rb', line 172

def clear_halt(endpoint)
  endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
  res = Call.libusb_clear_halt(@pHandle, endpoint)
  LIBUSB.raise_error res, "in libusb_clear_halt" if res!=0
end

#closeObject

Close a device handle.

Should be called on all open handles before your application exits.

Internally, this function destroys the reference that was added by LIBUSB::Device#open on the given device.

This is a non-blocking function; no requests are sent over the bus.


43
44
45
46
47
48
# File 'lib/libusb/dev_handle.rb', line 43

def close
  @bulk_transfer.free_buffer if @bulk_transfer
  @interrupt_transfer.free_buffer if @interrupt_transfer
  @control_transfer.free_buffer if @control_transfer
  Call.libusb_close(@pHandle)
end

#control_transfer(args = {}) {|result| ... } ⇒ Fixnum, ...

Perform a USB control transfer.

When called without a block, the transfer is done synchronously - so all events are handled internally and the sent/received data will be returned after completion or an exception will be raised.

When called with a block, the method returns immediately after submitting the transfer. You then have to ensure, that Context#handle_events is called properly. As soon as the transfer is completed, the block is called with the sent/received data in case of success or the exception instance in case of failure.

The direction of the transfer is inferred from the :bmRequestType field of the setup packet.

Parameters:

  • args (Hash) (defaults to: {})

Options Hash (args):

  • :bmRequestType (Fixnum)

    the request type field for the setup packet

  • :bRequest (Fixnum)

    the request field for the setup packet

  • :wValue (Fixnum)

    the value field for the setup packet

  • :wIndex (Fixnum)

    the index field for the setup packet

  • :dataOut (String)

    the data to send with an outgoing transfer, it is appended to the setup packet

  • :dataIn (Fixnum)

    the number of bytes expected to receive with an ingoing transfer (excluding setup packet)

  • :timeout (Fixnum)

    timeout (in millseconds) that this function should wait before giving up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.

Yield Parameters:

  • result (String, Integer, LIBUSB::Error)

    result of the transfer is yielded to the block, when the asynchronous transfer has finished

Returns:

  • (Fixnum)

    Number of bytes sent (excluding setup packet) for outgoing transfer

  • (String)

    Received data (without setup packet) for ingoing transfer

  • (self)

    When called with a block

Raises:


500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
# File 'lib/libusb/dev_handle.rb', line 500

def control_transfer(args={}, &block)
  bmRequestType = args.delete(:bmRequestType) || raise(ArgumentError, "param :bmRequestType not given")
  bRequest = args.delete(:bRequest) || raise(ArgumentError, "param :bRequest not given")
  wValue = args.delete(:wValue) || raise(ArgumentError, "param :wValue not given")
  wIndex = args.delete(:wIndex) || raise(ArgumentError, "param :wIndex not given")
  timeout = args.delete(:timeout) || 1000
  if bmRequestType&ENDPOINT_IN != 0
    dataIn = args.delete(:dataIn) || 0
    dataOut = ''
  else
    dataOut = args.delete(:dataOut) || ''
  end
  raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?

  # reuse transfer struct to speed up transfer
  @control_transfer ||= ControlTransfer.new dev_handle: self, allow_device_memory: true
  tr = @control_transfer
  tr.timeout = timeout
  if dataIn
    setup_data = [bmRequestType, bRequest, wValue, wIndex, dataIn].pack('CCvvv')
    tr.alloc_buffer( dataIn + CONTROL_SETUP_SIZE, setup_data )
  else
    tr.buffer = [bmRequestType, bRequest, wValue, wIndex, dataOut.bytesize, dataOut].pack('CCvvva*')
  end

  submit_transfer(tr, dataIn, CONTROL_SETUP_SIZE, &block)
end

#detach_kernel_driver(interface) ⇒ Object

Detach a kernel driver from an interface.

If successful, you will then be able to claim the interface and perform I/O.

Parameters:

  • interface (Interface, Fixnum)

    the interface to detach the driver from or it's bInterfaceNumber


270
271
272
273
274
# File 'lib/libusb/dev_handle.rb', line 270

def detach_kernel_driver(interface)
  interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
  res = Call.libusb_detach_kernel_driver(@pHandle, interface)
  LIBUSB.raise_error res, "in libusb_detach_kernel_driver" if res!=0
end

#eventmachine_bulk_transfer(opts = {}) ⇒ Object

Execute an eventmachine driven USB bulk transfer.

Examples:

tr = devh.eventmachine_bulk_transfer( endpoint: 0x02, dataOut: "data" )
tr.callback do |data|
  puts "sent: #{data.inspect}"
end
tr.errback do |ex|
  puts "send-err: #{ex}"
end

See Also:


154
155
156
# File 'lib/libusb/eventmachine.rb', line 154

def eventmachine_bulk_transfer(opts={})
  eventmachine_transfer(opts, :bulk_transfer)
end

#eventmachine_control_transfer(opts = {}) ⇒ Object

Execute an eventmachine driven USB control transfer.

Examples:

tr = devh.eventmachine_control_transfer(
  bmRequestType: ENDPOINT_IN|REQUEST_TYPE_CLASS|RECIPIENT_INTERFACE,
  bRequest: 0x01,
  wValue: 0, wIndex: 0, dataIn: 1 )
tr.callback do |data|
  puts "recved: #{data.inspect}"
end
tr.errback do |ex|
  puts "recv-err: #{ex}"
end

See Also:


174
175
176
# File 'lib/libusb/eventmachine.rb', line 174

def eventmachine_control_transfer(opts={})
  eventmachine_transfer(opts, :control_transfer)
end

#eventmachine_interrupt_transfer(opts = {}) ⇒ Object

Execute an eventmachine driven USB interrupt transfer.


137
138
139
# File 'lib/libusb/eventmachine.rb', line 137

def eventmachine_interrupt_transfer(opts={})
  eventmachine_transfer(opts, :interrupt_transfer)
end

#free_streamsObject

Free usb bulk streams allocated with #alloc_streams

Note streams are automatically free-ed when releasing an interface.

Available since libusb-1.0.19.

Parameters:

  • endpoints (Array<Fixnum>, Array<Endpoint>)

    array of endpoints to free streams on

See Also:


235
236
237
238
239
240
# File 'lib/libusb/dev_handle.rb', line 235

def free_streams(endpoints)
  pEndpoints = endpoints_as_ffi_bytes(endpoints)
  res = Call.libusb_free_streams(@pHandle, pEndpoints, endpoints.length)
  LIBUSB.raise_error res, "in libusb_free_streams" unless res>=0
  nil
end

#interrupt_transfer(args = {}) {|result| ... } ⇒ Fixnum, ...

Perform a USB interrupt transfer.

When called without a block, the transfer is done synchronously - so all events are handled internally and the sent/received data will be returned after completion or an exception will be raised.

When called with a block, the method returns immediately after submitting the transfer. You then have to ensure, that Context#handle_events is called properly. As soon as the transfer is completed, the block is called with the sent/received data in case of success or the exception instance in case of failure.

The direction of the transfer is inferred from the direction bits of the endpoint address.

For interrupt reads, the :dataIn param indicates the maximum length of data you are expecting to receive. If less data arrives than expected, this function will return that data.

You should check the returned number of bytes for interrupt writes. Not all of the data may have been written.

Also check Error#transferred when dealing with a timeout exception. libusb may have to split your transfer into a number of chunks to satisfy underlying O/S requirements, meaning that the timeout may expire after the first few chunks have completed. libusb is careful not to lose any data that may have been transferred; do not assume that timeout conditions indicate a complete lack of I/O.

The default endpoint bInterval value is used as the polling interval.

Parameters:

  • args (Hash) (defaults to: {})

Options Hash (args):

  • :endpoint (Endpoint, Fixnum)

    the (address of a) valid endpoint to communicate with

  • :dataOut (String)

    the data to send with an outgoing transfer

  • :dataIn (Fixnum)

    the number of bytes expected to receive with an ingoing transfer

  • :timeout (Fixnum)

    timeout (in millseconds) that this function should wait before giving up due to no response being received. For an unlimited timeout, use value 0. Defaults to 1000 ms.

Yield Parameters:

  • result (String, Integer, LIBUSB::Error)

    result of the transfer is yielded to the block, when the asynchronous transfer has finished

Returns:

  • (Fixnum)

    Number of bytes sent for an outgoing transfer

  • (String)

    Received data for an ingoing transfer

  • (self)

    When called with a block

Raises:


443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/libusb/dev_handle.rb', line 443

def interrupt_transfer(args={}, &block)
  timeout = args.delete(:timeout) || 1000
  endpoint = args.delete(:endpoint) || raise(ArgumentError, "no endpoint given")
  endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
  if endpoint&ENDPOINT_IN != 0
    dataIn = args.delete(:dataIn) || raise(ArgumentError, "no :dataIn given for interrupt read")
  else
    dataOut = args.delete(:dataOut) || raise(ArgumentError, "no :dataOut given for interrupt write")
  end
  raise ArgumentError, "invalid params #{args.inspect}" unless args.empty?

  # reuse transfer struct to speed up transfer
  @interrupt_transfer ||= InterruptTransfer.new dev_handle: self, allow_device_memory: true
  tr = @interrupt_transfer
  tr.endpoint = endpoint
  tr.timeout = timeout
  if dataOut
    tr.buffer = dataOut
  else
    tr.alloc_buffer(dataIn)
  end

  submit_transfer(tr, dataIn, 0, &block)
end

#kernel_driver_active?(interface) ⇒ Boolean

Determine if a kernel driver is active on an interface.

If a kernel driver is active, you cannot claim the interface, and libusb will be unable to perform I/O.

Parameters:

  • interface (Interface, Fixnum)

    the interface to check or it's bInterfaceNumber

Returns:

  • (Boolean)

257
258
259
260
261
262
# File 'lib/libusb/dev_handle.rb', line 257

def kernel_driver_active?(interface)
  interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
  res = Call.libusb_kernel_driver_active(@pHandle, interface)
  LIBUSB.raise_error res, "in libusb_kernel_driver_active" unless res>=0
  return res==1
end

#release_interface(interface) ⇒ Object

Release an interface previously claimed with #claim_interface.

You should release all claimed interfaces before closing a device handle.

This is a blocking function. A SET_INTERFACE control request will be sent to the device, resetting interface state to the first alternate setting.

Parameters:

  • interface (Interface, Fixnum)

    the interface or it's bInterfaceNumber you claimed previously


97
98
99
100
101
# File 'lib/libusb/dev_handle.rb', line 97

def release_interface(interface)
  interface = interface.bInterfaceNumber if interface.respond_to? :bInterfaceNumber
  res = Call.libusb_release_interface(@pHandle, interface)
  LIBUSB.raise_error res, "in libusb_release_interface" if res!=0
end

#reset_deviceObject

Perform a USB port reset to reinitialize a device.

The system will attempt to restore the previous configuration and alternate settings after the reset has completed.

If the reset fails, the descriptors change, or the previous state cannot be restored, the device will appear to be disconnected and reconnected. This means that the device handle is no longer valid (you should close it) and rediscover the device. A Exception of LIBUSB::ERROR_NOT_FOUND indicates when this is the case.

This is a blocking function which usually incurs a noticeable delay.


190
191
192
193
# File 'lib/libusb/dev_handle.rb', line 190

def reset_device
  res = Call.libusb_reset_device(@pHandle)
  LIBUSB.raise_error res, "in libusb_reset_device" if res!=0
end

#set_configuration(configuration) ⇒ Object Also known as: configuration=

Set the active configuration for a device.

The operating system may or may not have already set an active configuration on the device. It is up to your application to ensure the correct configuration is selected before you attempt to claim interfaces and perform other operations.

If you call this function on a device already configured with the selected configuration, then this function will act as a lightweight device reset: it will issue a SET_CONFIGURATION request using the current configuration, causing most USB-related device state to be reset (altsetting reset to zero, endpoint halts cleared, toggles reset).

You cannot change/reset configuration if your application has claimed interfaces - you should free them with #release_interface first. You cannot change/reset configuration if other applications or drivers have claimed interfaces.

A configuration value of nil will put the device in unconfigured state. The USB specifications state that a configuration value of 0 does this, however buggy devices exist which actually have a configuration 0.

You should always use this function rather than formulating your own SET_CONFIGURATION control request. This is because the underlying operating system needs to know when such changes happen.

This is a blocking function.

Parameters:

  • configuration (Configuration, Fixnum)

    the configuration or it's bConfigurationValue you wish to activate, or nil if you wish to put the device in unconfigured state


133
134
135
136
137
# File 'lib/libusb/dev_handle.rb', line 133

def set_configuration(configuration)
  configuration = configuration.bConfigurationValue if configuration.respond_to? :bConfigurationValue
  res = Call.libusb_set_configuration(@pHandle, configuration || -1)
  LIBUSB.raise_error res, "in libusb_set_configuration" if res!=0
end

#set_interface_alt_setting(setting_or_interface_number, alternate_setting = nil) ⇒ Object

Activate an alternate setting for an interface.

The interface must have been previously claimed with #claim_interface.

You should always use this function rather than formulating your own SET_INTERFACE control request. This is because the underlying operating system needs to know when such changes happen.

This is a blocking function.

Parameters:

  • setting_or_interface_number (Setting, Fixnum)

    the alternate setting to activate or the bInterfaceNumber of the previously-claimed interface

  • alternate_setting (Fixnum, nil) (defaults to: nil)

    the bAlternateSetting of the alternate setting to activate (only if first param is a Fixnum)


154
155
156
157
158
159
# File 'lib/libusb/dev_handle.rb', line 154

def set_interface_alt_setting(setting_or_interface_number, alternate_setting=nil)
  alternate_setting ||= setting_or_interface_number.bAlternateSetting if setting_or_interface_number.respond_to? :bAlternateSetting
  setting_or_interface_number = setting_or_interface_number.bInterfaceNumber if setting_or_interface_number.respond_to? :bInterfaceNumber
  res = Call.libusb_set_interface_alt_setting(@pHandle, setting_or_interface_number, alternate_setting)
  LIBUSB.raise_error res, "in libusb_set_interface_alt_setting" if res!=0
end

#string_descriptor_ascii(index) ⇒ Object


50
51
52
53
54
55
# File 'lib/libusb/dev_handle.rb', line 50

def string_descriptor_ascii(index)
  pString = FFI::MemoryPointer.new 0x100
  res = Call.libusb_get_string_descriptor_ascii(@pHandle, index, pString, pString.size)
  LIBUSB.raise_error res, "in libusb_get_string_descriptor_ascii" unless res>=0
  pString.read_string(res)
end