| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
package Hardware::iButton::Device; |
|
3
|
|
|
|
|
|
|
|
|
4
|
1
|
|
|
1
|
|
5
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
36
|
|
|
5
|
1
|
|
|
1
|
|
4
|
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
75
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
require Exporter; |
|
8
|
|
|
|
|
|
|
#require AutoLoader; |
|
9
|
1
|
|
|
1
|
|
6
|
use Time::HiRes qw(usleep); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
5
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
|
12
|
|
|
|
|
|
|
# Items to export into callers namespace by default. Note: do not export |
|
13
|
|
|
|
|
|
|
# names by default without a very good reason. Use EXPORT_OK instead. |
|
14
|
|
|
|
|
|
|
# Do not simply export all your public functions/methods/constants. |
|
15
|
|
|
|
|
|
|
@EXPORT = qw( |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
); |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
( $VERSION ) = '$Revision: 1.2 $ ' =~ /\$Revision:\s+([^\s]+)/; |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 NAME |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
Hardware::iButton::Device - object to represent iButtons |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
use Hardware::iButton::Connection; |
|
28
|
|
|
|
|
|
|
$c = new Hardware::iButton::Connection "/dev/ttyS0"; |
|
29
|
|
|
|
|
|
|
@b = $c->scan(); |
|
30
|
|
|
|
|
|
|
foreach $b (@b) { |
|
31
|
|
|
|
|
|
|
print "id: ", $b->id(), ", reg0: ",$b->readreg(0),"\n"; |
|
32
|
|
|
|
|
|
|
} |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
This module talks to iButtons via the "active" serial interface (anything |
|
37
|
|
|
|
|
|
|
using the DS2480, including the DS1411k and the DS 9097U). It builds up a list |
|
38
|
|
|
|
|
|
|
of devices available, lets you read and write their registers, etc. |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
The connection object is an Hardware::iButton::Connection. The main |
|
41
|
|
|
|
|
|
|
user-visible purpose of it is to provide a list of Hardware::iButton::Device |
|
42
|
|
|
|
|
|
|
objects. These can be subclassed once their family codes are known to provide |
|
43
|
|
|
|
|
|
|
specialized methods unique to the capabilities of that device. Those devices |
|
44
|
|
|
|
|
|
|
will then be Hardware::iButton::Device::DS1920, etc. |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=head1 AUTHOR |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
Brian Warner, warner@lothar.com |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
http://www.ibutton.com, http://sof.mit.edu/ibuttonpunks/ |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=cut |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# Preloaded methods go here. |
|
57
|
|
|
|
|
|
|
|
|
58
|
1
|
|
|
1
|
|
155
|
use vars qw(%models); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
1554
|
|
|
59
|
|
|
|
|
|
|
%models = ( |
|
60
|
|
|
|
|
|
|
"01" => { |
|
61
|
|
|
|
|
|
|
'model' => 'DS1990A', |
|
62
|
|
|
|
|
|
|
'memsize' => 0, |
|
63
|
|
|
|
|
|
|
'memtype' => "none", |
|
64
|
|
|
|
|
|
|
'specialfuncs' => "", |
|
65
|
|
|
|
|
|
|
}, |
|
66
|
|
|
|
|
|
|
"02" => { |
|
67
|
|
|
|
|
|
|
'model' => 'DS1991', |
|
68
|
|
|
|
|
|
|
'memsize' => 512/8, |
|
69
|
|
|
|
|
|
|
'memtype' => "NVRAM", |
|
70
|
|
|
|
|
|
|
'specialfuncs' => "protected nvram 3*384bits", |
|
71
|
|
|
|
|
|
|
}, |
|
72
|
|
|
|
|
|
|
"08" => { |
|
73
|
|
|
|
|
|
|
'model' => 'DS1992', |
|
74
|
|
|
|
|
|
|
'memsize' => 1024/8, |
|
75
|
|
|
|
|
|
|
'memtype' => "NVRAM", |
|
76
|
|
|
|
|
|
|
'specialfuncs' => "", |
|
77
|
|
|
|
|
|
|
}, |
|
78
|
|
|
|
|
|
|
"06" => { |
|
79
|
|
|
|
|
|
|
'model' => 'DS1993', |
|
80
|
|
|
|
|
|
|
'memsize' => 4096/8, |
|
81
|
|
|
|
|
|
|
'memtype' => "NVRAM", |
|
82
|
|
|
|
|
|
|
'specialfuncs' => "", |
|
83
|
|
|
|
|
|
|
}, |
|
84
|
|
|
|
|
|
|
"05" => { |
|
85
|
|
|
|
|
|
|
'model' => 'DS2405', |
|
86
|
|
|
|
|
|
|
'memsize' => 0, |
|
87
|
|
|
|
|
|
|
'memtype' => "none", |
|
88
|
|
|
|
|
|
|
'specialfuncs' => "switch", |
|
89
|
|
|
|
|
|
|
'class' => 'Hardware::iButton::Device::switch', |
|
90
|
|
|
|
|
|
|
}, |
|
91
|
|
|
|
|
|
|
"04" => { |
|
92
|
|
|
|
|
|
|
'model' => 'DS1994', |
|
93
|
|
|
|
|
|
|
'memsize' => 4096/8, |
|
94
|
|
|
|
|
|
|
'memtype' => "NVRAM", |
|
95
|
|
|
|
|
|
|
'specialfuncs' => "clock/counter", |
|
96
|
|
|
|
|
|
|
}, |
|
97
|
|
|
|
|
|
|
"0a" => { |
|
98
|
|
|
|
|
|
|
'model' => 'DS1995', |
|
99
|
|
|
|
|
|
|
'memsize' => 16*1024/8, |
|
100
|
|
|
|
|
|
|
'memtype' => "NVRAM", |
|
101
|
|
|
|
|
|
|
'specialfuncs' => "", |
|
102
|
|
|
|
|
|
|
}, |
|
103
|
|
|
|
|
|
|
"0c" => { |
|
104
|
|
|
|
|
|
|
'model' => 'DS1996', |
|
105
|
|
|
|
|
|
|
'memsize' => 64*1024/8, |
|
106
|
|
|
|
|
|
|
'memtype' => "NVRAM", |
|
107
|
|
|
|
|
|
|
'specialfuncs' => "", |
|
108
|
|
|
|
|
|
|
}, |
|
109
|
|
|
|
|
|
|
"09" => { |
|
110
|
|
|
|
|
|
|
'model' => 'DS1982', |
|
111
|
|
|
|
|
|
|
'memsize' => 1024/8, |
|
112
|
|
|
|
|
|
|
'memtype' => "EPROM", |
|
113
|
|
|
|
|
|
|
'specialfuncs' => "", |
|
114
|
|
|
|
|
|
|
}, |
|
115
|
|
|
|
|
|
|
"0b" => { |
|
116
|
|
|
|
|
|
|
'model' => 'DS1985', |
|
117
|
|
|
|
|
|
|
'memsize' => 16*1024/8, |
|
118
|
|
|
|
|
|
|
'memtype' => "EPROM", |
|
119
|
|
|
|
|
|
|
'specialfuncs' => "", |
|
120
|
|
|
|
|
|
|
}, |
|
121
|
|
|
|
|
|
|
"0f" => { |
|
122
|
|
|
|
|
|
|
'model' => 'DS1986', |
|
123
|
|
|
|
|
|
|
'memsize' => 64*1024/8, |
|
124
|
|
|
|
|
|
|
'memtype' => "EPROM", |
|
125
|
|
|
|
|
|
|
'specialfuncs' => "", |
|
126
|
|
|
|
|
|
|
}, |
|
127
|
|
|
|
|
|
|
"10" => { |
|
128
|
|
|
|
|
|
|
'model' => 'DS1920', |
|
129
|
|
|
|
|
|
|
'memsize' => 16/8, # yes, really. two bytes. |
|
130
|
|
|
|
|
|
|
'memtype' => "EEPROM", |
|
131
|
|
|
|
|
|
|
'specialfuncs' => "thermometer", |
|
132
|
|
|
|
|
|
|
'class' => 'Hardware::iButton::Device::DS1920', |
|
133
|
|
|
|
|
|
|
}, |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
"14" => { |
|
136
|
|
|
|
|
|
|
'model' => 'DS1971', |
|
137
|
|
|
|
|
|
|
'memsize' => 256/8, |
|
138
|
|
|
|
|
|
|
'memtype' => "EPROM", |
|
139
|
|
|
|
|
|
|
'specialfuncs' => "??", |
|
140
|
|
|
|
|
|
|
}, |
|
141
|
|
|
|
|
|
|
"16" => { |
|
142
|
|
|
|
|
|
|
'model' => 'javabutton', |
|
143
|
|
|
|
|
|
|
'memsize' => 0, |
|
144
|
|
|
|
|
|
|
'memtype' => "??", |
|
145
|
|
|
|
|
|
|
'specialfuncs' => "Java processor", |
|
146
|
|
|
|
|
|
|
'class' => 'Hardware::iButton::Device::JavaButton', |
|
147
|
|
|
|
|
|
|
}, |
|
148
|
|
|
|
|
|
|
); |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# new is the constructor, called by Hardware::iButton::Connection::scan() to |
|
152
|
|
|
|
|
|
|
# create the new Hardware::iButton::Device instance to return to the user |
|
153
|
|
|
|
|
|
|
sub new { |
|
154
|
0
|
|
|
0
|
0
|
|
my($class, $connection, $raw_id) = @_; |
|
155
|
0
|
|
|
|
|
|
my $self = bless {}, $class; |
|
156
|
|
|
|
|
|
|
# we'll rebless ourselves into a device-specific class once we set up some |
|
157
|
|
|
|
|
|
|
# basic stuff |
|
158
|
0
|
|
|
|
|
|
$self->{'connection'} = $connection; |
|
159
|
0
|
|
|
|
|
|
$self->{'raw_id'} = $raw_id; |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# things the user can query about, all derived from the raw_id |
|
162
|
0
|
|
|
|
|
|
$self->{'family'} = unpack("H2",pack("b8",substr($raw_id,0,8))); |
|
163
|
0
|
|
|
|
|
|
$self->{'serial'} = unpack("H12", |
|
164
|
|
|
|
|
|
|
pack("B48", |
|
165
|
|
|
|
|
|
|
scalar(reverse(substr($raw_id,8,48))))); |
|
166
|
0
|
|
|
|
|
|
$self->{'crc'} = unpack("H2",pack("b8",substr($raw_id,56,8))); |
|
167
|
0
|
|
|
|
|
|
$self->{'id'} = join("", |
|
168
|
|
|
|
|
|
|
$self->{'family'},$self->{'serial'},$self->{'crc'}); |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# check CRC |
|
171
|
0
|
|
|
|
|
|
my $crc = Hardware::iButton::Connection::crc(0, split(//, pack("b*", |
|
172
|
|
|
|
|
|
|
$raw_id))); |
|
173
|
0
|
0
|
|
|
|
|
if ($crc != 0) { |
|
174
|
0
|
|
|
|
|
|
warn("crc didn't match"); |
|
175
|
|
|
|
|
|
|
} |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# model-specific stuff |
|
178
|
0
|
0
|
|
|
|
|
if (defined($models{$self->{'family'}})) { |
|
179
|
0
|
|
|
|
|
|
my $m = $models{$self->{'family'}}; |
|
180
|
0
|
|
|
|
|
|
foreach (keys(%$m)) { |
|
181
|
|
|
|
|
|
|
#print " $_ -> ",$m->{$_},"\n"; |
|
182
|
0
|
|
|
|
|
|
$self->{$_} = $m->{$_}; |
|
183
|
|
|
|
|
|
|
} |
|
184
|
0
|
0
|
|
|
|
|
if ($m->{'class'}) { |
|
185
|
0
|
|
|
|
|
|
bless $self, $m->{'class'}; |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
} else { |
|
188
|
0
|
|
|
|
|
|
warn "unknown model, family code $self->{'family'}"; |
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
0
|
|
|
|
|
|
return $self; |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head2 accessors |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
$family = $b->family(); # "01" for DS1990A/DS2401 "id only" buttons |
|
197
|
|
|
|
|
|
|
$serial = $b->serial(); # "000001F1F1F3", as stamped on button |
|
198
|
|
|
|
|
|
|
$crc = $b->crc(); # "E5" error check byte |
|
199
|
|
|
|
|
|
|
$id = $b->id(); # the previous three joined together: "01000001F1F1F3E5" |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=cut |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub family { |
|
204
|
0
|
|
|
0
|
0
|
|
return $_[0]->{'family'}; |
|
205
|
|
|
|
|
|
|
} |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
sub serial { |
|
208
|
0
|
|
|
0
|
0
|
|
return $_[0]->{'serial'}; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub crc { |
|
212
|
0
|
|
|
0
|
0
|
|
return $_[0]->{'crc'}; |
|
213
|
|
|
|
|
|
|
} |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub id { |
|
216
|
0
|
|
|
0
|
0
|
|
return $_[0]->{'id'}; |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=head2 select |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
$b->select(); |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Activate this button (in Dallas terms, "move it to the Transport Layer"). All |
|
225
|
|
|
|
|
|
|
other buttons will be idled and will not respond to commands until the bus is |
|
226
|
|
|
|
|
|
|
reset with C<$c->reset()>. Returns 1 for success, undef if the button is no |
|
227
|
|
|
|
|
|
|
longer on the bus. |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=cut |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub select { |
|
232
|
0
|
|
|
0
|
1
|
|
my($self) = @_; |
|
233
|
0
|
|
|
|
|
|
return $self->{'connection'}->select($self->{'raw_id'}); |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub verify { |
|
237
|
0
|
|
|
0
|
0
|
|
my($self) = @_; |
|
238
|
0
|
|
|
|
|
|
return $self->{'connection'}->verify($self->{'raw_id'}); |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub reset { |
|
242
|
0
|
|
|
0
|
0
|
|
return $_[0]->{'connection'}->reset(); |
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head2 is_present |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
$button->is_present(); |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
Checks to see if the given button is still present, using the Search ROM |
|
251
|
|
|
|
|
|
|
command. Returns 1 if it is, 0 if not. |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=cut |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub is_present { |
|
256
|
0
|
|
|
0
|
1
|
|
my($self) = @_; |
|
257
|
0
|
0
|
|
|
|
|
return 1 |
|
258
|
|
|
|
|
|
|
# XXX pointless code if return 1 above ? |
|
259
|
|
|
|
|
|
|
if $self->{'connection'}->scan($self->{'family'}, $self->{'serial'}); |
|
260
|
0
|
|
|
|
|
|
return 0; |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=head2 Button Introspection |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
or, how not to get lost in your own navel |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
$model = $b->model(); # "DS1992" |
|
268
|
|
|
|
|
|
|
$bytes = $b->memsize(); # 128 bytes |
|
269
|
|
|
|
|
|
|
$type = $b->memtype(); # "NVRAM" |
|
270
|
|
|
|
|
|
|
$special = $b->specialfuncs(); # "thermometer", "clock", "java", "crypto" |
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
=cut |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
sub model { |
|
275
|
0
|
|
|
0
|
0
|
|
return $_[0]->{'model'}; |
|
276
|
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
sub memsize { |
|
278
|
0
|
|
|
0
|
0
|
|
return $_[0]->{'memsize'}; |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
sub memtype { |
|
281
|
0
|
|
|
0
|
0
|
|
return $_[0]->{'memtype'}; |
|
282
|
|
|
|
|
|
|
} |
|
283
|
|
|
|
|
|
|
sub specialfuncs { |
|
284
|
0
|
|
|
0
|
0
|
|
return $_[0]->{'specialfuncs'}; |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# common actions that all buttons can do |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=head2 read_memory |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
$data = $b->read_memory($start, $length); |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
Reads memory from the iButton. Acts like C<$data = substr(memory, $start, |
|
295
|
|
|
|
|
|
|
$length)>. If you read beyond the end of the device, you will get all ones |
|
296
|
|
|
|
|
|
|
in the unimplemented addresses. |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=cut |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
sub read_memory { |
|
301
|
0
|
|
|
0
|
1
|
|
my($self, $addr, $length) = @_; |
|
302
|
0
|
|
|
|
|
|
my $c = $self->{'connection'}; |
|
303
|
0
|
|
|
|
|
|
$self->select(); |
|
304
|
0
|
|
|
|
|
|
my $str = &Hardware::iButton::Connection::READ_MEMORY . pack("v",$addr) |
|
305
|
|
|
|
|
|
|
. "\xff" x $length; |
|
306
|
0
|
|
|
|
|
|
$c->send($str); |
|
307
|
0
|
|
|
|
|
|
$c->read(1+2); |
|
308
|
0
|
|
|
|
|
|
my $buf; |
|
309
|
0
|
|
|
|
|
|
$buf = $c->read($length); |
|
310
|
0
|
|
|
|
|
|
$self->reset(); |
|
311
|
0
|
|
|
|
|
|
return $buf; |
|
312
|
|
|
|
|
|
|
} |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=head2 write_memory |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
$b->write_memory($start, $data); |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
Writes memory to the iButton NVRAM. Acts like C
|
|
319
|
|
|
|
|
|
|
length($data)) = $data;>. Writes in chunks to separate 32-byte pages, each |
|
320
|
|
|
|
|
|
|
chunk going to the scratchpad first, verified there, then copied into |
|
321
|
|
|
|
|
|
|
NVRAM. Returns the number of bytes successfully written. |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=cut |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub write_memory_page { |
|
326
|
0
|
|
|
0
|
0
|
|
my($self, $pageaddr, $chunk) = @_; |
|
327
|
|
|
|
|
|
|
# the data does not span a page, |
|
328
|
|
|
|
|
|
|
# i.e. ($pageaddr % 32) == (($pageaddr+length($chunk)) % 32) |
|
329
|
|
|
|
|
|
|
# length($chunk) <= 32 |
|
330
|
|
|
|
|
|
|
|
|
331
|
0
|
|
|
|
|
|
my $c = $self->{'connection'}; |
|
332
|
|
|
|
|
|
|
|
|
333
|
0
|
|
|
|
|
|
$c->reset(); |
|
334
|
0
|
|
|
|
|
|
$self->select(); |
|
335
|
0
|
|
|
|
|
|
my $str = &Hardware::iButton::Connection::WRITE_SCRATCHPAD . pack("v",$pageaddr); |
|
336
|
0
|
|
|
|
|
|
$str .= $chunk; |
|
337
|
0
|
|
|
|
|
|
$c->send($str); |
|
338
|
0
|
|
|
|
|
|
$c->read(length($str)); |
|
339
|
0
|
|
|
|
|
|
$c->reset(); |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
# verify the scratchpad |
|
342
|
0
|
|
|
|
|
|
$self->select(); |
|
343
|
0
|
|
|
|
|
|
$str = &Hardware::iButton::Connection::READ_SCRATCHPAD . "\xff" x 3; |
|
344
|
0
|
|
|
|
|
|
$c->send($str); $c->read(1); |
|
|
0
|
|
|
|
|
|
|
|
345
|
0
|
|
|
|
|
|
my $buf; |
|
346
|
0
|
|
|
|
|
|
$buf = $c->read(3); |
|
347
|
|
|
|
|
|
|
# check it! |
|
348
|
|
|
|
|
|
|
# ("right foot red.. yellow foot blue.. left right yellow blue green!") |
|
349
|
|
|
|
|
|
|
# the first two bytes are the address we wrote. The third is a status byte. |
|
350
|
0
|
|
|
|
|
|
my $readback_addr = unpack("v", substr($buf, 0, 2)); |
|
351
|
0
|
0
|
|
|
|
|
if ($readback_addr != $pageaddr) { |
|
352
|
|
|
|
|
|
|
# address got garbled in transit |
|
353
|
0
|
|
|
|
|
|
print "address not correct: $readback_addr instead of $pageaddr\n"; |
|
354
|
0
|
|
|
|
|
|
$c->reset(); |
|
355
|
0
|
|
|
|
|
|
return 0; # try again |
|
356
|
|
|
|
|
|
|
} |
|
357
|
0
|
|
|
|
|
|
my $status = unpack("C", substr($buf, 2, 1)); |
|
358
|
|
|
|
|
|
|
# $status byte is (AA OF PF E4 E3 E2 E1 E0) |
|
359
|
|
|
|
|
|
|
# AA: authorization accepted: set once COPY_SCRATCHPAD happens |
|
360
|
|
|
|
|
|
|
# OF: overflow flag, if data ran beyond a page |
|
361
|
|
|
|
|
|
|
# PF: partial flag, if we didn't send a full byte |
|
362
|
|
|
|
|
|
|
# E: end address, should be ($pageaddr+$length-1)%32 |
|
363
|
0
|
0
|
|
|
|
|
if ($status & 0x80) { |
|
364
|
|
|
|
|
|
|
# AA flag still set, so the WRITE_SCRATCHPAD hasn't happened since |
|
365
|
|
|
|
|
|
|
# the last COPY_SCRATCHPAD |
|
366
|
0
|
|
|
|
|
|
print "AA flag set\n"; |
|
367
|
0
|
|
|
|
|
|
$c->reset(); |
|
368
|
0
|
|
|
|
|
|
return 0; |
|
369
|
|
|
|
|
|
|
} |
|
370
|
0
|
0
|
|
|
|
|
if ($status & 0x40) { |
|
371
|
|
|
|
|
|
|
# OF flag set, maybe we sent too many bytes, or the pageaddr got |
|
372
|
|
|
|
|
|
|
# garbled to make it look closer to the end of the page |
|
373
|
0
|
|
|
|
|
|
print "OF flag set\n"; |
|
374
|
0
|
|
|
|
|
|
$c->reset(); |
|
375
|
0
|
|
|
|
|
|
return 0; |
|
376
|
|
|
|
|
|
|
} |
|
377
|
0
|
0
|
|
|
|
|
if ($status & 0x20) { |
|
378
|
|
|
|
|
|
|
# PF set, some bits got dropped |
|
379
|
0
|
|
|
|
|
|
print "PF flag set\n"; |
|
380
|
0
|
|
|
|
|
|
$c->reset(); |
|
381
|
0
|
|
|
|
|
|
return 0; |
|
382
|
|
|
|
|
|
|
} |
|
383
|
0
|
0
|
|
|
|
|
if (($status & 0x1f) != ($pageaddr+length($chunk)-1)%32) { |
|
384
|
|
|
|
|
|
|
# addr isn't right |
|
385
|
0
|
|
|
|
|
|
print "addr is ",($status & 0x1f),", should be ", |
|
386
|
|
|
|
|
|
|
($pageaddr+length($chunk)-1)%32,"\n"; |
|
387
|
0
|
|
|
|
|
|
$c->reset(); |
|
388
|
0
|
|
|
|
|
|
return 0; |
|
389
|
|
|
|
|
|
|
} |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# read data out and check it |
|
392
|
0
|
|
|
|
|
|
$c->send("\xff" x length($chunk)); |
|
393
|
0
|
|
|
|
|
|
$buf = $c->read(length($chunk)); |
|
394
|
0
|
0
|
|
|
|
|
if ($buf ne $chunk) { |
|
395
|
|
|
|
|
|
|
# data got corrupted |
|
396
|
0
|
|
|
|
|
|
print "data readback was wrong\n"; |
|
397
|
0
|
|
|
|
|
|
$c->reset(); |
|
398
|
0
|
|
|
|
|
|
return 0; |
|
399
|
|
|
|
|
|
|
} |
|
400
|
0
|
|
|
|
|
|
$c->reset(); |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# looks good |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# copy from scratchpad to NVRAM |
|
405
|
0
|
|
|
|
|
|
$self->select(); |
|
406
|
0
|
|
|
|
|
|
$str = &Hardware::iButton::Connection::COPY_SCRATCHPAD |
|
407
|
|
|
|
|
|
|
. pack("v",$pageaddr) . pack("C", $status); |
|
408
|
0
|
|
|
|
|
|
$c->send($str); |
|
409
|
0
|
|
|
|
|
|
$c->read(1+2+1); |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# wait for it to program.. data book says 30us typ. |
|
412
|
|
|
|
|
|
|
# the device will respond with 1's if it's still programming |
|
413
|
0
|
|
|
|
|
|
usleep(50); |
|
414
|
0
|
|
|
|
|
|
while(1) { |
|
415
|
0
|
|
|
|
|
|
$c->send("\xff"); |
|
416
|
0
|
|
|
|
|
|
$buf = $c->read(1); |
|
417
|
0
|
0
|
|
|
|
|
last if $buf eq "\x00"; |
|
418
|
0
|
|
|
|
|
|
usleep(50*1000); # 50ms |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# read back and verify |
|
422
|
|
|
|
|
|
|
|
|
423
|
0
|
|
|
|
|
|
$c->reset(); |
|
424
|
|
|
|
|
|
|
} |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
sub write_memory_page_loop { |
|
427
|
0
|
|
|
0
|
0
|
|
my($self, $pageaddr, $chunk) = @_; |
|
428
|
|
|
|
|
|
|
# try a couple of times to write |
|
429
|
0
|
|
|
|
|
|
my $times = 3; |
|
430
|
0
|
|
|
|
|
|
my $nwritten; |
|
431
|
0
|
|
|
|
|
|
while ($times) { |
|
432
|
0
|
|
|
|
|
|
print "write(times=$times,pageaddr=$pageaddr,length=",length($chunk),")\n"; |
|
433
|
0
|
|
|
|
|
|
$nwritten = $self->write_memory_page($pageaddr, $chunk); |
|
434
|
0
|
0
|
|
|
|
|
last if $nwritten == length($chunk); |
|
435
|
0
|
|
|
|
|
|
$times--; |
|
436
|
|
|
|
|
|
|
} |
|
437
|
0
|
|
|
|
|
|
return $nwritten; |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
sub write_memory { |
|
441
|
0
|
|
|
0
|
1
|
|
my($self, $addr, $data) = @_; |
|
442
|
0
|
|
|
|
|
|
my $nwritten = 0; |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# find the first chunk boundaries: the scratchpad is like a direct-mapped |
|
445
|
|
|
|
|
|
|
# cache, so we can only copy to a single "page" (32-bytes) at a time. |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
# do we need to write a partial chunk first |
|
448
|
0
|
0
|
|
|
|
|
if ($addr % 32) { |
|
449
|
|
|
|
|
|
|
# yup |
|
450
|
0
|
|
|
|
|
|
my $chunklen = 32 - ($addr % 32); |
|
451
|
0
|
|
|
|
|
|
print "chunklen is $chunklen\n"; |
|
452
|
0
|
|
|
|
|
|
$nwritten += |
|
453
|
|
|
|
|
|
|
$self->write_memory_page_loop($addr, substr($data, 0, $chunklen)); |
|
454
|
0
|
|
|
|
|
|
$addr += $chunklen; |
|
455
|
0
|
|
|
|
|
|
substr($data, 0, $chunklen) = ''; |
|
456
|
|
|
|
|
|
|
} |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
# write chunks |
|
459
|
0
|
|
|
|
|
|
while(length($data)) { |
|
460
|
0
|
0
|
|
|
|
|
my $chunklen = (length($data) > 32) ? 32 : length($data); # max 32 |
|
461
|
0
|
|
|
|
|
|
print "chunklen is $chunklen\n"; |
|
462
|
0
|
|
|
|
|
|
$nwritten += |
|
463
|
|
|
|
|
|
|
$self->write_memory_page_loop($addr, substr($data, 0, $chunklen)); |
|
464
|
0
|
|
|
|
|
|
$addr += $chunklen; |
|
465
|
0
|
|
|
|
|
|
substr($data, 0, $chunklen) = ''; |
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# done! |
|
469
|
0
|
|
|
|
|
|
return $nwritten; |
|
470
|
|
|
|
|
|
|
} |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
package Hardware::iButton::Device::eeprom; |
|
473
|
|
|
|
|
|
|
# this is a class that implements read-eeprom and write-eeprom commands. |
|
474
|
|
|
|
|
|
|
# other device classes can inherit from this one |
|
475
|
1
|
|
|
1
|
|
6
|
use strict; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
32
|
|
|
476
|
1
|
|
|
1
|
|
4
|
use vars qw(@ISA); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
74
|
|
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
# read one byte |
|
479
|
|
|
|
|
|
|
sub read_eeprom { |
|
480
|
0
|
|
|
0
|
|
|
my($self, $addr) = @_; |
|
481
|
|
|
|
|
|
|
} |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
# write one byte |
|
484
|
|
|
|
|
|
|
sub write_eeprom { |
|
485
|
0
|
|
|
0
|
|
|
my($self, $addr, $data) = @_; |
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
package Hardware::iButton::Device::switch; |
|
490
|
1
|
|
|
1
|
|
4
|
use strict; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
34
|
|
|
491
|
1
|
|
|
1
|
|
5
|
use vars qw(@ISA); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
101
|
|
|
492
|
|
|
|
|
|
|
@ISA = qw(Hardware::iButton::Device); |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
#sub on { |
|
495
|
|
|
|
|
|
|
# my ($this) = @_; |
|
496
|
|
|
|
|
|
|
# print $this->verify() . "\n"; |
|
497
|
|
|
|
|
|
|
# $this->select(); |
|
498
|
|
|
|
|
|
|
#} |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
#sub off { |
|
501
|
|
|
|
|
|
|
# my ($this) = @_; |
|
502
|
|
|
|
|
|
|
# print $this->verify() . "\n"; |
|
503
|
|
|
|
|
|
|
# $this->select(); |
|
504
|
|
|
|
|
|
|
#} |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
#sub is_on { |
|
507
|
|
|
|
|
|
|
#} |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
sub toggle { |
|
510
|
0
|
|
|
0
|
|
|
my ($this) = @_; |
|
511
|
0
|
|
|
|
|
|
$this->select(); |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
package Hardware::iButton::Device::DS1920; |
|
516
|
|
|
|
|
|
|
|
|
517
|
1
|
|
|
1
|
|
5
|
use Hardware::iButton::Connection; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
25
|
|
|
518
|
1
|
|
|
1
|
|
4
|
use Time::HiRes qw(usleep); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
4
|
|
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
# this is the thermometer button. |
|
521
|
1
|
|
|
1
|
|
75
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
31
|
|
|
522
|
1
|
|
|
1
|
|
3
|
use vars qw(@ISA); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
529
|
|
|
523
|
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
@ISA = qw(Hardware::iButton::Device); |
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
sub read_temperature_scratchpad { |
|
527
|
0
|
|
|
0
|
|
|
my($self) = @_; |
|
528
|
0
|
|
|
|
|
|
my $c = $self->{'connection'}; |
|
529
|
|
|
|
|
|
|
|
|
530
|
0
|
|
|
|
|
|
$c->reset(); |
|
531
|
0
|
|
|
|
|
|
$c->mode(&Hardware::iButton::Connection::SET_COMMAND_MODE); |
|
532
|
0
|
|
|
|
|
|
$c->write("\x39"); # set a 524ms pullup |
|
533
|
0
|
|
|
|
|
|
$c->read(1); # response to config command |
|
534
|
0
|
|
|
|
|
|
$c->reset(); |
|
535
|
0
|
|
|
|
|
|
$self->select(); |
|
536
|
0
|
|
|
|
|
|
$c->mode(&Hardware::iButton::Connection::SET_COMMAND_MODE); |
|
537
|
0
|
|
|
|
|
|
$c->write("\xef"); # arm the pullup |
|
538
|
0
|
|
|
|
|
|
$c->write("\xf1"); # terminate pulse (??) |
|
539
|
0
|
|
|
|
|
|
$c->read(1); # response to 0xf1 |
|
540
|
0
|
|
|
|
|
|
$c->mode(&Hardware::iButton::Connection::SET_DATA_MODE); |
|
541
|
0
|
|
|
|
|
|
$c->send("\x44"); # start conversion. need to do a 0.5s strong pullup. |
|
542
|
0
|
|
|
|
|
|
$c->read(1); # read back 0x44 |
|
543
|
|
|
|
|
|
|
# wait |
|
544
|
0
|
|
|
|
|
|
usleep(600*1000); # wait .6s |
|
545
|
0
|
|
|
|
|
|
$c->mode(&Hardware::iButton::Connection::SET_COMMAND_MODE); |
|
546
|
0
|
|
|
|
|
|
$c->write("\xed"); # disarm pullup |
|
547
|
0
|
|
|
|
|
|
$c->write("\xf1"); # terminate pulse |
|
548
|
0
|
|
|
|
|
|
$c->read(1); # response?? |
|
549
|
|
|
|
|
|
|
|
|
550
|
0
|
|
|
|
|
|
$c->reset(); |
|
551
|
0
|
|
|
|
|
|
$self->select(); |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
# read scratchpad, bytes 0 and 1 (LSB and MSB) |
|
554
|
0
|
|
|
|
|
|
$c->send("\xbe"); $c->read(1); |
|
|
0
|
|
|
|
|
|
|
|
555
|
0
|
|
|
|
|
|
$c->send("\xff" x 9); |
|
556
|
0
|
|
|
|
|
|
my $scratchpad = $c->read(9); |
|
557
|
0
|
|
|
|
|
|
$c->reset(); |
|
558
|
|
|
|
|
|
|
# check CRC in last byte. |
|
559
|
0
|
0
|
|
|
|
|
if (Hardware::iButton::Connection::crc(0, split(//,$scratchpad))) { |
|
560
|
0
|
|
|
|
|
|
warn("scratchpadcrc was wrong"); |
|
561
|
|
|
|
|
|
|
} |
|
562
|
0
|
|
|
|
|
|
return $scratchpad; |
|
563
|
|
|
|
|
|
|
} |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
=head2 read_temperature |
|
566
|
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
$temp = $b->read_temperature(); |
|
568
|
|
|
|
|
|
|
$temp = $b->read_temperature_hires(); |
|
569
|
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
These methods can be used on DS1820/DS1920 Thermometer iButtons. They return |
|
571
|
|
|
|
|
|
|
a temperature in degrees C. The range is -55C to +100C, the resolution of the |
|
572
|
|
|
|
|
|
|
first is 0.5C, the resolution of the second is about 0.01C. The accuracy is |
|
573
|
|
|
|
|
|
|
about +/- 0.5C. |
|
574
|
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
Useful conversions: C<$f = $c*9/5 + 32>, C<$c = ($f-32)*5/9> . |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
=cut |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
sub read_temperature { |
|
581
|
0
|
|
|
0
|
|
|
my($self) = @_; |
|
582
|
0
|
|
|
|
|
|
my $scratchpad = $self->read_temperature_scratchpad($self); |
|
583
|
0
|
|
|
|
|
|
my $tempnumber = unpack("v",substr($scratchpad, 0, 2)); |
|
584
|
|
|
|
|
|
|
# now, that's really supposed to be a signed 16-bit little-endian |
|
585
|
|
|
|
|
|
|
# quantity, but there isn't a pack() code for such things. |
|
586
|
|
|
|
|
|
|
#printf("tempnumber as read is 0x%04x\n",$tempnumber); |
|
587
|
0
|
0
|
|
|
|
|
$tempnumber -= 0x10000 if $tempnumber > 0x8000; |
|
588
|
0
|
|
|
|
|
|
my $temp = $tempnumber / 2; |
|
589
|
0
|
|
|
|
|
|
return $temp; |
|
590
|
|
|
|
|
|
|
} |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
sub read_temperature_hires { |
|
593
|
0
|
|
|
0
|
|
|
my($self) = @_; |
|
594
|
0
|
|
|
|
|
|
my $scratchpad = $self->read_temperature_scratchpad($self); |
|
595
|
0
|
|
|
|
|
|
my $tempnumber = unpack("v",substr($scratchpad, 0, 2)); |
|
596
|
|
|
|
|
|
|
# now, that's really supposed to be a signed 16-bit little-endian |
|
597
|
|
|
|
|
|
|
# quantity, but there isn't a pack() code for such things. |
|
598
|
0
|
|
|
|
|
|
my $count_per_c = ord(substr($scratchpad, 7, 1)); |
|
599
|
0
|
|
|
|
|
|
my $count_remaining = ord(substr($scratchpad, 6, 1)); |
|
600
|
|
|
|
|
|
|
#printf("tempnumber as read is 0x%04x\n",$tempnumber); |
|
601
|
0
|
|
|
|
|
|
$tempnumber &= 0xfffe; # truncate LSB |
|
602
|
0
|
0
|
|
|
|
|
$tempnumber -= 0x10000 if $tempnumber > 0x8000; |
|
603
|
0
|
|
|
|
|
|
my $temp = ($tempnumber / 2) - 0.25 + |
|
604
|
|
|
|
|
|
|
($count_per_c - $count_remaining) / $count_per_c; |
|
605
|
0
|
|
|
|
|
|
return $temp; |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
package Hardware::iButton::Device::JavaButton; |
|
609
|
1
|
|
|
1
|
|
6
|
use strict; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
29
|
|
|
610
|
1
|
|
|
1
|
|
4
|
use vars qw(@ISA); |
|
|
1
|
|
|
|
|
6
|
|
|
|
1
|
|
|
|
|
28
|
|
|
611
|
|
|
|
|
|
|
|
|
612
|
1
|
|
|
1
|
|
4
|
use Hardware::iButton::Connection; |
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
20
|
|
|
613
|
1
|
|
|
1
|
|
4
|
use Time::HiRes qw(usleep); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
3
|
|
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
# this is the Java button. |
|
616
|
|
|
|
|
|
|
@ISA = qw(Hardware::iButton::Device); |
|
617
|
|
|
|
|
|
|
|
|
618
|
0
|
|
|
0
|
|
|
sub send_apdu { |
|
619
|
|
|
|
|
|
|
} |
|
620
|
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
# an APDU is just a specially formatted buffer. a Command APDU is sent to the |
|
622
|
|
|
|
|
|
|
# button, which responds with a Response APDU. |
|
623
|
|
|
|
|
|
|
# Command APDU: |
|
624
|
|
|
|
|
|
|
# byte header[4]; // CLA, INS, P1, P2 |
|
625
|
|
|
|
|
|
|
# byte Lc; |
|
626
|
|
|
|
|
|
|
# byte *Data; |
|
627
|
|
|
|
|
|
|
# byte Le; |
|
628
|
|
|
|
|
|
|
# Response APDU |
|
629
|
|
|
|
|
|
|
# word Len; |
|
630
|
|
|
|
|
|
|
# byte *Data; |
|
631
|
|
|
|
|
|
|
# word SW; // status word |
|
632
|
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
# wrappers for those APDUs |
|
634
|
|
|
|
|
|
|
# Command Packet |
|
635
|
|
|
|
|
|
|
# byte len; |
|
636
|
|
|
|
|
|
|
# byte cmdbyte; |
|
637
|
|
|
|
|
|
|
# byte groupid; |
|
638
|
|
|
|
|
|
|
# byte cmddata[max=255] |
|
639
|
|
|
|
|
|
|
# Return Packet |
|
640
|
|
|
|
|
|
|
# byte CSB |
|
641
|
|
|
|
|
|
|
# byte groupid |
|
642
|
|
|
|
|
|
|
# byte datalen |
|
643
|
|
|
|
|
|
|
# byte cmddata[max=2048] |
|
644
|
|
|
|
|
|
|
|
|
645
|
0
|
|
|
0
|
|
|
sub get_firmware_version_string { |
|
646
|
|
|
|
|
|
|
# apdu: class 0xd0, instruction 0x95, parm1 0x01, parm2 0x00 |
|
647
|
|
|
|
|
|
|
# header = "\xd0\x95\x01\x00" |
|
648
|
|
|
|
|
|
|
# Lc = "\x00", data is uninitialized |
|
649
|
|
|
|
|
|
|
# Le = "\x00" |
|
650
|
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
# cmdbyte = 137, groupid = 0, len = 3+4 (3+apdu header) + 1 + lc + 1 |
|
652
|
|
|
|
|
|
|
# data = header . lc . [lc bytes of data] . le |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
# SendAPDU() |
|
655
|
|
|
|
|
|
|
# so arg to sendcibmessage is: |
|
656
|
|
|
|
|
|
|
# len . cmdbyte(137) . groupid(0) . |
|
657
|
|
|
|
|
|
|
# [header(4bytes) . lc . data(lc bytes) . le] |
|
658
|
|
|
|
|
|
|
# sendcibmessage(data, len+1) |
|
659
|
|
|
|
|
|
|
# recvcibmessage |
|
660
|
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
#an apdu has a 4-byte header: class, instruction, parm1, parm2. then lots |
|
662
|
|
|
|
|
|
|
#of random data. class >= 0xd0 is for the ring itself, otherwise it is passed |
|
663
|
|
|
|
|
|
|
#to the applet (which one?) |
|
664
|
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
} |
|
666
|
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
# Autoload methods go after =cut, and are processed by the autosplit program. |
|
668
|
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
1; |
|
670
|
|
|
|
|
|
|
__END__ |