File Coverage

blib/lib/Device/Chip/SDCard.pm
Criterion Covered Total %
statement 112 123 91.0
branch 11 20 55.0
condition 3 4 75.0
subroutine 31 35 88.5
pod 5 7 71.4
total 162 189 85.7


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, 2016 -- leonerd@leonerd.org.uk
5              
6             package Device::Chip::SDCard;
7              
8 5     5   276562 use strict;
  5         18  
  5         114  
9 5     5   20 use warnings;
  5         6  
  5         110  
10 5     5   21 use base qw( Device::Chip );
  5         6  
  5         844  
11              
12             our $VERSION = '0.02';
13              
14 5     5   11973 use Data::Bitfield qw( bitfield boolfield );
  5         7675  
  5         272  
15 5     5   2132 use Future::Utils qw( repeat );
  5         9325  
  5         267  
16              
17 5     5   32 use constant PROTOCOL => "SPI";
  5         9  
  5         2233  
18              
19             =head1 NAME
20              
21             C - chip driver for F and F cards
22              
23             =head1 SYNOPSIS
24              
25             use Device::Chip::SDCard;
26              
27             my $card = Device::Chip::SDCard->new;
28              
29             $card->mount( Device::Chip::Adapter::...->new )->get;
30              
31             $card->initialise->get;
32              
33             my $bytes = $card->read_block( 0 )->get;
34              
35             print "Read block zero:\n";
36             printf "%v02X\n", $bytes;
37              
38             =head1 DESCRIPTION
39              
40             This L subclass provides specific communication to an F or
41             F storage card attached via an SPI adapter.
42              
43             At present it only supports MMC and SDSC ("standard capacity") cards, not SDHC
44             or SDXC.
45              
46             =cut
47              
48             sub SPI_options
49             {
50             return (
51 4     4 0 1005 mode => 0,
52             max_bitrate => 1E6,
53             );
54             }
55              
56             =head1 METHODS
57              
58             The following methods documented with a trailing call to C<< ->get >> return
59             L instances.
60              
61             =cut
62              
63             sub send_command
64             {
65 5     5 0 121 my $self = shift;
66 5         11 my ( $cmd, $arg, $readlen ) = @_;
67              
68 5   100     18 $arg //= 0;
69 5   50     26 $readlen //= 0;
70              
71 5         7 my $crcstop = 0x95;
72              
73             # TODO: until we can perform dynamic transactions with D:C:A we'll have to
74             # do this by presuming the maximum amount of time for the card to respond
75             # (8 words) and look for the response in what's returned
76              
77             $self->protocol->readwrite(
78             pack "C N C a*", 0x40 | $cmd, $arg, $crcstop, "\xFF" x ( 8 + $readlen ),
79             )->then( sub {
80 5     5   583 my ( $resp ) = @_;
81              
82             # Trim to the start of the expected result
83 5         13 substr $resp, 0, 7, "";
84              
85             # Look for a byte with top bit clear
86 5         11 while( length $resp ) {
87 9         19 my $ret = unpack( "C", $resp );
88 9 100       30 return Future->done( $ret, unpack "x a$readlen", $resp ) if !( $ret & 0x80 );
89              
90 4         9 substr $resp, 0, 1, "";
91             }
92 0         0 return Future->fail(
93             sprintf "Timed out waiting for response to command %02X", $cmd
94             );
95 5         12 });
96             }
97              
98             sub _recv_data_block
99             {
100 4     4   7 my $self = shift;
101 4         7 my ( $buf, $len ) = @_;
102              
103             # Wait for a token
104             ( repeat {
105 7     7   192 $buf =~ s/^\xFF+//;
106 7 100       26 $buf =~ s/^\xFE// and
107             return Future->done();
108              
109             $self->protocol->readwrite_no_ss( "\xFF" x 16 )
110 3         15 ->on_done( sub { $buf .= $_[0] } );
  3         243  
111 7     7   158 } until => sub { !$_[0]->get } )
112             # Now want the data + CRC
113             ->then( sub {
114 4 50   4   600 length $buf >= $len + 2 and
115             return Future->done();
116              
117 4         12 $self->protocol->readwrite_no_ss( "\xFF" x ( $len + 2 - length $buf ) )
118             })->then( sub {
119 4     4   518 $buf .= $_[0];
120             # TODO: might want to verify the CRC?
121 4         19 Future->done( substr $buf, 0, $len );
122 4         25 });
123             }
124              
125             # Commands
126             use constant {
127 5         539 CMD_GO_IDLE_STATE => 0,
128             CMD_SEND_OP_COND => 1,
129             CMD_SEND_CSD => 9,
130             CMD_SET_BLOCKLEN => 16,
131             CMD_READ_SINGLE_BLOCK => 17,
132             CMD_READ_OCR => 58,
133 5     5   34 };
  5         14  
