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