Class: LIBUSB::Transfer

Inherits:
Object
  • Object
show all
Defined in:
lib/libusb/transfer.rb

Overview

Abstract base class for USB transfers. Use ControlTransfer, BulkTransfer, InterruptTransfer, IsochronousTransfer to do transfers.

There are convenience methods for DevHandle#bulk_transfer, DevHandle#control_transfer and DevHandle#interrupt_transfer, that fit for most use cases. Using Transfer derived classes directly, however, is needed for isochronous transfers and allows a more advanced buffer management.

Defined Under Namespace

Classes: ZeroCopyMemory

Constant Summary collapse

TransferStatusToError =
{
  :TRANSFER_ERROR => LIBUSB::ERROR_IO,
  :TRANSFER_TIMED_OUT => LIBUSB::ERROR_TIMEOUT,
  :TRANSFER_CANCELLED => LIBUSB::ERROR_INTERRUPTED,
  :TRANSFER_STALL => LIBUSB::ERROR_PIPE,
  :TRANSFER_NO_DEVICE => LIBUSB::ERROR_NO_DEVICE,
  :TRANSFER_OVERFLOW => LIBUSB::ERROR_OVERFLOW,
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#allow_device_memoryObject

Try to use persistent device memory.

If enabled, attempts to allocate a block of persistent DMA memory suitable for transfers against the given device. The memory is allocated by #alloc_buffer or #buffer=. If unsuccessful, ordinary user space memory will be used.

Using this memory instead of regular memory means that the host controller can use DMA directly into the buffer to increase performance, and also that transfers can no longer fail due to kernel memory fragmentation.

It requires libusb-1.0.21 and Linux-4.6 to be effective, but it can safely be enabled on other systems.

Note that this type of memory is bound to the #dev_handle=. So even if the DevHandle is closed, the memory is still accessable and the device is locked. It is free'd by the garbage collector eventually, but in order to close the device deterministic, it is required to call #free_buffer on all LIBUSB::Transfers which use persistent device memory.


145
146
147
# File 'lib/libusb/transfer.rb', line 145

def allow_device_memory
  @allow_device_memory
end

#dev_handleObject

The handle for the device to communicate with.


66
67
68
# File 'lib/libusb/transfer.rb', line 66

def dev_handle
  @dev_handle
end

Instance Method Details

#actual_buffer(offset = 0) ⇒ Object

Retrieve the data actually transferred.

Parameters:

  • offset (Fixnum) (defaults to: 0)

    optional offset of the retrieved data in the buffer.


178
179
180
# File 'lib/libusb/transfer.rb', line 178

def actual_buffer(offset=0)
  @transfer[:buffer].get_bytes(offset, @transfer[:actual_length])
end

#actual_lengthObject

The number of bytes actually transferred.


125
126
127
# File 'lib/libusb/transfer.rb', line 125

def actual_length
  @transfer[:actual_length]
end

#alloc_buffer(len, data = nil) ⇒ Object

Allocate len bytes of data buffer for input transfer.

Parameters:

  • len (Fixnum)

    Number of bytes to allocate

  • data (String, nil) (defaults to: nil)

    some data to initialize the buffer with

See Also:


117
118
119
120
121
122
# File 'lib/libusb/transfer.rb', line 117

def alloc_buffer(len, data=nil)
  ensure_enough_buffer(len)
  @buffer.put_bytes(0, data) if data
  @transfer[:buffer] = @buffer
  @transfer[:length] = len
end

#bufferObject

Retrieve the current data buffer.


98
99
100
# File 'lib/libusb/transfer.rb', line 98

def buffer
  @transfer[:buffer].read_string(@transfer[:length])
end

#buffer=(data) ⇒ Object

Set output data that should be sent.


90
91
92
93
94
95
# File 'lib/libusb/transfer.rb', line 90

def buffer=(data)
  ensure_enough_buffer(data.bytesize)
  @buffer.put_bytes(0, data)
  @transfer[:buffer] = @buffer
  @transfer[:length] = data.bytesize
end

#callback=(proc) ⇒ Object

Set the block that will be invoked when the transfer completes, fails, or is cancelled.

Parameters:

  • proc (Proc)

    The block that should be called


186
187
188
189
190
191
192
193
# File 'lib/libusb/transfer.rb', line 186

def callback=(proc)
  # Save proc to instance variable so that GC doesn't free
  # the proc object before the transfer.
  @callback_proc = proc do |pTrans|
    proc.call(self)
  end
  @transfer[:callback] = @callback_proc
end

#cancel!Object

Asynchronously cancel a previously submitted transfer.

This function returns immediately, but this does not indicate cancellation is complete. Your callback function will be invoked at some later time with a transfer status of :TRANSFER_CANCELLED.


225
226
227
228
# File 'lib/libusb/transfer.rb', line 225

def cancel!
  res = Call.libusb_cancel_transfer( @transfer )
  LIBUSB.raise_error res, "in libusb_cancel_transfer" if res!=0
end

#endpoint=(endpoint) ⇒ Object

Set the address of a valid endpoint to communicate with.


83
84
85
86
# File 'lib/libusb/transfer.rb', line 83

def endpoint=(endpoint)
  endpoint = endpoint.bEndpointAddress if endpoint.respond_to? :bEndpointAddress
  @transfer[:endpoint] = endpoint
end

#free_bufferObject

Clear the current data buffer.


103
104
105
106
107
108
109
110
# File 'lib/libusb/transfer.rb', line 103

def free_buffer
  if @buffer
    @buffer.free
    @buffer = nil
    @transfer[:buffer] = nil
    @transfer[:length] = 0
  end
end

#memory_typeObject

Returns:

  • :device_memory - If persistent device memory is allocated.

  • :user_space - If user space memory is allocated.

  • nil - If no memory is allocated.


150
151
152
153
154
155
156
# File 'lib/libusb/transfer.rb', line 150

def memory_type
  case @buffer
    when ZeroCopyMemory then :device_memory
    when FFI::MemoryPointer then :user_space
    else nil
  end
end

#statusObject

The status of the transfer.

Only for use within transfer callback function or after the callback was called.

If this is an isochronous transfer, this field may read :TRANSFER_COMPLETED even if there were errors in the frames. Use the status field in each packet to determine if errors occurred.


202
203
204
# File 'lib/libusb/transfer.rb', line 202

def status
  @transfer[:status]
end

#submit!(&block) ⇒ Object

Submit a transfer.

This function will fire off the USB transfer and then return immediately. This method can be called with block. It is called when the transfer completes, fails, or is cancelled.


211
212
213
214
215
216
217
218
# File 'lib/libusb/transfer.rb', line 211

def submit!(&block)
  self.callback = block if block_given?

#       puts "submit transfer #{@transfer.inspect} buffer: #{@transfer[:buffer].inspect} length: #{@transfer[:length].inspect} status: #{@transfer[:status].inspect} callback: #{@transfer[:callback].inspect} dev_handle: #{@transfer[:dev_handle].inspect}"

  res = Call.libusb_submit_transfer( @transfer )
  LIBUSB.raise_error res, "in libusb_submit_transfer" if res!=0
end

#submit_and_waitObject

Submit the transfer and wait until the transfer completes or fails.

Inspect #status to check for transfer errors.

Raises:

  • (ArgumentError)

242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/libusb/transfer.rb', line 242

def submit_and_wait
  raise ArgumentError, "#{self.class}#dev_handle not set" unless @dev_handle

  @completion_flag.completed = false
  submit! do |tr2|
    @completion_flag.completed = true
  end

  until @completion_flag.completed?
    begin
      @dev_handle.device.context.handle_events nil, @completion_flag
    rescue ERROR_INTERRUPTED
      next
    rescue LIBUSB::Error
      cancel!
      until @completion_flag.completed?
        @dev_handle.device.context.handle_events nil, @completion_flag
      end
      raise
    end
  end
end

#submit_and_wait!Object

Submit the transfer and wait until the transfer completes or fails.

A proper Error is raised, in case the transfer did not complete.

Raises:


268
269
270
271
272
# File 'lib/libusb/transfer.rb', line 268

def submit_and_wait!
  submit_and_wait

  raise( TransferStatusToError[status] || ERROR_OTHER, "error #{status}") unless status==:TRANSFER_COMPLETED
end

#timeoutObject

Get timeout for this transfer in millseconds.

A value of 0 indicates no timeout.


78
79
80
# File 'lib/libusb/transfer.rb', line 78

def timeout
  @transfer[:timeout]
end

#timeout=(value) ⇒ Object

Set timeout for this transfer in millseconds.

A value of 0 indicates no timeout.


71
72
73
# File 'lib/libusb/transfer.rb', line 71

def timeout=(value)
  @transfer[:timeout] = value
end