libusb 0.2.2-x86-mingw32 → 0.3.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -47,18 +47,31 @@ module LIBUSB
47
47
  # * Bits 7: Direction 0 = Out, 1 = In (Ignored for Control Endpoints)
48
48
  #
49
49
  # @return [Integer]
50
+ #
51
+ # @see #endpoint_number
52
+ # @see #direction
50
53
  def bEndpointAddress
51
54
  self[:bEndpointAddress]
52
55
  end
53
56
 
57
+ # @return [Integer]
58
+ def endpoint_number
59
+ bEndpointAddress & 0b1111
60
+ end
61
+
62
+ # @return [Symbol] Either +:in+ or +:out+
63
+ def direction
64
+ bEndpointAddress & ENDPOINT_IN == 0 ? :out : :in
65
+ end
66
+
54
67
  # Attributes which apply to the endpoint when it is configured using the {Configuration#bConfigurationValue}.
55
68
  #
56
- # * Bits 0..1: Transfer Type
69
+ # * Bits 1..0: Transfer Type
57
70
  # * 00 = Control
58
71
  # * 01 = Isochronous
59
72
  # * 10 = Bulk
60
73
  # * 11 = Interrupt
61
- # * Bits 2..7: are reserved. If Isochronous endpoint,
74
+ # * Bits 7..2: are reserved. If Isochronous endpoint,
62
75
  # * Bits 3..2: Synchronisation Type (Iso Mode)
63
76
  # * 00 = No Synchonisation
64
77
  # * 01 = Asynchronous
@@ -71,10 +84,34 @@ module LIBUSB
71
84
  # * 11 = Reserved
72
85
  #
73
86
  # @return [Integer]
87
+ #
88
+ # @see #transfer_type
89
+ # @see #usage_type
90
+ # @see #synchronization_type
74
91
  def bmAttributes
75
92
  self[:bmAttributes]
76
93
  end
77
94
 
95
+ TransferTypes = [:control, :isochronous, :bulk, :interrupt]
96
+ # @return [Symbol] One of {TransferTypes}
97
+ def transfer_type
98
+ TransferTypes[bmAttributes & 0b11]
99
+ end
100
+
101
+ SynchronizationTypes = [:no_synchronization, :asynchronous, :adaptive, :synchronous]
102
+ # @return [Symbol] One of {SynchronizationTypes}
103
+ def synchronization_type
104
+ return unless transfer_type == :isochronous
105
+ SynchronizationTypes[(bmAttributes & 0b1100) >> 2]
106
+ end
107
+
108
+ UsageTypes = [:data, :feedback, :implicit_feedback, :unknown]
109
+ # @return [Symbol] One of {UsageTypes}
110
+ def usage_type
111
+ return unless transfer_type == :isochronous
112
+ UsageTypes[(bmAttributes & 0b110000) >> 4]
113
+ end
114
+
78
115
  # Maximum Packet Size this endpoint is capable of sending or receiving
79
116
  def wMaxPacketSize
80
117
  self[:wMaxPacketSize]
@@ -117,18 +154,8 @@ module LIBUSB
117
154
  attr_reader :setting
118
155
 
119
156
  def inspect
120
- endpoint_address = self.bEndpointAddress
121
- num = endpoint_address & 0b00001111
122
- inout = (endpoint_address & 0b10000000) == 0 ? "OUT" : "IN "
123
- bits = self.bmAttributes
124
- transfer_type = %w[Control Isochronous Bulk Interrupt][0b11 & bits]
125
- type = [transfer_type]
126
- if transfer_type == 'Isochronous'
127
- synchronization_type = %w[NoSynchronization Asynchronous Adaptive Synchronous][(0b1100 & bits) >> 2]
128
- usage_type = %w[Data Feedback ImplicitFeedback ?][(0b110000 & bits) >> 4]
129
- type << synchronization_type << usage_type
130
- end
131
- "\#<#{self.class} #{num} #{inout} #{type.join(" ")}>"
157
+ type = [transfer_type, synchronization_type, usage_type].compact
158
+ "\#<#{self.class} #{endpoint_number} #{direction} #{type.join(" ")}>"
132
159
  end
