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.



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

def allow_device_memory
  @allow_device_memory
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.



163
164
165
# File 'lib/libusb/transfer.rb', line 163

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

#actual_lengthObject

The number of bytes actually transferred.



110
111
112
# File 'lib/libusb/transfer.rb', line 110

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:



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

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.



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

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

#buffer=(data) ⇒ Object

Set output data that should be sent.



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

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



171
172
173
174
175
176
177
178
# File 'lib/libusb/transfer.rb', line 171

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.



210
211
212
213
# File 'lib/libusb/transfer.rb', line 210

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

#dev_handle=(dev) ⇒ Object

Set the handle for the device to communicate with.



55
56
57
58
# File 'lib/libusb/transfer.rb', line 55

def dev_handle=(dev)
  @dev_handle = dev
  @transfer[:dev_handle] = @dev_handle.pHandle
end

#endpoint=(endpoint) ⇒ Object

Set the address of a valid endpoint to communicate with.



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

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

#free_bufferObject

Clear the current data buffer.



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

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.



135
136
137
138
139
140
141
# File 'lib/libusb/transfer.rb', line 135

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.



187
188
189
# File 'lib/libusb/transfer.rb', line 187

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.



196
197
198
199
200
201
202
203
# File 'lib/libusb/transfer.rb', line 196

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.



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/libusb/transfer.rb', line 227

def submit_and_wait
  @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:



251
252
253
254
255
# File 'lib/libusb/transfer.rb', line 251

def submit_and_wait!
  submit_and_wait

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

#timeout=(value) ⇒ Object

Timeout for this transfer in millseconds.

A value of 0 indicates no timeout.



63
64
65
# File 'lib/libusb/transfer.rb', line 63

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