File Coverage

blib/lib/Device/BusPirate.pm
Criterion Covered Total %
statement 108 114 94.7
branch 7 10 70.0
condition 6 9 66.6
subroutine 23 25 92.0
pod 5 9 55.5
total 149 167 89.2


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