libftdi-ruby 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README.md +42 -0
  2. data/lib/ftdi.rb +204 -18
  3. data/lib/ftdi/version.rb +1 -2
  4. data/test.rb +5 -5
  5. metadata +8 -8
data/README.md CHANGED
@@ -2,3 +2,45 @@ Ruby bindings for [libftdi](http://www.intra2net.com/en/developer/libftdi/index.
2
2
 
3
3
  You must install libftdi itself in addition to this gem.
4
4
 
5
+ ## Synopsys
6
+
7
+ ```ruby
8
+ require 'rubygems'
9
+ require 'ftdi'
10
+
11
+ ctx = Ftdi::Context.new
12
+
13
+ begin
14
+ ctx.usb_open(0x0403, 0x6001)
15
+ begin
16
+ ctx.baudrate = 250000
17
+ ctx.set_line_property(:bits_8, :stop_bit_2, :none)
18
+ ctx.flowctrl = Ftdi::SIO_DISABLE_FLOW_CTRL
19
+
20
+ arr = Array.new(513) { |i| i.zero? ? 0 : 1 }
21
+ ctx.set_line_property2(:bits_8, :stop_bit_2, :none, :break_on)
22
+ sleep 0.001
23
+ ctx.set_line_property2(:bits_8, :stop_bit_2, :none, :break_off)
24
+ sleep 0.001
25
+ ctx.write_data(arr)
26
+
27
+ sleep 1
28
+
29
+ arr = [ 0 ] * 513
30
+ ctx.set_line_property2(:bits_8, :stop_bit_2, :none, :break_on)
31
+ sleep 0.001
32
+ ctx.set_line_property2(:bits_8, :stop_bit_2, :none, :break_off)
33
+ sleep 0.001
34
+ ctx.write_data(arr)
35
+
36
+ puts "Context is:"
37
+ ctx.members.each { |k| puts "#{k} = #{ctx[k]}" }
38
+
39
+ ensure
40
+ ctx.usb_close
41
+ end
42
+ rescue Ftdi::Error => e
43
+ $stderr.puts e.to_s
44
+ end
45
+ ```
46
+
@@ -1,6 +1,8 @@
1
1
  require 'ffi'
2
2
  require "ftdi/version"
3
3
 
4
+ # Represents libftdi ruby bindings.
5
+ # End-user API represented by {Ftdi::Context} class.
4
6
  module Ftdi
5
7
  extend FFI::Library
6
8
 
@@ -9,52 +11,80 @@ module Ftdi
9
11
  # FTDI chip type.
10
12
  ChipType = enum(:type_am, :type_bm, :type_2232c, :type_r, :type_2232h, :type_4232h, :type_232h)
11
13
 
12
- # Automatic loading / unloading of kernel modules
14
+ # Automatic loading / unloading of kernel modules.
13
15
  ModuleDetachMode = enum(:auto_detach_sio_module, :dont_detach_sio_module)
14
16
 
15
- # Number of bits for {Ftdi::Context.set_line_property}
17
+ # Number of bits for {Ftdi::Context#set_line_property}.
16
18
  BitsType = enum(
17
19
  :bits_7, 7,
18
20
  :bits_8, 8
19
21
  )
20
22
 
21
- # Number of stop bits for {Ftdi::Context.set_line_property}
23
+ # Number of stop bits for {Ftdi::Context#set_line_property}.
22
24
  StopbitsType = enum(
23
25
  :stop_bit_1, 0,
24
26
  :stop_bit_15, 1,
25
27
  :stop_bit_2, 2
26
28
  )
27
29
 
28
- # Parity mode for {Ftdi::Context.set_line_property}
30
+ # Parity mode for {Ftdi::Context#set_line_property}.
29
31
  ParityType = enum(:none, :odd, :even, :mark, :space)
30
32
 
31
- # Break type for {Ftdi::Context.set_line_property2}
33
+ # Break type for {Ftdi::Context#set_line_property2}.
32
34
  BreakType = enum(:break_off, :break_on)
33
35
 
36
+ # Port interface for chips with multiple interfaces.
37
+ # @see Ftdi::Context#interface=
38
+ Interface = enum(:interface_any, :interface_a, :interface_b, :interface_c, :interface_d)
39
+
40
+ # Flow control: disable
41
+ # @see Ftdi::Context#flowctrl=
34
42
  SIO_DISABLE_FLOW_CTRL = 0x0
43
+ # @see Ftdi::Context#flowctrl=
44
+ SIO_RTS_CTS_HS = (0x1 << 8)
45
+ # @see Ftdi::Context#flowctrl=
46
+ SIO_DTR_DSR_HS = (0x2 << 8)
47
+ # @see Ftdi::Context#flowctrl=
48
+ SIO_XON_XOFF_HS = (0x4 << 8)
35
49
 
36
50
  # Base error of libftdi.
37
51
  class Error < RuntimeError; end
38
52
 
39
- # Initialization error of libftdi.
53
+ # Represents initialization error of libftdi.
40
54
  class CannotInitializeContextError < Error; end
41
55
 
42
- # Error of libftdi with its status code.
56
+ # Represents error of libftdi with its status code.
43
57
  class StatusCodeError < Error
44
- attr_accessor :status_code
58
+ # Gets status code.
59
+ # @return [Fixnum] Status code.
60
+ attr_reader :status_code
45
61
 
46
62
  def initialize(status_code, message)
47
63
  super(message)
48
- self.status_code = status_code
64
+ @status_code = status_code
49
65
  end
50
66
 
67
+ # Gets string representation of the error.
68
+ # @return [String] Representation of the error.
51
69
  def to_s
52
70
  "#{status_code}: #{super}"
53
71
  end
54
72
  end
55
73
 
56
- # libftdi context
57
- class Context < FFI::Struct
74
+ # Represents libftdi context and end-user API.
75
+ # @example Open USB device
76
+ # ctx = Ftdi::Context.new
77
+ # begin
78
+ # ctx.usb_open(0x0403, 0x6001)
79
+ # begin
80
+ # ctx.baudrate = 250000
81
+ # ensure
82
+ # ctx.usb_close
83
+ # end
84
+ # rescue Ftdi::Error => e
85
+ # $stderr.puts e.to_s
86
+ # end
87
+ class Context < FFI::ManagedStruct
58
88
  layout(
59
89
  # USB specific
60
90
  # libusb's context
@@ -109,6 +139,8 @@ module Ftdi
109
139
  :module_detach_mode, Ftdi::ModuleDetachMode
110
140
  )
111
141
 
142
+ # Initializes new libftdi context.
143
+ # @raise [CannotInitializeContextError] libftdi cannot be initialized.
112
144
  def initialize
113
145
  ptr = Ftdi.ftdi_new
114
146
  raise CannotInitializeContextError.new if ptr.nil?
@@ -116,36 +148,85 @@ module Ftdi
116
148
  end
117
149
 
118
150
  # Deinitialize and free an ftdi context.
119
- def dispose
120
- Ftdi.ftdi_free(ctx)
151
+ # @return [NilClass] nil
152
+ def self.release(p)
153
+ Ftdi.ftdi_free(p)
121
154
  nil
122
155
  end
123
156
 
124
- alias :close :dispose
125
-
157
+ # Gets error text.
158
+ # @return [String] Error text.
126
159
  def error_string
127
160
  self[:error_str]
128
161
  end
129
162
 
130
163
  # Opens the first device with a given vendor and product ids.
164
+ # @param [Fixnum] vendor Vendor id.
165
+ # @param [Fixnum] product Product id.
166
+ # @return [NilClass] nil
167
+ # @raise [StatusCodeError] libftdi reports error.
168
+ # @raise [ArgumentError] Bad arguments.
131
169
  def usb_open(vendor, product)
132
170
  raise ArgumentError.new('vendor should be Fixnum') unless vendor.kind_of?(Fixnum)
133
171
  raise ArgumentError.new('product should be Fixnum') unless product.kind_of?(Fixnum)
134
172
  check_result(Ftdi.ftdi_usb_open(ctx, vendor, product))
135
173
  end
136
174
 
175
+ # Opens the first device with a given vendor and product ids, description and serial.
176
+ # @param [Fixnum] vendor Vendor id.
177
+ # @param [Fixnum] product Product id.
178
+ # @param [String] description Description to search for. Use nil if not needed.
179
+ # @param [String] serial Serial to search for. Use nil if not needed.
180
+ # @return [NilClass] nil
181
+ # @raise [StatusCodeError] libftdi reports error.
182
+ # @raise [ArgumentError] Bad arguments.
183
+ def usb_open_desc(vendor, product, description, serial)
184
+ raise ArgumentError.new('vendor should be Fixnum') unless vendor.kind_of?(Fixnum)
185
+ raise ArgumentError.new('product should be Fixnum') unless product.kind_of?(Fixnum)
186
+ check_result(Ftdi.ftdi_usb_open_desc(ctx, vendor, product, description, serial))
187
+ end
188
+
189
+ # Opens the index-th device with a given vendor and product ids, description and serial.
190
+ # @param [Fixnum] vendor Vendor id.
191
+ # @param [Fixnum] product Product id.
192
+ # @param [String] description Description to search for. Use nil if not needed.
193
+ # @param [String] serial Serial to search for. Use nil if not needed.
194
+ # @param [Fixnum] index Number of matching device to open if there are more than one, starts with 0.
195
+ # @return [NilClass] nil
196
+ # @raise [StatusCodeError] libftdi reports error.
197
+ # @raise [ArgumentError] Bad arguments.
198
+ def usb_open_desc_index(vendor, product, description, serial, index)
199
+ raise ArgumentError.new('vendor should be Fixnum') unless vendor.kind_of?(Fixnum)
200
+ raise ArgumentError.new('product should be Fixnum') unless product.kind_of?(Fixnum)
201
+ raise ArgumentError.new('index should be Fixnum') unless index.kind_of?(Fixnum)
202
+ raise ArgumentError.new('index should be greater than or equal to zero') if index < 0
203
+ check_result(Ftdi.ftdi_usb_open_desc_index(ctx, vendor, product, description, serial, index))
204
+ end
205
+
206
+ # Resets the ftdi device.
207
+ # @raise [StatusCodeError] libftdi reports error.
208
+ # @return [NilClass] nil
209
+ def usb_reset
210
+ check_result(Ftdi.ftdi_usb_reset(ctx))
211
+ end
212
+
137
213
  # Closes the ftdi device.
214
+ # @return [NilClass] nil
138
215
  def usb_close
139
216
  Ftdi.ftdi_usb_close(ctx)
140
217
  nil
141
218
  end
142
219
 
143
220
  # Gets the chip baud rate.
221
+ # @return [Fixnum] Baud rate.
144
222
  def baudrate
145
223
  self[:baudrate]
146
224
  end
147
225
 
148
226
  # Sets the chip baud rate.
227
+ # @raise [StatusCodeError] libftdi reports error.
228
+ # @raise [ArgumentError] Bad arguments.
229
+ # @return [NilClass] nil
149
230
  def baudrate=(new_baudrate)
150
231
  raise ArgumentError.new('baudrate should be Fixnum') unless new_baudrate.kind_of?(Fixnum)
151
232
  check_result(Ftdi.ftdi_set_baudrate(ctx, new_baudrate))
@@ -153,30 +234,126 @@ module Ftdi
153
234
 
154
235
  # Set (RS232) line characteristics.
155
236
  # The break type can only be set via {#set_line_property2} and defaults to "off".
237
+ # @param [BitsType] bits
238
+ # @param [StopbitsType] stopbits
239
+ # @param [ParityType] parity
240
+ # @raise [StatusCodeError] libftdi reports error.
241
+ # @return [NilClass] nil
156
242
  def set_line_property(bits, stopbits, parity)
157
243
  check_result(Ftdi.ftdi_set_line_property(ctx, bits, stopbits, parity))
158
244
  end
159
245
 
160
246
  # Set (RS232) line characteristics.
247
+ # @param [BitsType] bits
248
+ # @param [StopbitsType] stopbits
249
+ # @param [ParityType] parity
250
+ # @param [BreakType] _break
251
+ # @raise [StatusCodeError] libftdi reports error.
252
+ # @return [NilClass] nil
161
253
  def set_line_property2(bits, stopbits, parity, _break)
162
254
  check_result(Ftdi.ftdi_set_line_property2(ctx, bits, stopbits, parity, _break))
163
255
  end
164
256
 
165
- # Set flowcontrol for ftdi chip.
257
+ # Set flow control setting for ftdi chip.
258
+ # @param [Fixnum] new_flowctrl New flow control setting.
259
+ # @raise [StatusCodeError] libftdi reports error.
260
+ # @return [Fixnum] New flow control setting.
261
+ # @see SIO_DISABLE_FLOW_CTRL
262
+ # @see SIO_RTS_CTS_HS
263
+ # @see SIO_DTR_DSR_HS
264
+ # @see SIO_XON_XOFF_HS
166
265
  def flowctrl=(new_flowctrl)
167
266
  check_result(Ftdi.ftdi_setflowctrl(ctx, new_flowctrl))
267
+ new_flowctrl
268
+ end
269
+
270
+ # Gets write buffer chunk size.
271
+ # @return [Fixnum] Write buffer chunk size.
272
+ # @raise [StatusCodeError] libftdi reports error.
273
+ # @see #write_data_chunksize=
274
+ def write_data_chunksize
275
+ p = FFI::MemoryPointer.new(:uint, 1)
276
+ check_result(Ftdi.ftdi_write_data_get_chunksize(ctx, p))
277
+ p.read_uint
278
+ end
279
+
280
+ # Configure write buffer chunk size.
281
+ # Automatically reallocates the buffer.
282
+ # @note Default is 4096.
283
+ # @param [Fixnum] new_chunksize Write buffer chunk size.
284
+ # @return [Fixnum] New write buffer chunk size.
285
+ # @raise [StatusCodeError] libftdi reports error.
286
+ def write_data_chunksize=(new_chunksize)
287
+ check_result(Ftdi.ftdi_write_data_set_chunksize(ctx, new_chunksize))
288
+ new_chunksize
168
289
  end
169
290
 
291
+ # Writes data.
292
+ # @param [String, Array] bytes String or array of integers that will be interpreted as bytes using pack('c*').
293
+ # @return [Fixnum] Number of written bytes.
294
+ # @raise [StatusCodeError] libftdi reports error.
170
295
  def write_data(bytes)
171
296
  bytes = bytes.pack('c*') if bytes.respond_to?(:pack)
172
297
  size = bytes.respond_to?(:bytesize) ? bytes.bytesize : bytes.size
173
298
  mem_buf = FFI::MemoryPointer.new(:char, size)
174
299
  mem_buf.put_bytes(0, bytes)
175
- r = Ftdi.ftdi_write_data(ctx, mem_buf, size)
176
- check_result(r)
300
+ bytes_written = Ftdi.ftdi_write_data(ctx, mem_buf, size)
301
+ check_result(bytes_written)
302
+ bytes_written
303
+ end
304
+
305
+ # Gets read buffer chunk size.
306
+ # @return [Fixnum] Read buffer chunk size.
307
+ # @raise [StatusCodeError] libftdi reports error.
308
+ # @see #read_data_chunksize=
309
+ def read_data_chunksize
310
+ p = FFI::MemoryPointer.new(:uint, 1)
311
+ check_result(Ftdi.ftdi_read_data_get_chunksize(ctx, p))
312
+ p.read_uint
313
+ end
314
+
315
+ # Configure read buffer chunk size.
316
+ # Automatically reallocates the buffer.
317
+ # @note Default is 4096.
318
+ # @param [Fixnum] new_chunksize Read buffer chunk size.
319
+ # @return [Fixnum] New read buffer chunk size.
320
+ # @raise [StatusCodeError] libftdi reports error.
321
+ def read_data_chunksize=(new_chunksize)
322
+ check_result(Ftdi.ftdi_read_data_set_chunksize(ctx, new_chunksize))
323
+ new_chunksize
324
+ end
325
+
326
+ # Reads data in chunks from the chip.
327
+ # Returns when at least one byte is available or when the latency timer has elapsed.
328
+ # Automatically strips the two modem status bytes transfered during every read.
329
+ # @return [String] Bytes read; Empty string if no bytes read.
330
+ # @see #read_data_chunksize
331
+ # @raise [StatusCodeError] libftdi reports error.
332
+ def read_data
333
+ chunksize = read_data_chunksize
334
+ p = FFI::MemoryPointer.new(:char, chunksize)
335
+ bytes_read = Ftdi.ftdi_read_data(ctx, p, chunksize)
336
+ check_result(bytes_read)
337
+ r = p.read_bytes(bytes_read)
338
+ r.force_encoding("ASCII-8BIT") if r.respond_to?(:force_encoding)
177
339
  r
178
340
  end
179
341
 
342
+ # Gets used interface of the device.
343
+ # @return [Interface] Used interface of the device.
344
+ def interface
345
+ Interface[self[:interface]]
346
+ end
347
+
348
+ # Open selected channels on a chip, otherwise use first channel.
349
+ # @param [Interface] new_interface Interface to use for FT2232C/2232H/4232H chips.
350
+ # @raise [StatusCodeError] libftdi reports error.
351
+ # @return [Interface] New interface.
352
+ def interface=(new_interface)
353
+ check_result(Ftdi.ftdi_set_interface(ctx, new_interface))
354
+ new_interface
355
+ end
356
+
180
357
  private
181
358
  def ctx
182
359
  self.to_ptr
@@ -193,11 +370,20 @@ module Ftdi
193
370
  attach_function :ftdi_new, [ ], :pointer
194
371
  attach_function :ftdi_free, [ :pointer ], :void
195
372
  attach_function :ftdi_usb_open, [ :pointer, :int, :int ], :int
373
+ attach_function :ftdi_usb_open_desc, [ :pointer, :int, :int, :string, :string ], :int
374
+ attach_function :ftdi_usb_open_desc_index, [ :pointer, :int, :int, :string, :string, :uint ], :int
375
+ attach_function :ftdi_usb_reset, [ :pointer ], :int
196
376
  attach_function :ftdi_usb_close, [ :pointer ], :void
197
377
  attach_function :ftdi_set_baudrate, [ :pointer, :int ], :int
198
378
  attach_function :ftdi_set_line_property, [ :pointer, BitsType, StopbitsType, ParityType ], :int
199
379
  attach_function :ftdi_set_line_property2, [ :pointer, BitsType, StopbitsType, ParityType, BreakType ], :int
200
380
  attach_function :ftdi_setflowctrl, [ :pointer, :int ], :int
201
381
  attach_function :ftdi_write_data, [ :pointer, :pointer, :int ], :int
382
+ attach_function :ftdi_write_data_set_chunksize, [ :pointer, :uint ], :int
383
+ attach_function :ftdi_write_data_get_chunksize, [ :pointer, :pointer ], :int
384
+ attach_function :ftdi_read_data, [ :pointer, :pointer, :int ], :int
385
+ attach_function :ftdi_read_data_set_chunksize, [ :pointer, :uint ], :int
386
+ attach_function :ftdi_read_data_get_chunksize, [ :pointer, :pointer ], :int
387
+ attach_function :ftdi_set_interface, [ :pointer, Interface ], :int
202
388
  end
203
389
 
@@ -1,6 +1,5 @@
1
- # libftdi ruby bindings.
2
1
  module Ftdi
3
2
  # Gem version.
4
- VERSION = "0.0.2".freeze
3
+ VERSION = "0.0.3".freeze
5
4
  end
6
5
 
data/test.rb CHANGED
@@ -26,18 +26,19 @@ end
26
26
  begin
27
27
  ctx.usb_open(0x0403, 0x6001)
28
28
  begin
29
+ puts "Interface used: #{ctx.interface}"
30
+ puts "Read buffer chunk size: #{ctx.read_data_chunksize}"
31
+ puts "Write buffer chunk size: #{ctx.write_data_chunksize}"
29
32
  ctx.baudrate = BAUD_RATE
30
33
  ctx.set_line_property(:bits_8, :stop_bit_2, :none)
31
34
  ctx.flowctrl = Ftdi::SIO_DISABLE_FLOW_CTRL
32
35
 
33
- arr = [ 0 ]
34
- 512.times { arr << 1 }
36
+ arr = Array.new(513) { |i| i.zero? ? 0 : 1 }
35
37
  dmx_write(ctx, arr)
36
38
 
37
39
  sleep 1
38
40
 
39
- arr = [ 0 ]
40
- 512.times { arr << 0 }
41
+ arr = [ 0 ] * 513
41
42
  dmx_write(ctx, arr)
42
43
 
43
44
  puts "Context is:"
@@ -50,5 +51,4 @@ rescue Ftdi::Error => e
50
51
  $stderr.puts e.to_s
51
52
  end
52
53
 
53
- ctx.dispose
54
54
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libftdi-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-11 00:00:00.000000000 Z
12
+ date: 2012-04-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ffi
16
- requirement: &2156787020 !ruby/object:Gem::Requirement
16
+ requirement: &2153402300 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '1.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2156787020
24
+ version_requirements: *2153402300
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: yard
27
- requirement: &2156802300 !ruby/object:Gem::Requirement
27
+ requirement: &2153401800 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.7.5
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2156802300
35
+ version_requirements: *2153401800
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: redcarpet
38
- requirement: &2156801320 !ruby/object:Gem::Requirement
38
+ requirement: &2153401340 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: 1.17.2
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2156801320
46
+ version_requirements: *2153401340
47
47
  description: libftdi library bindings to talk to FTDI chips
48
48
  email:
49
49
  - akzhan.abdulin@gmail.com