Class: LIBUSB::DevHandle
- Inherits:
-
Object
- Object
- LIBUSB::DevHandle
- 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
-
#device ⇒ Device
readonly
The device this handle belongs to.
Instance Method Summary collapse
-
#alloc_streams(num_streams, endpoints) ⇒ Fixnum
Allocate up to num_streams usb bulk streams on the specified endpoints.
-
#attach_kernel_driver(interface) ⇒ Object
Re-attach an interface’s kernel driver, which was previously detached using #detach_kernel_driver.
-
#auto_detach_kernel_driver= ⇒ Object
Enable/disable libusb’s automatic kernel driver detachment.
-
#bos ⇒ Bos
Get a Binary Object Store (BOS) descriptor.
-
#bulk_transfer(timeout: 1000, endpoint:, dataIn: nil, dataOut: nil, allow_device_memory: false) {|result| ... } ⇒ Fixnum, ...
Perform a USB bulk transfer.
-
#claim_interface(interface) ⇒ Object
Claim an interface on a given device handle.
-
#clear_halt(endpoint) ⇒ Object
Clear the halt/stall condition for an endpoint.
-
#close ⇒ Object
Close a device handle.
-
#control_transfer(bmRequestType:, bRequest:, wValue:, wIndex:, timeout: 1000, dataIn: nil, dataOut: nil, allow_device_memory: false) {|result| ... } ⇒ Fixnum, ...
Perform a USB control transfer.
-
#detach_kernel_driver(interface) ⇒ Object
Detach a kernel driver from an interface.
-
#eventmachine_bulk_transfer(**opts) ⇒ Object
Execute an eventmachine driven USB bulk transfer.
-
#eventmachine_control_transfer(**opts) ⇒ Object
Execute an eventmachine driven USB control transfer.
-
#eventmachine_interrupt_transfer(**opts) ⇒ Object
Execute an eventmachine driven USB interrupt transfer.
-
#free_streams ⇒ Object
Free usb bulk streams allocated with #alloc_streams.
-
#initialize(device, pHandle) ⇒ DevHandle
constructor
A new instance of DevHandle.
-
#interrupt_transfer(timeout: 1000, endpoint:, dataIn: nil, dataOut: nil, allow_device_memory: false) {|result| ... } ⇒ Fixnum, ...
Perform a USB interrupt transfer.
-
#kernel_driver_active?(interface) ⇒ Boolean
Determine if a kernel driver is active on an interface.
-
#release_interface(interface) ⇒ Object
Release an interface previously claimed with #claim_interface.
-
#reset_device ⇒ Object
Perform a USB port reset to reinitialize a device.
-
#set_configuration(configuration) ⇒ Object
(also: #configuration=)
Set the active configuration for a device.
-
#set_interface_alt_setting(setting_or_interface_number, alternate_setting = nil) ⇒ Object
Activate an alternate setting for an interface.
- #string_descriptor_ascii(index) ⇒ Object
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
#device ⇒ Device (readonly)
Returns 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.
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.
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.
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 |
#bos ⇒ Bos
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.
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(timeout: 1000, endpoint:, dataIn: nil, dataOut: nil, allow_device_memory: false) {|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.
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
# File 'lib/libusb/dev_handle.rb', line 375 def bulk_transfer(timeout: 1000, endpoint:, dataIn: nil, dataOut: nil, allow_device_memory: false, &block) endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress if endpoint&ENDPOINT_IN != 0 dataIn || raise(ArgumentError, "no :dataIn given for bulk read") else dataOut || raise(ArgumentError, "no :dataOut given for bulk write") end # reuse transfer struct to speed up transfer @bulk_transfer ||= BulkTransfer.new dev_handle: self, allow_device_memory: allow_device_memory 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.
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.
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 |
#close ⇒ Object
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(bmRequestType:, bRequest:, wValue:, wIndex:, timeout: 1000, dataIn: nil, dataOut: nil, allow_device_memory: false) {|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.
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
# File 'lib/libusb/dev_handle.rb', line 505 def control_transfer(bmRequestType:, bRequest:, wValue:, wIndex:, timeout: 1000, dataIn: nil, dataOut: nil, allow_device_memory: false, &block) if bmRequestType&ENDPOINT_IN != 0 raise ArgumentError, "invalid param :dataOut" unless dataOut.nil? dataIn ||= 0 dataOut = '' else raise ArgumentError, "invalid param :dataIn" unless dataIn.nil? dataOut ||= '' end # reuse transfer struct to speed up transfer @control_transfer ||= ControlTransfer.new dev_handle: self, allow_device_memory: allow_device_memory 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.
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.
158 159 160 |
# File 'lib/libusb/eventmachine.rb', line 158 def eventmachine_bulk_transfer(**opts) eventmachine_transfer(opts, :bulk_transfer) end |
#eventmachine_control_transfer(**opts) ⇒ Object
Execute an eventmachine driven USB control transfer.
178 179 180 |
# File 'lib/libusb/eventmachine.rb', line 178 def eventmachine_control_transfer(**opts) eventmachine_transfer(opts, :control_transfer) end |
#eventmachine_interrupt_transfer(**opts) ⇒ Object
Execute an eventmachine driven USB interrupt transfer.
141 142 143 |
# File 'lib/libusb/eventmachine.rb', line 141 def eventmachine_interrupt_transfer(**opts) eventmachine_transfer(opts, :interrupt_transfer) end |
#free_streams ⇒ Object
Free usb bulk streams allocated with #alloc_streams
Note streams are automatically free-ed when releasing an interface.
Available since libusb-1.0.19.
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(timeout: 1000, endpoint:, dataIn: nil, dataOut: nil, allow_device_memory: false) {|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.
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
# File 'lib/libusb/dev_handle.rb', line 446 def interrupt_transfer(timeout: 1000, endpoint:, dataIn: nil, dataOut: nil, allow_device_memory: false, &block) endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress if endpoint&ENDPOINT_IN != 0 dataIn || raise(ArgumentError, "no :dataIn given for interrupt read") else dataOut || raise(ArgumentError, "no :dataOut given for interrupt write") end # reuse transfer struct to speed up transfer @interrupt_transfer ||= InterruptTransfer.new dev_handle: self, allow_device_memory: allow_device_memory 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.
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.
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_device ⇒ Object
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.
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.
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 |