133
160
 
134
161
  # The {Device} this Endpoint belongs to.
@@ -0,0 +1,183 @@
1
+ # This file is part of Libusb for Ruby.
2
+ #
3
+ # Libusb for Ruby is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Libusb for Ruby is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'libusb'
17
+ require 'eventmachine'
18
+
19
+ module LIBUSB
20
+ class Context
21
+ # Register libusb's file descriptors and timeouts to EventMachine.
22
+ #
23
+ # @example
24
+ # require 'libusb/eventmachine'
25
+ # context = LIBUSB::Context.new
26
+ # EventMachine.run do
27
+ # context.eventmachine_register
28
+ # end
29
+ #
30
+ # @see
31
+ # DevHandle#eventmachine_bulk_transfer
32
+ # DevHandle#eventmachine_control_transfer
33
+ # DevHandle#eventmachine_interrupt_transfer
34
+ def eventmachine_register
35
+ @eventmachine_attached_fds = {}
36
+ @eventmachine_timer = nil
37
+
38
+ pollfds = self.pollfds
39
+ if pollfds
40
+ pollfds.each do |pollfd|
41
+ eventmachine_add_pollfd(pollfd)
42
+ end
43
+
44
+ self.on_pollfd_added do |pollfd|
45
+ eventmachine_add_pollfd(pollfd)
46
+ end
47
+
48
+ self.on_pollfd_removed do |pollfd|
49
+ eventmachine_rm_pollfd(pollfd)
50
+ end
51
+ else
52
+ # Libusb pollfd API is not available on this platform.
53
+ # Use simple polling timer, instead:
54
+ EventMachine.add_periodic_timer(0.01) do
55
+ @eventmachine_timer = self.handle_events 0
56
+ end
57
+ end
58
+ end
59
+
60
+ def eventmachine_unregister
61
+ @eventmachine_timer.cancel if @eventmachine_timer
62
+ @eventmachine_attached_fds.each do |fd, watcher|
63
+ watcher.detach
64
+ end
65
+ end
66
+
67
+ class EMPollfdHandler < EventMachine::Connection
68
+ def initialize
69
+ @callbacks = []
70
+ super
71
+ end
72
+
73
+ def on_need_handle_events(&block)
74
+ @callbacks << block
75
+ end
76
+
77
+ def need_handle_events
78
+ @callbacks.each do |cb|
79
+ cb.call
80
+ end
81
+ end
82
+ alias notify_readable need_handle_events
83
+ alias notify_writable need_handle_events
84
+ end
85
+
86
+ private
87
+ def eventmachine_add_pollfd(pollfd)
88
+ conn = EventMachine.watch(pollfd.io, EMPollfdHandler)
89
+ conn.notify_readable = pollfd.pollin?
90
+ conn.notify_writable = pollfd.pollout?
91
+ cb = proc do
92
+ if @eventmachine_timer
93
+ @eventmachine_timer.cancel
94
+ @eventmachine_timer = nil
95
+ end
96
+
97
+ self.handle_events 0
98
+ timeout = self.next_timeout
99
+ # puts "libusb new timeout: #{timeout.inspect}"
100
+ if timeout
101
+ @eventmachine_timer = EventMachine.add_timer(timeout, &cb)
102
+ end
103
+ end
104
+ conn.on_need_handle_events(&cb)
105
+
106
+ @eventmachine_attached_fds[pollfd.fd] = conn
107
+ # puts "libusb pollfd added: #{pollfd.inspect}"
108
+ end
109
+
110
+ def eventmachine_rm_pollfd(pollfd)
111
+ @eventmachine_attached_fds[pollfd.fd].detach
112
+ # puts "libusb pollfd removed: #{pollfd.inspect}"
113
+ end
114
+ end
115
+
116
+ class DevHandle
117
+ class EMTransfer
118
+ include EM::Deferrable
119
+
120
+ def initialize(opts, dev_handle, transfer_method)
121
+ dev_handle.send(transfer_method, opts) do |res|
122
+ EM.next_tick do
123
+ if res.kind_of?(LIBUSB::Error)
124
+ fail res
125
+ else
126
+ succeed res
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ # Execute an eventmachine driven USB interrupt transfer.
134
+ #
135
+ # @see Context#eventmachine_register
136
+ # DevHandle#interrupt_transfer
137
+ def eventmachine_interrupt_transfer(opts={})
138
+ eventmachine_transfer(opts, :interrupt_transfer)
139
+ end
140
+
141
+ # Execute an eventmachine driven USB bulk transfer.
142
+ #
143
+ # @example
144
+ # tr = devh.eventmachine_bulk_transfer( :endpoint => 0x02, :dataOut => "data" )
145
+ # tr.callback do |data|
146
+ # puts "sent: #{data.inspect}"
147
+ # end
148
+ # tr.errback do |ex|
149
+ # puts "send-err: #{ex}"
150
+ # end
151
+ #
152
+ # @see Context#eventmachine_register
153
+ # DevHandle#bulk_transfer
154
+ def eventmachine_bulk_transfer(opts={})
155
+ eventmachine_transfer(opts, :bulk_transfer)
156
+ end
157
+
158
+ # Execute an eventmachine driven USB control transfer.
159
+ #
160
+ # @example
161
+ # tr = devh.eventmachine_control_transfer(
162
+ # :bmRequestType=>ENDPOINT_IN|REQUEST_TYPE_CLASS|RECIPIENT_INTERFACE,
163
+ # :bRequest=>0x01,
164
+ # :wValue=>0, :wIndex=>0, :dataIn=>1 )
165
+ # tr.callback do |data|
166
+ # puts "recved: #{data.inspect}"
167
+ # end
168
+ # tr.errback do |ex|
169
+ # puts "recv-err: #{ex}"
170
+ # end
171
+ #
172
+ # @see Context#eventmachine_register
173
+ # DevHandle#control_transfer
174
+ def eventmachine_control_transfer(opts={})
175
+ eventmachine_transfer(opts, :control_transfer)
176
+ end
177
+
178
+ private
179
+ def eventmachine_transfer(opts, method)
180
+ EMTransfer.new opts, self, method
181
+ end
182
+ end
183
+ end
@@ -19,10 +19,16 @@ module LIBUSB
19
19
  # Abstract base class for USB transfers. Use