134              
135             # Response first byte bitflags
136             use constant {
137 5         7667 RESP_PARAM_ERROR => 1<<6,
138             RESP_ADDR_ERROR => 1<<5,
139             RESP_ERASESEQ_ERROR => 1<<4,
140             RESP_CRC_ERROR => 1<<3,
141             RESP_ILLEGAL_CMD => 1<<2,
142             RESP_ERASE_RESET => 1<<1,
143             RESP_IDLE => 1<<0,
144 5     5   29 };
  5         8  
145              
146             =head2 initialise
147              
148             $card->initialise->get
149              
150             Checks that an SD card is present, switches it into SPI mode and waits for its
151             initialisation process to complete.
152              
153             =cut
154              
155             sub initialise
156             {
157 1     1 1 148 my $self = shift;
158              
159             # Initialise first by switching the card into SPI mode
160             $self->protocol->write( "\xFF" x 10 )->then( sub {
161 1     1   117 $self->send_command( CMD_GO_IDLE_STATE )
162             })->then( sub {
163 1     1   78 my ( $resp ) = @_;
164 1 50       3 $resp == 1 or die "Expected 01 response; got $resp";
165              
166             repeat {
167             # TODO: Consider using SEND_IF_COND and doing SDHC initialisation
168 2         106 $self->send_command( CMD_SEND_OP_COND );
169             } while => sub {
170 2         89 my $resp = shift->get;
171 2         15 $resp & RESP_IDLE;
172 1         18 }, foreach => [ 1 .. 200 ];
173             })->then( sub {
174 1     1   209 my ( $resp ) = @_;
175 1 50       4 $resp & RESP_IDLE and die "Timed out waiting for card to leave IDLE mode";
176              
177 1         3 $self->send_command( CMD_SET_BLOCKLEN, 512 );
178             })->then( sub {
179 1     1   71 my ( $resp ) = @_;
180 1 50       5 $resp == 0 or die "Expected 00 response; got $resp";
181              
182 1         3 Future->done;
183 1         4 });
184             }
185              
186             =head2 size
187              
188             $n_bytes = $card->size->get
189              
190             Returns the size of the media card in bytes.
191              
192             =cut
193              
194             sub size
195             {
196 0     0 1 0 my $self = shift;
197              
198             $self->read_csd->then( sub {
199 0     0   0 my ( $csd ) = @_;
200 0         0 return Future->done( $csd->{bytes} );
201 0         0 });
202             }
203              
204             sub _spi_txn
205             {
206 4     4   11 my $self = shift;
207 4         7 my ( $code ) = @_;
208              
209             $self->protocol->assert_ss->then(
210             $code
211             )->followed_by( sub {
212 4     4   335 my ( $f ) = @_;
213 4         10 $self->protocol->release_ss->then( sub { $f } );
  4         435  
214 4         13 });
215             }
216              
217             =head2 read_csd
218              
219             $data = $card->read_csd->get;
220              
221             Returns a C reference containing decoded fields from the SD card's CSD
222             ("card-specific data") register.
223              
224             This hash will contain the following fields:
225              
226             TAAC
227             NSAC
228             TRAN_SPEED
229             CCC
230             READ_BL_LEN
231             READ_BL_LEN_PARTIAL
232             WRITE_BLK_MISALIGN
233             READ_BLK_MISALIGN
234             DSR_IMP
235             C_SIZE
236             VDD_R_CURR_MIN
237             VDD_R_CURR_MAX
238             VDD_W_CURR_MIN
239             VDD_W_CURR_MAX
240             C_SIZE_MULT
241             ERASE_BLK_EN
242             SECTOR_SIZE
243             WP_GRP_SIZE
244             WP_GRP_ENABLE
245             R2W_FACTOR
246             WRITE_BL_LEN
247             WRITE_BL_PARTIAL
248             FILE_FORMAT_GRP
249             COPY
250             PERM_WRITE_PROTECT
251             TEMP_WRITE_PROTECT
252             FILE_FORMAT
253              
254             The hash will also contain the following calculated fields, derived from the
255             decoded fields above for convenience of calling code.
256              
257             blocks # number of blocks implied by C_SIZE / C_SIZE_MULT
258             bytes # number of bytes of storage, implied by blocks and READ_BL_LEN
259              
260             =cut
261              
262             # This code is most annoying to write as it involves lots of bitwise unpacking
263             # at non-byte boundaries. It's easier (though inefficient) to perform this on
264             # an array of 128 1-bit values
265             sub _bits_to_uint {
266 17     17   18 my $n = 0;
267 17         64 ( $n <<= 1 ) |= $_ for reverse @_;
268 17         63 return $n;
269             }
270              
271             my %_DECSCALE = (
272             1 => 1.0, 2 => 1.2, 3 => 1.3, 4 => 1.5, 5 => 2.0, 6 => 2.5,
273             7 => 3.0, 8 => 3.5, 9 => 4.0, 0xA => 4.5, 0xB => 5.0,
274             0xC => 5.5, 0xD => 6.0, 0xE => 7.0, 0xF => 8.0
275             );
276              
277             sub _convert_decimal
278             {
279 2     2   4 my ( $unit, $val ) = @_;
280              
281 2         13 my $mult = $unit % 3;
282 2         3 $unit -= $mult;
283 2         4 $unit /= 3;
284              
285 2         6 $val = $_DECSCALE{$val} * ( 10 ** $mult );
286              
287 2         14 return $val . substr( "num kMG", $unit + 3, 1 );
288             }
289              
290             my %_CURRMIN = (
291             0 => 0.5, 1 => 1, 2 => 5, 3 => 10,
292             4 => 25, 5 => 35, 6 => 60, 7 => 100,
293             );
294             my %_CURRMAX = (
295             0 => 1, 1 => 5, 2 => 10, 3 => 25,
296             4 => 35, 5 => 45, 6 => 80, 7 => 200,
297             );
298              
299             sub _unpack_csd_v0
300             {
301 1     1   2 my ( $bytes ) = @_;
302 1         25 my @bits = reverse split //, unpack "B128", $bytes;
303              
304             my %csd = (
305             TAAC => _convert_decimal( _bits_to_uint( @bits[112 .. 114] ) - 9, _bits_to_uint( @bits[115 .. 118] ) ) . "s",
306             NSAC => 100*_bits_to_uint( @bits[104 .. 111] ) . "ck",
307             TRAN_SPEED => _convert_decimal( _bits_to_uint( @bits[ 96 .. 98] ) + 5, _bits_to_uint( @bits[ 99 .. 102] ) ) . "bit/s",
308 12         21 CCC => [ grep { $bits[84+$_] } 0 .. 11 ],
309             READ_BL_LEN => 2**_bits_to_uint( @bits[ 80 .. 83] ),
310             READ_BL_LEN_PARTIAL => $bits[79],
311             WRITE_BLK_MISALIGN => $bits[78],
312             READ_BLK_MISALIGN => $bits[77],
313             DSR_IMP => $bits[76],
314             C_SIZE => _bits_to_uint( @bits[ 62 .. 73] ),
315             VDD_R_CURR_MIN => $_CURRMIN{ _bits_to_uint( @bits[ 59 .. 61] ) } . "mA",
316             VDD_R_CURR_MAX => $_CURRMAX{ _bits_to_uint( @bits[ 56 .. 58] ) } . "mA",
317             VDD_W_CURR_MIN => $_CURRMIN{ _bits_to_uint( @bits[ 53 .. 55] ) } . "mA",
318 1         5 VDD_W_CURR_MAX => $_CURRMAX{ _bits_to_uint( @bits[ 50 .. 52] ) } . "mA",
319             C_SIZE_MULT => _bits_to_uint( @bits[ 47 .. 49] ),
320             ERASE_BLK_EN => $bits[46],
321             SECTOR_SIZE => 1+_bits_to_uint( @bits[ 39 .. 45] ),
322             WP_GRP_SIZE => 1+_bits_to_uint( @bits[ 32 .. 38] ),
323             WP_GRP_ENABLE => $bits[31],
324             R2W_FACTOR => 2**_bits_to_uint( @bits[ 26 .. 28] ),
325             WRITE_BL_LEN => 2**_bits_to_uint( @bits[ 22 .. 25] ),
326             WRITE_BL_PARTIAL => $bits[21],
327             FILE_FORMAT_GRP => $bits[15],
328             COPY => $bits[14],
329             PERM_WRITE_PROTECT => $bits[13],
330             TEMP_WRITE_PROTECT => $bits[12],
331             FILE_FORMAT => _bits_to_uint( @bits[ 10 .. 11] ),
332             # Final bits are the CRC, which we ignore
333             );
334              
335 1         5 $csd{blocks} = ( 1 + $csd{C_SIZE} ) * ( 2 ** ( $csd{C_SIZE_MULT} + 2 ) );
336 1         3 $csd{bytes} = $csd{blocks} * $csd{READ_BL_LEN};
337              
338 1         8 return \%csd;
339             }
340              
341             sub read_csd
342             {
343 1     1 1 136 my $self = shift;
344              
345 1         2 my $protocol = $self->protocol;
346              
347 1         5 my $buf;
348              
349             $self->_spi_txn( sub {
350             $protocol->write_no_ss(
351             pack "C N C a*", 0x40 | CMD_SEND_CSD, 0, 0xFF, "\xFF"
352             )->then( sub {
353 1         96 $protocol->readwrite_no_ss( "\xFF" x 8 )
354             })->then( sub {
355 1         101 ( $buf ) = @_;
356 1         4 $buf =~ s/^\xFF*//;
357 1 50       4 $buf =~ s/^\0// or
358             return Future->fail( sprintf "Expected response 00; got %02X to SEND_CSD", ord $buf );
359              
360 1         4 $self->_recv_data_block( $buf, 16 );
361 1     1   113 });
362             })->then( sub {
363 1     1   57 my ( $csd ) = @_;
364             # Top two bits give the structure version
365 1         1 my $ver = vec( $csd, 0, 2 );
366 1 50       3 if( $ver == 0 ) {
    0          
367 1         3 return Future->done( _unpack_csd_v0( $csd ) );
368             }
369             elsif( $ver == 1 ) {
370 0         0 return Future->done( _unpack_csd_v1( $csd ) );
371             }
372             else {
373 0         0 return Future->fail( "Bad CSD structure version $ver" );
374             }
375 1         12 });
376             }
377              
378             =head2 read_ocr
379              
380             $fields = $card->read_ocr->get
381              
382             Returns a C reference containing decoded fields from the card's OCR
383             ("operating conditions register").
384              
385             This hash will contain the following fields:
386              
387             BUSY
388             CCS
389             UHS_II
390             1V8_ACCEPTED
391             3V5, 3V4, 3V3, ..., 2V7
392              
393             =cut
394              
395             bitfield OCR =>
396             BUSY => boolfield( 31 ),
397             CCS => boolfield( 30 ),
398             UHS_II => boolfield( 29 ),
399             '1V8_ACCEPTED' => boolfield( 24 ),
400             '3V5' => boolfield( 23 ),
401             '3V4' => boolfield( 22 ),
402             '3V3' => boolfield( 21 ),
403             '3V2' => boolfield( 20 ),
404             '3V1' => boolfield( 19 ),
405             '3V0' => boolfield( 18 ),
406             '2V9' => boolfield( 17 ),
407             '2V8' => boolfield( 16 ),
408             '2V7' => boolfield( 15 );
409              
410             sub read_ocr
411             {
412 0     0 1 0 my $self = shift;
413              
414             $self->send_command( CMD_READ_OCR, undef, 4 )->then( sub {
415 0     0   0 my ( $resp, $ocr ) = @_;
416 0         0 Future->done( { unpack_OCR( unpack "N", $ocr ) } );
417 0         0 });
418             }
419              
420             =head2 read_block
421              
422             $bytes = $card->read_block( $lba )->get
423              
424             Returns a 512-byte bytestring containing data read from the given sector of
425             the card.
426              
427             =cut
428              
429             sub read_block
430             {
431 3     3 1 8324 my $self = shift;
432 3         6 my ( $lba ) = @_;
433              
434 3         6 my $byteaddr = $lba * 512;
435              
436 3         14 my $protocol = $self->protocol;
437              
438 3         13 my $buf;
439              
440             $self->_spi_txn( sub {
441             $protocol->write_no_ss(
442             pack "C N C a*", 0x40 | CMD_READ_SINGLE_BLOCK, $byteaddr, 0xFF, "\xFF"
443             )->then( sub {
444 3         303 $protocol->readwrite_no_ss( "\xFF" x 8 );
445             })->then( sub {
446 3         327 ( $buf ) = @_;
447 3         8 $buf =~ s/^\xFF*//;
448 3 50       12 $buf =~ s/^\0// or
449             return Future->fail( sprintf "Expected response 00; got %02X to READ_SINGLE_BLOCK", ord $buf );
450              
451 3         8 $self->_recv_data_block( $buf, 512 );
452 3     3   341 });
453 3         15 });
454             }
455              
456             =head1 TODO
457              
458             =over 4
459              
460             =item *
461              
462             Support block writing.
463              
464             =item *
465              
466             Support the different initialisation sequence (and block size requirements) of
467             SDHC cards.
468              
469             =back
470              
471             =head1 AUTHOR
472              
473             Paul Evans
474              
475             =cut
476              
477             0x55AA;