line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# You may distribute under the terms of either the GNU General Public License |
2
|
|
|
|
|
|
|
# or the Artistic License (the same terms as Perl itself) |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# (C) Paul Evans, 2015-2021 -- leonerd@leonerd.org.uk |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Device::Chip::Adapter::BusPirate 0.22; |
7
|
|
|
|
|
|
|
|
8
|
2
|
|
|
2
|
|
3929
|
use v5.14; |
|
2
|
|
|
|
|
9
|
|
9
|
2
|
|
|
2
|
|
14
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
79
|
|
10
|
2
|
|
|
2
|
|
13
|
use base qw( Device::Chip::Adapter ); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
1251
|
|
11
|
|
|
|
|
|
|
|
12
|
2
|
|
|
2
|
|
26222
|
use Carp; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
107
|
|
13
|
|
|
|
|
|
|
|
14
|
2
|
|
|
2
|
|
14
|
use Future::AsyncAwait; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
9
|
|
15
|
2
|
|
|
2
|
|
73
|
use Future::Mutex; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
47
|
|
16
|
|
|
|
|
|
|
|
17
|
2
|
|
|
2
|
|
9
|
use Device::BusPirate; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
1727
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 NAME |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
C - a C implementation |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 DESCRIPTION |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
This class implements the L interface for the |
26
|
|
|
|
|
|
|
I, allowing an instance of a L driver to communicate |
27
|
|
|
|
|
|
|
with the actual chip hardware by using the I as a hardware |
28
|
|
|
|
|
|
|
adapter. |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=cut |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=cut |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=head2 new |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
$adapter = Device::Chip::Adapter::BusPirate->new( %args ) |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
Returns a new instance of a C. Takes the |
41
|
|
|
|
|
|
|
same named arguments as L. |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=cut |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub new |
46
|
|
|
|
|
|
|
{ |
47
|
1
|
|
|
1
|
1
|
97
|
my $class = shift; |
48
|
|
|
|
|
|
|
|
49
|
1
|
|
|
|
|
9
|
my $bp = Device::BusPirate->new( @_ ); |
50
|
|
|
|
|
|
|
|
51
|
1
|
|
|
|
|
7
|
bless { |
52
|
|
|
|
|
|
|
bp => $bp, |
53
|
|
|
|
|
|
|
mode => undef, |
54
|
|
|
|
|
|
|
}, $class; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub new_from_description |
58
|
|
|
|
|
|
|
{ |
59
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
60
|
0
|
|
|
|
|
0
|
my %args = @_; |
61
|
|
|
|
|
|
|
# Whitelist known-OK constructor args |
62
|
0
|
|
|
|
|
0
|
$class->new( map { $_ => $args{$_} } qw( serial baud ) ); |
|
0
|
|
|
|
|
0
|
|
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head1 METHODS |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
This module provides no new methods beyond the basic API documented in |
68
|
|
|
|
|
|
|
L at version 0.01. |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
Since version I this module now supports multiple instances of the I2C |
71
|
|
|
|
|
|
|
protocol, allowing multiple chips to be shared on the same bus. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=cut |
74
|
|
|
|
|
|
|
|
75
|
1
|
|
|
1
|
|
20
|
sub _modename { return ( ref($_[0]) =~ m/.*::(.*?)$/ )[0] } |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
async sub make_protocol_GPIO |
78
|
0
|
|
|
0
|
0
|
0
|
{ |
79
|
0
|
|
|
|
|
0
|
my $self = shift; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
$self->{mode} and |
82
|
0
|
0
|
|
|
|
0
|
croak "Cannot enter GPIO protocol when " . _modename( $self->{mode} ) . " already active"; |
83
|
|
|
|
|
|
|
|
84
|
0
|
|
|
|
|
0
|
my $mode = await $self->{bp}->enter_mode( "BB" ); |
85
|
0
|
|
|
|
|
0
|
$self->{mode} = $mode; |
86
|
|
|
|
|
|
|
|
87
|
0
|
|
|
|
|
0
|
await $mode->configure( open_drain => 0 ); |
88
|
|
|
|
|
|
|
|
89
|
0
|
|
|
|
|
0
|
return Device::Chip::Adapter::BusPirate::_GPIO->new( $mode ); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
async sub make_protocol_SPI |
93
|
0
|
|
|
0
|
0
|
0
|
{ |
94
|
0
|
|
|
|
|
0
|
my $self = shift; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
$self->{mode} and |
97
|
0
|
0
|
|
|
|
0
|
croak "Cannot enter SPI protocol when " . _modename( $self->{mode} ) . " already active"; |
98
|
|
|
|
|
|
|
|
99
|
0
|
|
|
|
|
0
|
my $mode = await $self->{bp}->enter_mode( "SPI" ); |
100
|
0
|
|
|
|
|
0
|
$self->{mode} = $mode; |
101
|
|
|
|
|
|
|
|
102
|
0
|
|
|
|
|
0
|
await $mode->configure( open_drain => 0 ); |
103
|
|
|
|
|
|
|
|
104
|
0
|
|
|
|
|
0
|
return Device::Chip::Adapter::BusPirate::_SPI->new( $mode ); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
async sub _enter_mode_I2C |
108
|
2
|
|
|
2
|
|
4
|
{ |
109
|
2
|
|
|
|
|
5
|
my $self = shift; |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
return $self->{mode} if |
112
|
2
|
100
|
66
|
|
|
16
|
$self->{mode} and _modename( $self->{mode} ) eq "I2C"; |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
$self->{mode} and |
115
|
1
|
50
|
|
|
|
4
|
croak "Cannot enter I2C protocol when " . _modename( $self->{mode} ) . " already active"; |
116
|
|
|
|
|
|
|
|
117
|
1
|
|
|
|
|
5
|
my $mode = await $self->{bp}->enter_mode( "I2C" ); |
118
|
1
|
|
|
|
|
138
|
$self->{mode} = $mode; |
119
|
|
|
|
|
|
|
|
120
|
1
|
|
|
|
|
6
|
await $mode->configure( open_drain => 1 ); |
121
|
|
|
|
|
|
|
|
122
|
1
|
|
|
|
|
33
|
return $mode; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
async sub make_protocol_I2C |
126
|
2
|
|
|
2
|
0
|
1146
|
{ |
127
|
2
|
|
|
|
|
5
|
my $self = shift; |
128
|
|
|
|
|
|
|
|
129
|
2
|
|
|
|
|
11
|
my $mode = await $self->_enter_mode_I2C; |
130
|
|
|
|
|
|
|
|
131
|
2
|
|
66
|
|
|
129
|
my $mutex = $self->{mutex} //= Future::Mutex->new; |
132
|
|
|
|
|
|
|
|
133
|
2
|
|
|
|
|
34
|
return Device::Chip::Adapter::BusPirate::_I2C->new( $mode, $mutex ); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
async sub make_protocol_UART |
137
|
0
|
|
|
0
|
0
|
0
|
{ |
138
|
0
|
|
|
|
|
0
|
my $self = shift; |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
$self->{mode} and |
141
|
0
|
0
|
|
|
|
0
|
croak "Cannot enter UART protocol when " . _modename( $self->{mode} ) . " already active"; |
142
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
0
|
my $mode = await $self->{bp}->enter_mode( "UART" ); |
144
|
0
|
|
|
|
|
0
|
$self->{mode} = $mode; |
145
|
|
|
|
|
|
|
|
146
|
0
|
|
|
|
|
0
|
await $mode->configure( open_drain => 0 ); |
147
|
|
|
|
|
|
|
|
148
|
0
|
|
|
|
|
0
|
return Device::Chip::Adapter::BusPirate::_UART->new( $mode ); |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
sub shutdown |
152
|
|
|
|
|
|
|
{ |
153
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
154
|
0
|
|
|
|
|
0
|
$self->{mode}->power( 0 )->get; |
155
|
0
|
|
|
|
|
0
|
$self->{bp}->stop; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
package |
159
|
|
|
|
|
|
|
Device::Chip::Adapter::BusPirate::_base; |
160
|
|
|
|
|
|
|
|
161
|
2
|
|
|
2
|
|
17
|
use Carp; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
132
|
|
162
|
2
|
|
|
2
|
|
13
|
use List::Util qw( first ); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
1654
|
|
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
sub new |
165
|
|
|
|
|
|
|
{ |
166
|
2
|
|
|
2
|
|
5
|
my $class = shift; |
167
|
2
|
|
|
|
|
5
|
my ( $mode, $mutex ) = @_; |
168
|
|
|
|
|
|
|
|
169
|
2
|
|
|
|
|
15
|
bless { mode => $mode, mutex => $mutex }, $class; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub sleep |
173
|
|
|
|
|
|
|
{ |
174
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
175
|
0
|
|
|
|
|
0
|
$self->{mode}->pirate->sleep( @_ ); |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
sub power |
179
|
|
|
|
|
|
|
{ |
180
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
181
|
0
|
|
|
|
|
0
|
$self->{mode}->power( @_ ); |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub _find_speed |
185
|
|
|
|
|
|
|
{ |
186
|
1
|
|
|
1
|
|
2
|
shift; |
187
|
1
|
|
|
|
|
4
|
my ( $max_bitrate, @speeds ) = @_; |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
return first { |
190
|
2
|
|
|
2
|
|
5
|
my $rate = $_; |
191
|
2
|
50
|
|
|
|
17
|
$rate =~ m/(.*)k$/ and $rate = 1E3 * $1; |
192
|
2
|
50
|
|
|
|
8
|
$rate =~ m/(.*)M$/ and $rate = 1E6 * $1; |
193
|
|
|
|
|
|
|
|
194
|
2
|
|
|
|
|
8
|
$rate <= $max_bitrate |
195
|
1
|
|
|
|
|
10
|
} @speeds; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# Most modes only have access to the AUX GPIO pin |
199
|
0
|
|
|
0
|
|
0
|
sub list_gpios { return qw( AUX ) } |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
sub meta_gpios |
202
|
|
|
|
|
|
|
{ |
203
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
204
|
|
|
|
|
|
|
|
205
|
0
|
|
|
|
|
0
|
return map { Device::Chip::Adapter::GPIODefinition( $_, "rw", 0 ) } |
|
0
|
|
|
|
|
0
|
|
206
|
|
|
|
|
|
|
$self->list_gpios; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
sub write_gpios |
210
|
|
|
|
|
|
|
{ |
211
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
212
|
0
|
|
|
|
|
0
|
my ( $gpios ) = @_; |
213
|
|
|
|
|
|
|
|
214
|
0
|
|
|
|
|
0
|
my $mode = $self->{mode}; |
215
|
|
|
|
|
|
|
|
216
|
0
|
|
|
|
|
0
|
foreach my $pin ( keys %$gpios ) { |
217
|
0
|
0
|
|
|
|
0
|
$pin eq "AUX" or |
218
|
|
|
|
|
|
|
croak "Unrecognised GPIO pin name $pin"; |
219
|
|
|
|
|
|
|
|
220
|
0
|
|
|
|
|
0
|
return $mode->aux( $gpios->{$pin} ); |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
0
|
|
|
|
|
0
|
Future->done; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub read_gpios |
227
|
|
|
|
|
|
|
{ |
228
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
229
|
0
|
|
|
|
|
0
|
my ( $gpios ) = @_; |
230
|
|
|
|
|
|
|
|
231
|
0
|
|
|
|
|
0
|
my $mode = $self->{mode}; |
232
|
|
|
|
|
|
|
|
233
|
0
|
|
|
|
|
0
|
my @f; |
234
|
0
|
|
|
|
|
0
|
foreach my $pin ( @$gpios ) { |
235
|
0
|
0
|
|
|
|
0
|
$pin eq "AUX" or |
236
|
|
|
|
|
|
|
croak "Unrecognised GPIO pin name $pin"; |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
return $mode->read_aux |
239
|
0
|
|
|
0
|
|
0
|
->transform( done => sub { { AUX => $_[0] } } ); |
|
0
|
|
|
|
|
0
|
|
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
0
|
|
|
|
|
0
|
Future->done( {} ); |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# there's no more efficient way to tris_gpios than just read and ignore the result |
246
|
|
|
|
|
|
|
async sub tris_gpios |
247
|
0
|
|
|
0
|
|
0
|
{ |
248
|
0
|
|
|
|
|
0
|
my $self = shift; |
249
|
0
|
|
|
|
|
0
|
await $self->read_gpios; |
250
|
0
|
|
|
|
|
0
|
return; |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
package |
254
|
|
|
|
|
|
|
Device::Chip::Adapter::BusPirate::_GPIO; |
255
|
2
|
|
|
2
|
|
20
|
use base qw( Device::Chip::Adapter::BusPirate::_base ); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
956
|
|
256
|
|
|
|
|
|
|
|
257
|
2
|
|
|
2
|
|
17
|
use List::Util 1.29 qw( pairmap ); |
|
2
|
|
|
|
|
41
|
|
|
2
|
|
|
|
|
751
|
|
258
|
|
|
|
|
|
|
|
259
|
0
|
|
|
0
|
|
0
|
sub list_gpios { return qw( MISO CS MOSI CLK AUX ) } |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub write_gpios |
262
|
|
|
|
|
|
|
{ |
263
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
264
|
0
|
|
|
|
|
0
|
my ( $gpios ) = @_; |
265
|
|
|
|
|
|
|
|
266
|
0
|
|
|
|
|
0
|
my $mode = $self->{mode}; |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
# TODO: validity checking |
269
|
|
|
|
|
|
|
$mode->write( |
270
|
0
|
|
|
0
|
|
0
|
pairmap { lc $a => $b } %$gpios |
|
0
|
|
|
|
|
0
|
|
271
|
|
|
|
|
|
|
) |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
async sub read_gpios |
275
|
0
|
|
|
0
|
|
0
|
{ |
276
|
0
|
|
|
|
|
0
|
my $self = shift; |
277
|
0
|
|
|
|
|
0
|
my ( $gpios ) = @_; |
278
|
|
|
|
|
|
|
|
279
|
0
|
|
|
|
|
0
|
my $mode = $self->{mode}; |
280
|
|
|
|
|
|
|
|
281
|
0
|
|
|
|
|
0
|
my $vals = await $mode->read( map { lc $_ } @$gpios ); |
282
|
|
|
|
|
|
|
|
283
|
0
|
|
|
0
|
|
0
|
return { pairmap { uc $a => $b } %$vals }; |
|
0
|
|
|
|
|
0
|
|
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
package |
287
|
|
|
|
|
|
|
Device::Chip::Adapter::BusPirate::_SPI; |
288
|
2
|
|
|
2
|
|
70
|
use base qw( Device::Chip::Adapter::BusPirate::_base Device::Chip::ProtocolBase::SPI ); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
1696
|
|
289
|
|
|
|
|
|
|
|
290
|
2
|
|
|
2
|
|
2941
|
use Carp; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
666
|
|
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
my @SPI_SPEEDS = (qw( 8M 4M 2.6M 2M 1M 250k 125k 30k )); |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
sub configure |
295
|
|
|
|
|
|
|
{ |
296
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
297
|
0
|
|
|
|
|
0
|
my %args = @_; |
298
|
|
|
|
|
|
|
|
299
|
0
|
|
|
|
|
0
|
my $mode = delete $args{mode}; |
300
|
0
|
|
|
|
|
0
|
my $max_bitrate = delete $args{max_bitrate}; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
croak "Cannot support SPI wordsize other than 8" |
303
|
0
|
0
|
0
|
|
|
0
|
if ( $args{wordsize} // 8 ) != 8; |
304
|
|
|
|
|
|
|
|
305
|
0
|
0
|
|
|
|
0
|
croak "Unrecognised configuration options: " . join( ", ", keys %args ) |
306
|
|
|
|
|
|
|
if %args; |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
$self->{mode}->configure( |
309
|
0
|
0
|
|
|
|
0
|
( defined $mode ? |
|
|
0
|
|
|
|
|
|
310
|
|
|
|
|
|
|
( mode => $mode ) : () ), |
311
|
|
|
|
|
|
|
( defined $max_bitrate ? |
312
|
|
|
|
|
|
|
( speed => $self->_find_speed( $max_bitrate, @SPI_SPEEDS ) ) : () ), |
313
|
|
|
|
|
|
|
); |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
sub readwrite |
317
|
|
|
|
|
|
|
{ |
318
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
319
|
0
|
|
|
|
|
0
|
my ( $data ) = @_; |
320
|
|
|
|
|
|
|
|
321
|
0
|
|
|
|
|
0
|
$self->{mode}->writeread_cs( $data ); |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
sub readwrite_no_ss |
325
|
|
|
|
|
|
|
{ |
326
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
327
|
0
|
|
|
|
|
0
|
my ( $data ) = @_; |
328
|
|
|
|
|
|
|
|
329
|
0
|
|
|
|
|
0
|
$self->{mode}->writeread( $data ); |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
sub assert_ss |
333
|
|
|
|
|
|
|
{ |
334
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
335
|
0
|
|
|
|
|
0
|
$self->{mode}->chip_select( 0 ); |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
sub release_ss |
339
|
|
|
|
|
|
|
{ |
340
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
341
|
0
|
|
|
|
|
0
|
$self->{mode}->chip_select( 1 ); |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
package |
345
|
|
|
|
|
|
|
Device::Chip::Adapter::BusPirate::_I2C; |
346
|
2
|
|
|
2
|
|
15
|
use base qw( Device::Chip::Adapter::BusPirate::_base ); |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
672
|
|
347
|
|
|
|
|
|
|
|
348
|
2
|
|
|
2
|
|
16
|
use Carp; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
2225
|
|
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
my @I2C_SPEEDS = (qw( 400k 100k 50k 5k )); |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# TODO - addr ought to be a mount option somehow |
353
|
|
|
|
|
|
|
sub configure |
354
|
|
|
|
|
|
|
{ |
355
|
2
|
|
|
2
|
|
962
|
my $self = shift; |
356
|
2
|
|
|
|
|
9
|
my %args = @_; |
357
|
|
|
|
|
|
|
|
358
|
2
|
|
|
|
|
6
|
my $addr = delete $args{addr}; |
359
|
2
|
|
|
|
|
6
|
my $max_bitrate = delete $args{max_bitrate}; |
360
|
|
|
|
|
|
|
|
361
|
2
|
50
|
|
|
|
7
|
croak "Unrecognised configuration options: " . join( ", ", keys %args ) |
362
|
|
|
|
|
|
|
if %args; |
363
|
|
|
|
|
|
|
|
364
|
2
|
50
|
|
|
|
12
|
$self->{addr} = $addr if defined $addr; |
365
|
|
|
|
|
|
|
|
366
|
2
|
|
|
|
|
4
|
my @f; |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
push @f, $self->{mode}->configure( |
369
|
2
|
100
|
|
|
|
14
|
speed => $self->_find_speed( $max_bitrate, @I2C_SPEEDS ) |
370
|
|
|
|
|
|
|
) if defined $max_bitrate; |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
# It's highly likely the user will want the pullups enabled here |
373
|
2
|
|
|
|
|
307
|
push @f, $self->{mode}->pullup( 1 ); |
374
|
|
|
|
|
|
|
|
375
|
2
|
|
|
|
|
661
|
Future->needs_all( @f ); |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub DESTROY |
379
|
|
|
|
|
|
|
{ |
380
|
2
|
|
|
2
|
|
1513
|
my $self = shift; |
381
|
2
|
50
|
|
|
|
13
|
$self->{mode}->pullup( 0 )->get if $self->{mode}; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
async sub write |
385
|
3
|
|
|
3
|
|
1369
|
{ |
386
|
3
|
|
|
|
|
6
|
my $self = shift; |
387
|
3
|
|
|
|
|
6
|
my ( $bytes ) = @_; |
388
|
|
|
|
|
|
|
|
389
|
3
|
|
|
3
|
|
17
|
await $self->txn(sub { shift->write( $bytes ) }); |
|
3
|
|
|
|
|
10
|
|
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
async sub read |
393
|
1
|
|
|
1
|
|
689
|
{ |
394
|
1
|
|
|
|
|
3
|
my $self = shift; |
395
|
1
|
|
|
|
|
50
|
my ( $len ) = @_; |
396
|
|
|
|
|
|
|
|
397
|
1
|
|
|
1
|
|
11
|
return await $self->txn(sub { shift->read( $len ) }); |
|
1
|
|
|
|
|
4
|
|
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
async sub write_then_read |
401
|
1
|
|
|
1
|
|
1351
|
{ |
402
|
1
|
|
|
|
|
3
|
my $self = shift; |
403
|
1
|
|
|
|
|
4
|
my ( $write_bytes, $read_len ) = @_; |
404
|
|
|
|
|
|
|
|
405
|
1
|
|
|
1
|
|
3
|
return await $self->txn(async sub { |
406
|
1
|
|
|
|
|
3
|
my ( $helper ) = @_; |
407
|
1
|
|
|
|
|
5
|
await $helper->write( $write_bytes ); |
408
|
1
|
|
|
|
|
143
|
return await $helper->read( $read_len ); |
409
|
1
|
|
|
|
|
8
|
}); |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
sub txn |
413
|
|
|
|
|
|
|
{ |
414
|
6
|
|
|
6
|
|
1233
|
my $self = shift; |
415
|
6
|
|
|
|
|
12
|
my ( $code ) = @_; |
416
|
|
|
|
|
|
|
|
417
|
6
|
50
|
|
|
|
21
|
defined( my $addr = $self->{addr} ) or |
418
|
|
|
|
|
|
|
croak "Cannot ->txn without a defined addr"; |
419
|
|
|
|
|
|
|
|
420
|
6
|
|
100
|
|
|
36
|
my $helper = $self->{txn_helper} //= bless( [ $self->{mode}, $self->{addr} ], "Device::Chip::Adapter::BusPirate::_I2C::Txn" ); |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
return $self->{mutex}->enter(sub { |
423
|
|
|
|
|
|
|
return $code->( $helper )->followed_by(sub { |
424
|
6
|
|
|
|
|
918
|
my ( $f ) = @_; |
425
|
6
|
|
|
|
|
21
|
return $self->{mode}->stop_bit->then( sub { $f } ); |
|
6
|
|
|
|
|
403
|
|
426
|
6
|
|
|
6
|
|
640
|
}); |
427
|
6
|
|
|
|
|
31
|
}); |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
package |
431
|
|
|
|
|
|
|
Device::Chip::Adapter::BusPirate::_I2C::Txn; |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
async sub write |
434
|
6
|
|
|
6
|
|
151
|
{ |
435
|
6
|
|
|
|
|
10
|
my $self = shift; |
436
|
6
|
|
|
|
|
13
|
my ( $bytes ) = @_; |
437
|
6
|
|
|
|
|
16
|
my ( $mode, $addr ) = @$self; |
438
|
|
|
|
|
|
|
|
439
|
6
|
|
|
|
|
19
|
await $mode->start_bit; |
440
|
6
|
|
|
|
|
426
|
await $mode->write( chr( $addr << 1 | 0 ) . $bytes ); |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
async sub read |
444
|
4
|
|
|
4
|
|
282
|
{ |
445
|
4
|
|
|
|
|
6
|
my $self = shift; |
446
|
4
|
|
|
|
|
8
|
my ( $len ) = @_; |
447
|
4
|
|
|
|
|
10
|
my ( $mode, $addr ) = @$self; |
448
|
|
|
|
|
|
|
|
449
|
4
|
|
|
|
|
13
|
await $mode->start_bit; |
450
|
4
|
|
|
|
|
279
|
await $mode->write( chr( $addr << 1 | 1 ) ); |
451
|
4
|
|
|
|
|
283
|
return await $mode->read( $len ); |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
package |
455
|
|
|
|
|
|
|
Device::Chip::Adapter::BusPirate::_UART; |
456
|
2
|
|
|
2
|
|
18
|
use base qw( Device::Chip::Adapter::BusPirate::_base ); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
680
|
|
457
|
|
|
|
|
|
|
|
458
|
2
|
|
|
2
|
|
17
|
use Carp; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
450
|
|
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
sub configure |
461
|
|
|
|
|
|
|
{ |
462
|
0
|
|
|
0
|
|
|
my $self = shift; |
463
|
0
|
|
|
|
|
|
my %args = @_; |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
return $self->{mode}->configure( |
466
|
|
|
|
|
|
|
baud => $args{baudrate}, |
467
|
|
|
|
|
|
|
bits => $args{bits}, |
468
|
|
|
|
|
|
|
parity => $args{parity}, |
469
|
|
|
|
|
|
|
stop => $args{stop}, |
470
|
0
|
|
|
|
|
|
); |
471
|
|
|
|
|
|
|
} |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
sub write |
474
|
|
|
|
|
|
|
{ |
475
|
0
|
|
|
0
|
|
|
my $self = shift; |
476
|
0
|
|
|
|
|
|
my ( $bytes ) = @_; |
477
|
|
|
|
|
|
|
|
478
|
0
|
|
|
|
|
|
return $self->{mode}->write( $bytes ); |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
|
481
|
0
|
|
|
0
|
|
|
sub read { croak "Device::BusPirate does not support read on UART" } |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=head1 AUTHOR |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
Paul Evans |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
=cut |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
0x55AA; |