ruby-adept 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.travis.yml +6 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +16 -0
- data/adept.gemspec +33 -0
- data/autotest/discover.rb +2 -0
- data/bin/bprog +110 -0
- data/firmware/.gitignore +73 -0
- data/firmware/epp_stream/Basys2_100_250General.ucf +21 -0
- data/firmware/epp_stream/epp_controller.vhd +210 -0
- data/firmware/epp_stream/epp_stream.xise +355 -0
- data/firmware/epp_stream/fifo.vhd +178 -0
- data/firmware/epp_stream/tests/fifo_testbench.vhdl +164 -0
- data/lib/adept/boards/basys2.rb +84 -0
- data/lib/adept/boards.rb +2 -0
- data/lib/adept/connection_provider.rb +30 -0
- data/lib/adept/data_formats/bitstream.rb +116 -0
- data/lib/adept/data_formats/data_factories.rb +33 -0
- data/lib/adept/data_formats.rb +2 -0
- data/lib/adept/device.rb +127 -0
- data/lib/adept/error.rb +4 -0
- data/lib/adept/jtag/connection.rb +404 -0
- data/lib/adept/jtag/device.rb +178 -0
- data/lib/adept/jtag/devices/fpga.rb +162 -0
- data/lib/adept/jtag/devices/null.rb +0 -0
- data/lib/adept/jtag/devices/platform_flash.rb +23 -0
- data/lib/adept/jtag/devices.rb +2 -0
- data/lib/adept/jtag/error.rb +8 -0
- data/lib/adept/jtag/tap_state.rb +67 -0
- data/lib/adept/jtag/tap_states.rb +52 -0
- data/lib/adept/jtag.rb +11 -0
- data/lib/adept/low_level/connection.rb +59 -0
- data/lib/adept/low_level/device.rb +43 -0
- data/lib/adept/low_level/device_error.rb +22 -0
- data/lib/adept/low_level/device_manager.rb +142 -0
- data/lib/adept/low_level/enhanced_parallel.rb +151 -0
- data/lib/adept/low_level/error_handler.rb +60 -0
- data/lib/adept/low_level/jtag.rb +379 -0
- data/lib/adept/low_level/library.rb +173 -0
- data/lib/adept/low_level.rb +4 -0
- data/lib/adept/version.rb +3 -0
- data/lib/adept.rb +11 -0
- data/spec/firmware/epp_loopback.bit +0 -0
- data/spec/lib/adept/data_formats/bitstream_spec.rb +95 -0
- data/spec/lib/adept/data_formats/data_factories_spec.rb +42 -0
- data/spec/lib/adept/device_spec.rb +88 -0
- data/spec/lib/adept/jtag/connection_spec.rb +433 -0
- data/spec/lib/adept/jtag/device_spec.rb +107 -0
- data/spec/lib/adept/jtag/devices/fpga_spec.rb +71 -0
- data/spec/lib/adept/low_level/enhanced_parallel_spec.rb +72 -0
- data/spec/lib/adept/low_level/jtag_spec.rb +204 -0
- data/spec/spec_helpers.rb +25 -0
- metadata +240 -0
@@ -0,0 +1,162 @@
|
|
1
|
+
#
|
2
|
+
# FPGA JTAG Target
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'adept/jtag'
|
6
|
+
|
7
|
+
module Adept
|
8
|
+
module JTAG
|
9
|
+
module Devices
|
10
|
+
|
11
|
+
#
|
12
|
+
# Base module for JTAG devices.
|
13
|
+
#
|
14
|
+
class FPGA < Device
|
15
|
+
|
16
|
+
#Basic device definitions.
|
17
|
+
InstructionWidth = 6
|
18
|
+
supports_idcode "X1c1a093", "X1c10093"
|
19
|
+
|
20
|
+
#Supported boundary-scan instructions.
|
21
|
+
Instructions = \
|
22
|
+
{
|
23
|
+
:extest => 0b001111,
|
24
|
+
:sample => 0b000001,
|
25
|
+
:preload => 0b000001, # Same as :sample
|
26
|
+
:user1 => 0b000010, # Not available until after configuration
|
27
|
+
:user2 => 0b000011, # Not available until after configuration
|
28
|
+
:cfg_out => 0b000100, # Not available during configuration with another mode.
|
29
|
+
:cfg_in => 0b000101, # Not available during configuration with another mode.
|
30
|
+
:intest => 0b000111,
|
31
|
+
:usercode => 0b001000,
|
32
|
+
:idcode => 0b001001,
|
33
|
+
:highz => 0b001010,
|
34
|
+
:jprogram => 0b001011, # Not available during configuration with another mode.
|
35
|
+
:jstart => 0b001100, # Not available during configuration with another mode.
|
36
|
+
:jshutdown => 0b001101, # Not available during configuration with another mode.
|
37
|
+
:bypass => 0b111111,
|
38
|
+
:isc_enable => 0b010000,
|
39
|
+
:isc_program => 0b010001,
|
40
|
+
:isc_noop => 0b010101,
|
41
|
+
:isc_disable => 0b010110
|
42
|
+
}
|
43
|
+
|
44
|
+
#Database which maps IDCodes to bit-file part numbers.
|
45
|
+
#Used to validate
|
46
|
+
PartIdcodes = \
|
47
|
+
{
|
48
|
+
'3s100ecp132' => 'X1c10093',
|
49
|
+
'3s250ecp132' => 'X1c1a093'
|
50
|
+
}
|
51
|
+
|
52
|
+
ConfigurationStartup = 14_000
|
53
|
+
FPGAStartup = 100
|
54
|
+
|
55
|
+
#
|
56
|
+
# Verifies the device's IDcode using the explicit IDCode instruction.
|
57
|
+
#
|
58
|
+
def verify_idcode
|
59
|
+
|
60
|
+
#Put the device into IDCode retrival mode.
|
61
|
+
self.instruction = :idcode
|
62
|
+
|
63
|
+
#And attempt to retrieve the 32-bit IDcode.
|
64
|
+
id_code = receive_data(32).reverse
|
65
|
+
|
66
|
+
#If the two IDcodes don't match, raise an error.
|
67
|
+
raise JTAG::Error, "IDCode verification failed! Expected: #{@idcode.unpack("H*")}, receieved #{id_code.unpack("H*")}. " unless id_code == @idcode
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Returns the "user code", an ID number which identifies the configuration of the FPGA.
|
73
|
+
#
|
74
|
+
def usercode
|
75
|
+
|
76
|
+
#Put the device into IDCode retrival mode.
|
77
|
+
self.instruction = :usercode
|
78
|
+
|
79
|
+
#And attempt to retrieve the 32-bit IDcode.
|
80
|
+
usercode_packed = receive_data(32).reverse
|
81
|
+
|
82
|
+
#Return the usercode as a hex string.
|
83
|
+
usercode_packed.unpack("H*").first.upcase
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Configures (programs) the given FPGA.
|
89
|
+
#
|
90
|
+
def configure(bitstream)
|
91
|
+
|
92
|
+
validate_bitstream(bitstream)
|
93
|
+
|
94
|
+
#Send the bitstream to the FPGA.
|
95
|
+
initialize_configuration
|
96
|
+
transmit_data(bitstream.to_s)
|
97
|
+
finalize_configuration
|
98
|
+
|
99
|
+
#And verify that the programming succeeded.
|
100
|
+
unless bitstream.usercode == usercode || bitstrea.usercode.nil?
|
101
|
+
raise ProgrammingError, "Programming failed; expected a usercode of #{bitstream.usercode}, recieved #{usercode}."
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
def part_name
|
107
|
+
connected_part, _ = PartIdcodes.find { |part, mask| self.class.idcode_matches_mask(mask, @idcode) }
|
108
|
+
connected_part
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Returns true iff the provided bitstream is intended for this FPGA.
|
113
|
+
#
|
114
|
+
def supports_bitstream?(bitstream)
|
115
|
+
self.class.idcode_matches_mask(PartIdcodes[bitstream.part], @idcode)
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
#
|
122
|
+
# Check to ensure that the provided bit-stream is intended for this part.
|
123
|
+
#
|
124
|
+
def validate_bitstream(bitstream)
|
125
|
+
unless supports_bitstream?(bitstream)
|
126
|
+
raise ProgrammingError, "The provided bitstream was intended for an '#{bitstream.part}', but a '#{part_name}' is connected."
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
#
|
131
|
+
# Performs the initial steps which ready the FPGA for configuration.
|
132
|
+
#
|
133
|
+
def initialize_configuration
|
134
|
+
|
135
|
+
#Pulse the Program pin via JTAG.
|
136
|
+
self.instruction = :jprogram
|
137
|
+
|
138
|
+
#Put the device into configuration mode, and give it 14,000 cycles to start up.
|
139
|
+
self.instruction = :cfg_in
|
140
|
+
run_test(ConfigurationStartup)
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# Performs the final steps which start up a configured program.
|
146
|
+
#
|
147
|
+
def finalize_configuration
|
148
|
+
|
149
|
+
#Put the FPGA into startup mode...
|
150
|
+
self.instruction = :jstart
|
151
|
+
|
152
|
+
#And then allow the FPGA to run normally.
|
153
|
+
run_test(FPGAStartup)
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
File without changes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#
|
2
|
+
# FPGA Configuration Platform Flash
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'adept/jtag'
|
6
|
+
|
7
|
+
module Adept
|
8
|
+
module JTAG
|
9
|
+
module Devices
|
10
|
+
|
11
|
+
#
|
12
|
+
# Base module for JTAG devices.
|
13
|
+
#
|
14
|
+
class PlatformFlash < Device
|
15
|
+
|
16
|
+
InstructionWidth = 8
|
17
|
+
supports_idcode "X5045093"
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
|
2
|
+
module Adept
|
3
|
+
module JTAG
|
4
|
+
module TAPStates
|
5
|
+
|
6
|
+
#
|
7
|
+
# Base for all Test Access Port states.
|
8
|
+
#
|
9
|
+
module TAPState
|
10
|
+
|
11
|
+
#
|
12
|
+
# Returns the successor to the current state, given an input value.
|
13
|
+
#
|
14
|
+
def next_state(value)
|
15
|
+
|
16
|
+
#Get the name of the module which describes the successor state...
|
17
|
+
state = self::NextState[value]
|
18
|
+
|
19
|
+
#... and get a reference to the module itself.
|
20
|
+
TAPStates.const_get(state)
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Determines the next value which should be present on the mode-set line
|
26
|
+
# to get to the given state.
|
27
|
+
#
|
28
|
+
def next_hop_towards(state)
|
29
|
+
|
30
|
+
#Get a refrence to the state module indicated by the TowardsZeroIfStateIs metaconstant.
|
31
|
+
towards_zero = TAPStates.const_get(self::NextState[0])
|
32
|
+
towards_one = TAPStates.const_get(self::NextState[1])
|
33
|
+
|
34
|
+
#Determine if a next-hop of one would cause us to get stuck in a loop.
|
35
|
+
towards_one_would_cause_loop = (towards_one == self || self::NextState[1] == :Reset)
|
36
|
+
|
37
|
+
#If the next state would be achievable by providing a hop of zero,
|
38
|
+
#or a hop of one would cause a loop, then the next hop should be zero.
|
39
|
+
#
|
40
|
+
#Otherwise, the next hop should be '1'.
|
41
|
+
((state == towards_zero) || towards_one_would_cause_loop) ? [0, towards_zero] : [1, towards_one]
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Dynamically create a new TAPState module.
|
48
|
+
#
|
49
|
+
def self.tap_state(name, next_state)
|
50
|
+
|
51
|
+
#Create a new Module for the new TAP state...
|
52
|
+
mod = Module.new
|
53
|
+
|
54
|
+
#... and assign it the given name.
|
55
|
+
self.const_set(name, mod)
|
56
|
+
|
57
|
+
#Add the TAPState methods to the new module...
|
58
|
+
mod.extend(TAPState)
|
59
|
+
|
60
|
+
#And set the module's NextState constant, as given.
|
61
|
+
mod.const_set(:NextState, next_state)
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
require 'adept/jtag/tap_state'
|
3
|
+
|
4
|
+
module Adept
|
5
|
+
module JTAG
|
6
|
+
module TAPStates
|
7
|
+
|
8
|
+
#
|
9
|
+
# JTAG Test Access Port Finite State Machine
|
10
|
+
#
|
11
|
+
|
12
|
+
tap_state :Reset, 0 => :Idle, 1 => :Reset
|
13
|
+
tap_state :Idle, 0 => :Idle, 1 => :SelectDR
|
14
|
+
|
15
|
+
#Data Register Access States
|
16
|
+
tap_state :SelectDR, 0 => :CaptureDR, 1 => :SelectIR
|
17
|
+
tap_state :CaptureDR, 0 => :ShiftDR, 1 => :Exit1DR
|
18
|
+
tap_state :ShiftDR, 0 => :ShiftDR, 1 => :Exit1DR
|
19
|
+
tap_state :Exit1DR, 0 => :PauseDR, 1 => :UpdateDR
|
20
|
+
tap_state :PauseDR, 0 => :PauseDR, 1 => :Exit2DR
|
21
|
+
tap_state :Exit2DR, 0 => :ShiftDR, 1 => :UpdateDR
|
22
|
+
tap_state :UpdateDR, 0 => :Idle, 1 => :SelectDR
|
23
|
+
|
24
|
+
#Instruction Register Access States
|
25
|
+
tap_state :SelectIR, 0 => :CaptureIR, 1 => :Reset
|
26
|
+
tap_state :CaptureIR, 0 => :ShiftIR, 1 => :Exit1IR
|
27
|
+
tap_state :ShiftIR, 0 => :ShiftIR, 1 => :Exit1IR
|
28
|
+
tap_state :Exit1IR, 0 => :PauseIR, 1 => :UpdateIR
|
29
|
+
tap_state :PauseIR, 0 => :PauseIR, 1 => :Exit2IR
|
30
|
+
tap_state :Exit2IR, 0 => :ShiftIR, 1 => :UpdateIR
|
31
|
+
tap_state :UpdateIR, 0 => :Idle, 1 => :SelectDR
|
32
|
+
|
33
|
+
|
34
|
+
#
|
35
|
+
# Override the FSM's base behavior in the SelectDR state, as that state
|
36
|
+
# is the "branch point" between the instruction register and the data-register columns.
|
37
|
+
#
|
38
|
+
module SelectDR
|
39
|
+
|
40
|
+
#
|
41
|
+
# Override the heuristic for path-finding within the TAP finite state machine;
|
42
|
+
# move to the appropriate column depending on if we're looking for an Data Register
|
43
|
+
# or Instruction Register access.
|
44
|
+
#
|
45
|
+
def self.next_hop_towards(state)
|
46
|
+
#If we're looking for an instruction state, continue to the next column.
|
47
|
+
(state.name =~ /IR$/) ? 1 : 0
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/adept/jtag.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
module Adept
|
5
|
+
module LowLevel
|
6
|
+
|
7
|
+
#
|
8
|
+
# Module mix-in which adds the basic functionality for
|
9
|
+
#
|
10
|
+
module Connection
|
11
|
+
|
12
|
+
#
|
13
|
+
# Hook which runs whenever a module extends AdeptConnection.
|
14
|
+
#
|
15
|
+
def self.extended(base)
|
16
|
+
|
17
|
+
#Attach each of the relevant functions to the extending class.
|
18
|
+
base.module_eval do
|
19
|
+
|
20
|
+
#Returns the amount of connections the given port provides.
|
21
|
+
attach_adept_function :GetPortCount, [:ulong, :pointer]
|
22
|
+
|
23
|
+
#Enable the JTAG port with the given number. Only one JTAG device can be active at a time!
|
24
|
+
attach_adept_function :EnableEx, [:ulong, :int32]
|
25
|
+
|
26
|
+
#Disable the currently active JTAG port.
|
27
|
+
attach_adept_function :Disable, [:ulong]
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Returns the number of JTAG ports the given device offers.
|
34
|
+
#
|
35
|
+
def port_count(device)
|
36
|
+
|
37
|
+
#Create a pointer to a new Int32 in memory...
|
38
|
+
count_pointer = FFI::MemoryPointer.new(:int32)
|
39
|
+
|
40
|
+
#... and fill it with the number of available JTAG ports.
|
41
|
+
GetPortCount(device, count_pointer)
|
42
|
+
|
43
|
+
#Return the acquired count as a ruby integer.
|
44
|
+
count_pointer.get_int32(0)
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Returns a true-like value if the given device supports JTAG,
|
50
|
+
# or nil if it does not.
|
51
|
+
#
|
52
|
+
def supported?(device)
|
53
|
+
port_count(device).nonzero?
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'adept/low_level/device_manager'
|
3
|
+
|
4
|
+
module Adept
|
5
|
+
module LowLevel
|
6
|
+
|
7
|
+
#Maximum length for an Adept device's name, including a null terminator.
|
8
|
+
NameMaxLength = 64
|
9
|
+
|
10
|
+
#Maximum length for an Adept device path, including a null terminator.
|
11
|
+
PathMaxLength = 260 + 1
|
12
|
+
|
13
|
+
#
|
14
|
+
# Structure used by the Adept SDK to represent an adept device.
|
15
|
+
#
|
16
|
+
class Device < FFI::Struct
|
17
|
+
|
18
|
+
#Set up the structure's layout.
|
19
|
+
layout :name, [:char, NameMaxLength], #char[NameMaxLength]
|
20
|
+
:path, [:char, PathMaxLength], #char[PathMaxLength]
|
21
|
+
:transport, :ulong
|
22
|
+
|
23
|
+
#
|
24
|
+
#Convert the given device record into a ruby hash.
|
25
|
+
#
|
26
|
+
def to_h
|
27
|
+
result = Hash[members.zip(values)]
|
28
|
+
|
29
|
+
#Convert the internal character arrays to ruby strings.
|
30
|
+
result[:name] = result[:name].to_s
|
31
|
+
result[:path] = result[:path].to_s
|
32
|
+
|
33
|
+
#And convert the device's transport to a string.
|
34
|
+
result[:transport] = DeviceManager::get_transport_name(result[:transport])
|
35
|
+
|
36
|
+
#return the resultant hash
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module Adept
|
3
|
+
module LowLevel
|
4
|
+
|
5
|
+
#
|
6
|
+
# Wrapper for Digilent Device Manager errors, which allows
|
7
|
+
# errors raised from the low-level SDK to be handled like ruby exceptions.
|
8
|
+
#
|
9
|
+
class DeviceError < StandardError
|
10
|
+
|
11
|
+
attr_accessor :short_name
|
12
|
+
attr_accessor :code
|
13
|
+
|
14
|
+
def initialize(message, short_name, code=nil)
|
15
|
+
@short_name = short_name
|
16
|
+
@code = code
|
17
|
+
super(message)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'adept/low_level/device'
|
3
|
+
require 'adept/low_level/library'
|
4
|
+
|
5
|
+
module Adept
|
6
|
+
module LowLevel
|
7
|
+
|
8
|
+
#Stores the maximum length of a transport string, will terminating null.
|
9
|
+
TransportMaxLength = 16
|
10
|
+
|
11
|
+
#
|
12
|
+
# DeviceManager (DMGR)
|
13
|
+
# Wrapper for the low-level Adept device management functionality.
|
14
|
+
#
|
15
|
+
module DeviceManager
|
16
|
+
extend LowLevel::Library
|
17
|
+
|
18
|
+
#Wrap the device manager library, libDMGR
|
19
|
+
wrap_adept_library 'dmgr'
|
20
|
+
|
21
|
+
|
22
|
+
#
|
23
|
+
# Enumeration functions.
|
24
|
+
#
|
25
|
+
|
26
|
+
#Get the list of all detected Adept devices; should be followed by the accompanying FreeDvcEnum call.
|
27
|
+
attach_adept_function :EnumDevices, [:pointer]
|
28
|
+
|
29
|
+
#Free the internal list of connected devices.
|
30
|
+
attach_adept_function :FreeDvcEnum, []
|
31
|
+
|
32
|
+
|
33
|
+
#
|
34
|
+
# Populate the internal list of connected devices.
|
35
|
+
# Returns the total amount of devices enumerated.
|
36
|
+
#
|
37
|
+
def self.populate_device_list
|
38
|
+
|
39
|
+
#Create a pointer to a new C integer.
|
40
|
+
count_pointer = FFI::MemoryPointer.new(:int)
|
41
|
+
|
42
|
+
#Enumerate all of the connected devices, and retrieve the amount of enumerated devices.
|
43
|
+
EnumDevices(count_pointer)
|
44
|
+
|
45
|
+
#Dereference the count pointer, extracting the number of devices present.
|
46
|
+
count_pointer.get_int(0)
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Free the internal list of connected devices.
|
52
|
+
#
|
53
|
+
def self.free_device_list
|
54
|
+
FreeDvcEnum()
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
#
|
59
|
+
# Device Information-Query Functions
|
60
|
+
#
|
61
|
+
|
62
|
+
#Get all enumeration information regarding the current device.
|
63
|
+
attach_adept_function :GetDvc, [:int, :pointer]
|
64
|
+
|
65
|
+
#Get a string describing the transport
|
66
|
+
attach_adept_function :GetDtpString, [:ulong, :pointer]
|
67
|
+
|
68
|
+
#
|
69
|
+
# Returns a single record from the internal device enumeration list.
|
70
|
+
# This record contains low-level information about how to connect to the device.
|
71
|
+
#
|
72
|
+
def self.get_device_info(device_number)
|
73
|
+
|
74
|
+
#Create a new, empty low-level device structure.
|
75
|
+
device = Device.new
|
76
|
+
|
77
|
+
#Get the device's information from the internal enumeration table.
|
78
|
+
GetDvc(device_number, device.pointer)
|
79
|
+
|
80
|
+
#And return the newly-fetched device object as a ruby hash.
|
81
|
+
device.to_h
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Returns the transport name for a given transport ID ("DTP").
|
87
|
+
#
|
88
|
+
def self.get_transport_name(transport_id)
|
89
|
+
|
90
|
+
#Create a string buffer for the transport's name...
|
91
|
+
string_pointer = FFI::MemoryPointer.new(TransportMaxLength)
|
92
|
+
|
93
|
+
#... and fill it with the relevant transport's description.
|
94
|
+
GetDtpString(transport_id, string_pointer)
|
95
|
+
|
96
|
+
#Return the retrieved string.
|
97
|
+
string_pointer.read_string
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Device Open/Close Functions
|
103
|
+
#
|
104
|
+
|
105
|
+
#Open the device at the specified path.
|
106
|
+
attach_adept_function :Open, [:pointer, :string]
|
107
|
+
|
108
|
+
#Close the device with the specified handle.
|
109
|
+
attach_adept_function :Close, [:ulong]
|
110
|
+
|
111
|
+
#
|
112
|
+
# Opens the device at the given path, and returns an interface handle.
|
113
|
+
#
|
114
|
+
def self.open_device(path)
|
115
|
+
|
116
|
+
#Create a pointer to a new C long.
|
117
|
+
handle_pointer = FFI::MemoryPointer.new(:ulong)
|
118
|
+
|
119
|
+
#Open the device at the given path, retrieving the newly-created device handle.
|
120
|
+
Open(handle_pointer, path)
|
121
|
+
|
122
|
+
#Dereference the handle pointer, retrieving the handle itself.
|
123
|
+
handle = handle_pointer.get_ulong(0)
|
124
|
+
|
125
|
+
#If we recieved a handle of zero (C's NULL), convert that to nil;
|
126
|
+
#otherwise, return the handle directly.
|
127
|
+
handle.zero?() ? nil : handle
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
# Closes the device which is referenced by the given interface handle.
|
133
|
+
#
|
134
|
+
def self.close_device(handle)
|
135
|
+
Close(handle)
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|