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.



149
150
151
# File 'lib/libusb/transfer.rb', line 149

def allow_device_memory
  @allow_device_memory
end

#dev_handleObject

The handle for the device to communicate with.



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

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.



182
183
184
# File 'lib/libusb/transfer.rb', line 182

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

#actual_lengthObject

The number of bytes actually transferred.



129
130
131
# File 'lib/libusb/transfer.rb', line 129

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:



121
122
123
124
125
126
# File 'lib/libusb/transfer.rb', line 121

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.



102
103
104
# File 'lib/libusb/transfer.rb', line 102

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

#buffer=(data) ⇒ Object

Set output data that should be sent.



94
95
96
97
98
99
# File 'lib/libusb/transfer.rb', line 94

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



190
191
192
193
194
195
196
197
# File 'lib/libusb/transfer.rb', line 190

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.



229
230
231
232
# File 'lib/libusb/transfer.rb', line 229

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.



87
88
89
90
# File 'lib/libusb/transfer.rb', line 87

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

#free_bufferObject

Clear the current data buffer.



107
108
109
110
111
112
113
114
# File 'lib/libusb/transfer.rb', line 107

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.



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

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.



206
207
208
# File 'lib/libusb/transfer.rb', line 206

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.



215
216
217
218
219
220
221
222
# File 'lib/libusb/transfer.rb', line 215

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)


246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/libusb/transfer.rb', line 246

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 Exception
      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:



272
273
274
275
276
# File 'lib/libusb/transfer.rb', line 272

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.



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

def timeout
  @transfer[:timeout]
end

#timeout=(value) ⇒ Object

Set timeout for this transfer in millseconds.

A value of 0 indicates no timeout.



75
76
77
# File 'lib/libusb/transfer.rb', line 75

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