File Coverage

blib/lib/Device/BusPirate.pm
Criterion Covered Total %
statement 89 102 87.2
branch 7 12 58.3
condition 7 16 43.7
subroutine 24 26 92.3
pod 6 10 60.0
total 133 166 80.1


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, 2014-2021 -- leonerd@leonerd.org.uk
5              
6             package Device::BusPirate 0.22;
7              
8 7     7   458589 use v5.14;
  7         82  
9 7     7   38 use warnings;
  7         15  
  7         229  
10              
11 7     7   38 use Carp;
  7         12  
  7         466  
12              
13 7     7   41 use Fcntl qw( O_NOCTTY O_NDELAY );
  7         14  
  7         656  
14 7     7   3472 use Future::AsyncAwait;
  7         116636  
  7         42  
15 7     7   3695 use Future::Mutex;
  7         3461  
  7         256  
16 7     7   3658 use Future::IO 0.04; # ->syswrite_exactly
  7         54237  
  7         352  
17 7     7   4549 use IO::Termios 0.07; # cfmakeraw
  7         203884  
  7         52  
18 7     7   399 use Time::HiRes qw( time );
  7         15  
  7         58  
19              
20             use Module::Pluggable
21 7         86 search_path => "Device::BusPirate::Mode",
22             except => qr/^Device::BusPirate::Mode::_/,
23             require => 1,
24 7     7   5651 sub_name => "modes";
  7         83878  
25             my %MODEMAP = map { $_->MODE => $_ } __PACKAGE__->modes;
26              
27 7   50 7   1001 use constant BUS_PIRATE => $ENV{BUS_PIRATE} || "/dev/ttyUSB0";
  7         17  
  7         679  
28 7   50 7   57 use constant PIRATE_DEBUG => $ENV{PIRATE_DEBUG} // 0;
  7         16  
  7         9543  