20
20
  # {ControlTransfer}, {BulkTransfer}, {InterruptTransfer}, {IsochronousTransfer}
21
21
  # to do transfers.
22
+ #
23
+ # There are convenience methods for {DevHandle#bulk_transfer}, {DevHandle#control_transfer}
24
+ # and {DevHandle#interrupt_transfer}, that fit for most use cases.
25
+ # Using {Transfer} derived classes directly, however, is needed for isochronous transfers and
26
+ # allows a more advanced buffer management.
22
27
  class Transfer
23
28
  def initialize(args={})
24
29
  args.each{|k,v| send("#{k}=", v) }
25
30
  @buffer = nil
31
+ @completion_flag = Context::CompletionFlag.new
26
32
  end
27
33
  private :initialize
28
34
 
@@ -156,26 +162,33 @@ module LIBUSB
156
162
 
157
163
  # Submit the transfer and wait until the transfer completes or fails.
158
164
  #
159
- # A proper {LIBUSB::Error} is raised, in case the transfer did not complete.
160
- def submit_and_wait!
161
- completed = false
165
+ # Inspect {#status} to check for transfer errors.
166
+ def submit_and_wait
167
+ @completion_flag.completed = false
162
168
  submit! do |tr2|
163
- completed = true
169
+ @completion_flag.completed = true
164
170
  end
165
171
 
166
- until completed
172
+ until @completion_flag.completed?
167
173
  begin
168
- @dev_handle.device.context.handle_events
174
+ @dev_handle.device.context.handle_events nil, @completion_flag
169
175
  rescue ERROR_INTERRUPTED
170
176
  next
171
177
  rescue LIBUSB::Error
172
178
  cancel!
