summaryrefslogtreecommitdiff
path: root/tools/iap/Device
diff options
context:
space:
mode:
Diffstat (limited to 'tools/iap/Device')
-rw-r--r--tools/iap/Device/iPod.pm386
1 files changed, 386 insertions, 0 deletions
diff --git a/tools/iap/Device/iPod.pm b/tools/iap/Device/iPod.pm
new file mode 100644
index 0000000000..b2d686cce7
--- /dev/null
+++ b/tools/iap/Device/iPod.pm
@@ -0,0 +1,386 @@
1package Device::iPod;
2
3use Device::SerialPort;
4use POSIX qw(isgraph);
5use strict;
6
7sub new {
8 my $class = shift;
9 my $port = shift;
10 my $self = {};
11 my $s;
12
13 $self->{-serial} = undef;
14 $self->{-inbuf} = '';
15 $self->{-error} = undef;
16 $self->{-baudrate} = 57600;
17 $self->{-debug} = 0;
18
19 return bless($self, $class);
20}
21
22sub open {
23 my $self = shift;
24 my $port = shift;
25
26 $self->{-serial} = new Device::SerialPort($port);
27 unless(defined($self->{-serial})) {
28 $self->{-error} = $!;
29 return undef;
30 }
31
32 $self->{-serial}->parity('none');
33 $self->{-serial}->databits(8);
34 $self->{-serial}->stopbits(1);
35 $self->{-serial}->handshake('none');
36 return $self->baudrate($self->{-baudrate});
37}
38
39sub baudrate {
40 my $self = shift;
41 my $baudrate = shift;
42
43 if ($baudrate < 1) {
44 $self->{-error} = "Invalid baudrate";
45 return undef;
46 }
47
48 $self->{-baudrate} = $baudrate;
49 if (defined($self->{-serial})) {
50 $self->{-serial}->baudrate($baudrate);
51 }
52
53 return 1;
54}
55
56sub sendmsg {
57 my $self = shift;
58 my $lingo = shift;
59 my $command = shift;
60 my $data = shift || '';
61
62 return $self->_nosetup() unless(defined($self->{-serial}));
63
64 if (($lingo < 0) || ($lingo > 255)) {
65 $self->{-error} = 'Invalid lingo';
66 return undef;
67 }
68
69 if ($command < 0) {
70 $self->{-error} = 'Invalid command';
71 return undef;
72 }
73
74 if ($lingo == 4) {
75 if ($command > 0xffff) {
76 $self->{-error} = 'Invalid command';
77 return undef;
78 }
79 return $self->_send($self->_frame_cmd(pack("Cn", $lingo, $command) . $data));
80 } else {
81 if ($command > 0xff) {
82 $self->{-error} = 'Invalid command';
83 return undef;
84 }
85 return $self->_send($self->_frame_cmd(pack("CC", $lingo, $command) . $data));
86 }
87}
88
89sub sendraw {
90 my $self = shift;
91 my $data = shift;
92
93 return $self->_nosetup() unless(defined($self->{-serial}));
94
95 return $self->_send($data);
96}
97
98sub recvmsg {
99 my $self = shift;
100 my $m;
101 my @m;
102
103 return $self->_nosetup() unless(defined($self->{-serial}));
104
105 $m = $self->_fillbuf();
106 unless(defined($m)) {
107 # Error was set by lower levels
108 return wantarray?():undef;
109 }
110
111 printf("Fetched %s\n", $self->_hexstring($m)) if $self->{-debug};
112
113 @m = $self->_unframe_cmd($m);
114
115 unless(@m) {
116 return undef;
117 }
118
119 if (wantarray()) {
120 return @m;
121 } else {
122 return {-lingo => $m[0], -cmd => $m[1], -payload => $m[2]};
123 }
124}
125
126sub emptyrecv {
127 my $self = shift;
128 my $m;
129
130 while ($m = $self->_fillbuf()) {
131 printf("Discarded %s\n", $self->_hexstring($m)) if (defined($m) && $self->{-debug});
132 }
133}
134
135sub error {
136 my $self = shift;
137
138 return $self->{-error};
139}
140
141sub _nosetup {
142 my $self = shift;
143
144 $self->{-error} = 'Serial port not setup';
145 return undef;
146}
147
148sub _frame_cmd {
149 my $self = shift;
150 my $data = shift;
151 my $l = length($data);
152 my $csum;
153
154 if ($l > 0xffff) {
155 $self->{-error} = 'Command too long';
156 return undef;
157 }
158
159 if ($l > 255) {
160 $data = pack("Cn", 0, length($data)) . $data;
161 } else {
162 $data = pack("C", length($data)) . $data;
163 }
164
165 foreach (unpack("C" x length($data), $data)) {
166 $csum += $_;
167 }
168 $csum &= 0xFF;
169 $csum = 0x100 - $csum;
170
171 return "\xFF\x55" . $data . pack("C", $csum);
172}
173
174sub _unframe_cmd {
175 my $self = shift;
176 my $data = shift;
177 my $payload = '';
178 my ($count, $length, $csum);
179 my $state = 0;
180 my $c;
181 my ($lingo, $cmd);
182
183 return () unless(defined($data));
184
185 foreach $c (unpack("C" x length($data), $data)) {
186 if ($state == 0) {
187 # Wait for sync
188 next unless($c == 255);
189 $state = 1;
190 } elsif ($state == 1) {
191 # Wait for sop
192 next unless($c == 85);
193 $state = 2;
194 } elsif ($state == 2) {
195 # Length (short frame)
196 $csum = $c;
197 if ($c == 0) {
198 # Large frame
199 $state = 3;
200 } else {
201 $state = 5;
202 }
203 $length = $c;
204 $count = 0;
205 next;
206 } elsif ($state == 3) {
207 # Large frame, hi
208 $csum += $c;
209 $length = ($c << 8);
210 $state = 4;
211 next;
212 } elsif ($state == 4) {
213 # Large frame, lo
214 $csum += $c;
215 $length |= $c;
216 if ($length == 0) {
217 $self->{-error} = 'Length is 0';
218 return ();
219 }
220 $state = 5;
221 next;
222 } elsif ($state == 5) {
223 # Data bytes
224 $csum += $c;
225 $payload .= chr($c);
226 $count += 1;
227 if ($count == $length) {
228 $state = 6;
229 }
230 } elsif ($state == 6) {
231 # Checksum byte
232 $csum += $c;
233 if (($csum & 0xFF) != 0) {
234 $self->{-error} = 'Invalid checksum';
235 return ();
236 }
237 $state = 7;
238 last;
239 } else {
240 $self->{-error} = 'Invalid state';
241 return ();
242 }
243 }
244
245 # If we get here, we either have data or not. Check.
246 if ($state != 7) {
247 $self->{-error} = 'Could not unframe data';
248 return ();
249 }
250
251 $lingo = unpack("C", $payload);
252 if ($lingo == 4) {
253 return unpack("Cna*", $payload);
254 } else {
255 return unpack("CCa*", $payload);
256 }
257}
258
259sub _send {
260 my $self = shift;
261 my $data = shift;
262 my $l = length($data);
263 my $c;
264
265 printf("Sending %s\n", $self->_hexstring($data)) if $self->{-debug};
266
267 $c = $self->{-serial}->write($data);
268 unless(defined($c)) {
269 $self->{-error} = 'write failed';
270 return undef;
271 }
272
273 if ($c != $l) {
274 $self->{-error} = 'incomplete write';
275 return undef;
276 }
277
278 return 1;
279}
280
281sub _fillbuf {
282 my $self = shift;
283 my $timeout = shift || 2;
284 my $to;
285
286 # Read from the port until we have a complete message in the buffer,
287 # or until we haven't read any new data for $timeout seconds, whatever
288 # comes first.
289
290 $to = $timeout;
291
292 while(!$self->_message_in_buffer() && $to > 0) {
293 my ($c, $s) = $self->{-serial}->read(255);
294 if ($c == 0) {
295 # No data read
296 select(undef, undef, undef, 0.1);
297 $to -= 0.1;
298 } else {
299 $self->{-inbuf} .= $s;
300 $to = $timeout;
301 }
302 }
303 if ($self->_message_in_buffer()) {
304 # There is a complete message in the buffer
305 return $self->_message();
306 } else {
307 # Timeout occured
308 $self->{-error} = 'Timeout reading from port';
309 return undef;
310 }
311}
312
313sub _message_in_buffer {
314 my $self = shift;
315 my $sp = 0;
316 my $i;
317
318 $i = index($self->{-inbuf}, "\xFF\x55", $sp);
319 while ($i != -1) {
320 my $header;
321 my $len;
322 my $large = 0;
323
324
325 $header = substr($self->{-inbuf}, $i, 3);
326 if (length($header) != 3) {
327 # Runt frame
328 return ();
329 }
330 $len = unpack("x2C", $header);
331 if ($len == 0) {
332 # Possible large frame
333 $header = substr($self->{-inbuf}, $i, 5);
334 if (length($header) != 5) {
335 # Runt frame
336 return ();
337 }
338 $large = 1;
339 $len = unpack("x3n", $header);
340 }
341
342 # Add framing, checksum and length
343 $len = $len+3+($large?3:1);
344
345 if (length($self->{-inbuf}) < ($i+$len)) {
346 # Buffer too short to hold rest of frame. Try again.
347 $sp = $i+1;
348 $i = index($self->{-inbuf}, "\xFF\x55", $sp);
349 } else {
350 return ($i, $len);
351 }
352 }
353
354 # No complete message found
355 return ();
356}
357
358
359sub _message {
360 my $self = shift;
361 my $start;
362 my $len;
363 my $m;
364
365 # Return the first complete message in the buffer, removing the message
366 # and everything before it from the buffer.
367 ($start, $len) = $self->_message_in_buffer();
368 unless(defined($start)) {
369 $self->{-error} = 'No complete message in buffer';
370 return undef;
371 }
372 $m = substr($self->{-inbuf}, $start, $len);
373 $self->{-inbuf} = substr($self->{-inbuf}, $start+$len);
374
375 return $m;
376}
377
378sub _hexstring {
379 my $self = shift;
380 my $s = shift;
381
382 return join("", map { (($_ == 0x20) || isgraph(chr($_)))?chr($_):sprintf("\\x%02x", $_) }
383 unpack("C" x length($s), $s));
384}
385
3861;