29              
30             =head1 NAME
31              
32             C - interact with a F device
33              
34             =head1 DESCRIPTION
35              
36             This module allows a program to interact with a F hardware
37             electronics debugging device, attached over a USB-emulated serial port. In the
38             following description, the reader is assumed to be generally aware of the
39             device and its capabilities. For more information about the F see:
40              
41             =over 2
42              
43             L
44              
45             =back
46              
47             This module and its various component modules are based on L, allowing
48             either synchronous or asynchronous communication with the attached hardware
49             device.
50              
51             To use it synchronously, call the C method of any returned C
52             instances to obtain the eventual result:
53              
54             my $spi = $pirate->enter_mode( "SPI" )->get;
55              
56             $spi->power( 1 )->get;
57             my $input = $spi->writeread_cs( $output )->get;
58              
59             A truely-asynchronous program would use the futures more conventionally,
60             perhaps by using C<< ->then >> chaining:
61              
62             my $input = $pirate->enter_mode( "SPI" )
63             ->then( sub {
64             my ( $spi ) = @_;
65              
66             $spi->power( 1 )->then( sub {
67             $spi->writeread_cs( $output );
68             });
69             });
70              
71             This module uses L for its underlying IO operations, so using it
72             in a program would require the event system to integrate with C
73             appropriately.
74              
75             =cut
76              
77             =head1 CONSTRUCTOR
78              
79             =cut
80              
81             =head2 new
82              
83             $pirate = Device::BusPirate->new( %args )
84              
85             Returns a new C instance to communicate with the given
86             device. Takes the following named arguments:
87              
88             =over 4
89              
90             =item serial => STRING
91              
92             Path to the serial port device node the Bus Pirate is attached to. If not
93             supplied, the C environment variable is used; falling back on a
94             default of F.
95              
96             =item baud => INT
97              
98             Serial baud rate to communicate at. Normally it should not be necessary to
99             change this from its default of C<115200>.
100              
101             =back
102              
103             =cut
104              
105             sub new
106             {
107 6     6 1 476 my $class = shift;
108 6         30 my %args = @_;
109              
110             # undocumented 'fh 'argument for unit testing
111 6   33     44 my $fh = $args{fh} // do {
112 0   0     0 my $serial = $args{serial} || BUS_PIRATE;
113 0   0     0 my $baud = $args{baud} || 115200;
114              
115 0 0       0 my $fh = IO::Termios->open( $serial, "$baud,8,n,1", O_NOCTTY|O_NDELAY )
116             or croak "Cannot open serial port $serial - $!";
117              
118 0         0 for( $fh->getattr ) {
119 0         0 $_->cfmakeraw();
120 0         0 $_->setflag_clocal( 1 );
121              
122 0         0 $fh->setattr( $_ );
123             }
124              
125 0         0 $fh->blocking( 0 );
126              
127 0         0 $fh;
128             };
129              
130 6         36 return bless {
131             fh => $fh,
132             }, $class;
133             }
134              
135             =head1 METHODS
136              
137             The following methods documented with a trailing call to C<< ->get >> return
138             L instances.
139              
140             =cut
141              
142             # For Modes
143             sub write
144             {
145 120     120 0 1594 my $self = shift;
146 120         212 my ( $buf ) = @_;
147              
148 120         163 printf STDERR "PIRATE >> %v02x\n", $buf if PIRATE_DEBUG > 1;
149              
150 120         416 my $f = Future::IO->syswrite_exactly( $self->{fh}, $buf );
151              
152 120 50       4917 return $f if wantarray;
153 120     120   567 $f->on_ready( sub { undef $f } );
  120         2366  
154             }
155              
156             async sub write_expect_ack
157 64     64 0 112 {
158 64         100 my $self = shift;
159 64         149 my ( $out, $name, $timeout ) = @_;
160              
161 64         150 await $self->write_expect_acked_data( $out, 0, $name, $timeout );
162 64         4556 return;
163             }
164              
165             async sub write_expect_acked_data
166 86     86 0 134 {
167 86         116 my $self = shift;
168 86         191 my ( $out, $readlen, $name, $timeout ) = @_;
169              
170 86         235 $self->write( $out );
171 86         546 my $buf = await $self->read( 1 + $readlen, $name, $timeout );
172              
173 86 50       55917 substr( $buf, 0, 1, "" ) eq "\x01" or
174             die "Expected ACK response to $name";
175              
176 86         351 return $buf;
177             }
178              
179             # For Modes
180             sub read
181             {
182 123     123 0 1363 my $self = shift;
183 123         268 my ( $n, $name, $timeout ) = @_;
184              
185 123 100       268 return Future->done( "" ) unless $n;
186              
187 122         185 my $buf = "";
188 122         392 my $f = Future::IO->sysread_exactly( $self->{fh}, $n );
189              
190             $f->on_done( sub {
191 0     0   0 printf STDERR "PIRATE << %v02x\n", $_[0];
192 122         7708 }) if Device::BusPirate::PIRATE_DEBUG > 1;
193              
194 122 100       305 return $f unless defined $name;
195              
196 113   100     492 return Future->wait_any(
197             $f,
198             $self->sleep( $timeout // 2 )->then_fail( "Timeout waiting for $name" ),
199             );
200             }
201              
202             =head2 sleep
203              
204             $pirate->sleep( $timeout )->get
205              
206             Returns a C that will become ready after the given timeout (in
207             seconds), unless it is cancelled first.
208              
209             =cut
210              
211             sub sleep
212             {
213 118     118 1 197 my $self = shift;
214 118         214 my ( $timeout ) = @_;
215              
216 118         323 return Future::IO->sleep( $timeout );
217             }
218              
219             =head2 enter_mutex
220              
221             @result = $pirate->enter_mutex( $code )->get
222              
223             Acts as a mutex lock, to ensure only one block of code runs at once. Calls to
224             C will be queued up; each C<$code> block will only be invoked
225             once the C returned from the previous has completed.
226              
227             Mode implementations should use this method to guard complete wire-level
228             transactions, ensuring that multiple concurrent ones will not collide with
229             each other.
230              
231             =cut
232              
233             sub enter_mutex
234             {
235 9     9 1 18 my $self = shift;
236 9         17 my ( $code ) = @_;
237              
238 9   66     59 ( $self->{mutex} //= Future::Mutex->new )->enter( $code );
239             }
240              
241             =head2 enter_mode
242              
243             $mode = $pirate->enter_mode( $modename )->get
244              
245             Switches the attached device into the given mode, and returns an object to
246             represent that hardware mode to interact with. This will be an instance of a
247             class depending on the given mode name.
248              
249             =over 4
250              
251             =item C
252              
253             The bit-banging mode. Returns an instance of L.
254              
255             =item C
256              
257             The I2C mode. Returns an instance of L.
258              
259             =item C
260              
261             The SPI mode. Returns an instance of L.
262              
263             =item C
264              
265             The UART mode. Returns an instance of L.
266              
267             =back
268              
269             Once a mode object has been created, most of the interaction with the device
270             would be done using that mode object, as it will have methods relating to the
271             specifics of that hardware mode. See the classes listed above for more
272             information.
273              
274             =cut
275              
276             async sub enter_mode
277 5     5 1 108 {
278 5         11 my $self = shift;
279 5         15 my ( $modename ) = @_;
280              
281 5 50       25 my $modeclass = $MODEMAP{$modename} or
282             croak "Unrecognised mode '$modename'";
283              
284 5         20 await $self->start;
285              
286 5         1407 $self->{mode} = $modeclass->new( $self );
287 5         31 await $self->{mode}->start;
288             }
289              
290             =head2 start
291              
292             $pirate->start->get
293              
294             Starts binary IO mode on the F device, enabling the module to
295             actually communicate with it. Normally it is not necessary to call this method
296             explicitly as it will be done by the setup code of the mode object.
297              
298             =cut
299              
300             sub start
301             {
302 5     5 1 11 my $self = shift;
303              
304             Future->wait_any(
305 5     5   12 (async sub {
306 5         22 my $buf = await $self->read( 5, "start", 2.5 );
307 5         3858 ( $self->{version} ) = $buf =~ m/^BBIO(\d)/;
308 5         33 return $self->{version};
309             })->(),
310 5     5   1869 (async sub {
311 5         22 foreach my $i ( 1 .. 20 ) {
312 5         27 $self->write( "\0" );
313 5         54 await $self->sleep( 0.05 );
314             }
315 0           die "Timed out waiting for device to enter bitbang mode";
316 5         33 })->(),
317             );
318             }
319              
320             =head2 stop
321              
322             $pirate->stop
323              
324             Stops binary IO mode on the F device and returns it to user
325             terminal mode. It may be polite to perform this at the end of a program to
326             return it to a mode that a user can interact with normally on a terminal.
327              
328             =cut
329              
330             sub stop
331             {
332 0     0 1   my $self = shift;
333              
334 0           $self->write( "\0\x0f" );
335             }
336              
337             =head1 TODO
338              
339             =over 4
340              
341             =item *
342              
343             More modes - 1-wire, raw-wire
344              
345             =item *
346              
347             AUX frequency measurement and ADC support.
348              
349             =back
350              
351             =head1 AUTHOR
352              
353             Paul Evans
354              
355             =cut
356              
357             0x55AA;