173
- until completed
174
- @dev_handle.device.context.handle_events
179
+ until @completion_flag.completed?
180
+ @dev_handle.device.context.handle_events nil, @completion_flag
175
181
  end
176
182
  raise
177
183
  end
178
184
  end
185
+ end
186
+
187
+ # Submit the transfer and wait until the transfer completes or fails.
188
+ #
189
+ # A proper {LIBUSB::Error} is raised, in case the transfer did not complete.
190
+ def submit_and_wait!
191
+ submit_and_wait
179
192
 
180
193
  raise( TransferStatusToError[status] || ERROR_OTHER, "error #{status}") unless status==:TRANSFER_COMPLETED
181
194
  end
@@ -0,0 +1,19 @@
1
+ # This file is part of Libusb for Ruby.
2
+ #
3
+ # Libusb for Ruby is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Libusb for Ruby is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module LIBUSB
17
+ # Library version of libusb for Ruby
18
+ VERSION = "0.3.0"
19
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "libusb/version_gem"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "libusb"
7
+ s.version = LIBUSB::VERSION
8
+ s.authors = ["Lars Kanis"]
9
+ s.email = ["lars@greiz-reinsdorf.de"]
10
+ s.homepage = "http://github.com/larskanis/libusb"
11
+ s.summary = %q{Access USB devices from Ruby via libusb-1.0}
12
+ s.description = %q{LIBUSB is a Ruby binding that gives Ruby programmers access to arbitrary USB devices}
13
+ s.rdoc_options = %w[--main README.md --charset=UTF-8]
14
+
15
+ s.rubyforge_project = "libusb"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ s.extensions = ['ext/extconf.rb']
22
+
23
+ # travis currently runs a slightly older version of rbx,
24
+ # that needs a special ffi version.
25
+ unless ENV['TRAVIS'] && defined?(RUBY_ENGINE) && RUBY_ENGINE=='rbx'
26
+ s.add_runtime_dependency 'ffi', '>= 1.0'
27
+ end
28
+
29
+ s.add_development_dependency 'rake-compiler', '>= 0.6'
30
+ s.add_development_dependency 'bundler'
31
+ end
@@ -68,7 +68,7 @@ class TestLibusbCompat < Test::Unit::TestCase
68
68
  assert_equal config_desc, ep.configuration, "backref should be correct"
69
69
  assert_equal dev, ep.device, "backref should be correct"
70
70
 
71
- assert_operator 0, :<, ep.wMaxPacketSize, "packet size should be > 0"
71
+ assert_operator 0, :<=, ep.wMaxPacketSize, "packet size should be > 0"
72
72
  end
73
73
  end
74
74
  end
@@ -16,7 +16,7 @@
16
16
  require "test/unit"
17
17
  require "libusb/compat"
18
18
 
19
- class TestLibusbCompat < Test::Unit::TestCase
19
+ class TestLibusbCompatMassStorage < Test::Unit::TestCase
20
20
  include USB
21
21
 
22
22
  attr_accessor :devh
@@ -30,7 +30,7 @@ class TestLibusbCompat < Test::Unit::TestCase
30
30
  USB.each_device_by_class(USB_CLASS_MASS_STORAGE, 0x06, 0x50){|dev| devs << dev }
31
31
 
32
32
  dev = devs.last
33
- abort "no mass storage device found" unless dev
33
+ skip "no mass storage device found" unless dev
34
34
  devh = dev.open
35
35
  if RUBY_PLATFORM=~/linux/i
36
36
  data = " "*1000
@@ -101,7 +101,7 @@ class TestLibusbDescriptors < Test::Unit::TestCase
101
101
  assert_equal 5, ep.bDescriptorType
102
102
  assert_kind_of Integer, ep.bEndpointAddress
103
103
  assert_kind_of Integer, ep.bmAttributes
104
- assert_operator 0, :<, ep.wMaxPacketSize, "packet size should be > 0"
104
+ assert_operator 0, :<=, ep.wMaxPacketSize, "packet size should be > 0"
105
105
  assert_kind_of Integer, ep.bInterval
