ruby-adept 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|