106
106
  assert_kind_of Integer, ep.bRefresh
107
107
  assert_kind_of Integer, ep.bSynchAddress
@@ -0,0 +1,118 @@
1
+ # This file is part of Libusb for Ruby.
2
+ #
3
+ # Libusb for Ruby is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # Libusb for Ruby is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with Libusb for Ruby. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require "test/unit"
17
+ require "libusb"
18
+ require "libusb/eventmachine"
19
+ require "eventmachine"
20
+
21
+ class TestLibusbEventMachine < Test::Unit::TestCase
22
+ include LIBUSB
23
+ BOMS_GET_MAX_LUN = 0xFE
24
+
25
+ attr_accessor :context
26
+ attr_accessor :device
27
+ attr_accessor :devh
28
+ attr_accessor :endpoint_in
29
+ attr_accessor :endpoint_out
30
+
31
+ def setup
32
+ @context = Context.new
33
+ @context.debug = 3
34
+
35
+ @device = context.devices( :bClass=>CLASS_MASS_STORAGE, :bSubClass=>[0x06,0x01], :bProtocol=>0x50 ).last
36
+ skip "no mass storage device found" unless @device
37
+
38
+ @endpoint_in = @device.endpoints.find{|ep| ep.bEndpointAddress&ENDPOINT_IN != 0 }
39
+ @endpoint_out = @device.endpoints.find{|ep| ep.bEndpointAddress&ENDPOINT_IN == 0 }
40
+ @devh = @device.open
41
+
42
+ if RUBY_PLATFORM=~/linux/i && devh.kernel_driver_active?(0)
43
+ devh.detach_kernel_driver(0)
44
+ end
45
+ devh.claim_interface(0)
46
+
47
+ # clear any pending data
48
+ devh.clear_halt(endpoint_in)
49
+ end
50
+
51
+ def teardown
52
+ end
53
+
54
+ def em_run
55
+ EventMachine.run do
56
+ @context.eventmachine_register
57
+
58
+ EventMachine.add_shutdown_hook do
59
+ @devh.release_interface(0) if @devh
60
+ @devh.close if @devh
61
+ @context.eventmachine_unregister
62
+ end
63
+
64
+ yield
65
+ end
66
+ end
67
+
68
+
69
+ def test_bulk_transfer
70
+ em_run do
71
+ ticks = 0
72
+ tr = devh.eventmachine_bulk_transfer(
73
+ :endpoint => @endpoint_in,
74
+ :timeout => 1500,
75
+ :dataIn => 123 )
76
+ # puts "started usb transfer #{tr}"
77
+
78
+ tr.callback do |data|
79
+ # puts "recved: #{data.inspect}"
80
+
81
+ assert false, "the bulk transfer shouldn't succeed"
82
+ EventMachine.stop
83
+ end
84
+ tr.errback do |text|
85
+ # puts "recv-err: #{text}"
86
+ assert true, "the bulk transfer should fail"
87
+
88
+ assert_operator ticks, :>=, 4
89
+ EventMachine.stop
90
+ end
91
+
92
+ EventMachine.add_periodic_timer(0.333) do
93
+ ticks += 1
94
+ end
95
+ end
96
+ end
97
+
98
+ def test_event_loop
99
+ em_run do
100
+ tr = devh.eventmachine_control_transfer(
101
+ :bmRequestType=>ENDPOINT_IN|REQUEST_TYPE_CLASS|RECIPIENT_INTERFACE,
102
+ :bRequest=>BOMS_GET_MAX_LUN,
103
+ :wValue=>0, :wIndex=>0, :dataIn=>1 )
104
+
105
+ # puts "started usb transfer #{tr}"
106
+ tr.callback do |data|
107
+ # puts "recved: #{data.inspect}"
108
+ assert true, "the control transfer should succeed"
109
+ EventMachine.stop
110
+ end
111
+ tr.errback do |text|
112
+ # puts "recv-err: #{text}"
113
+ assert false, "the control transfer shouldn't fail"
114
+ EventMachine.stop
115
+ end
116
+ end
117
+ end
118
+ end