diff options
Diffstat (limited to 'tools/iap')
-rw-r--r-- | tools/iap/Device/iPod.pm | 386 | ||||
-rw-r--r-- | tools/iap/Makefile | 7 | ||||
-rw-r--r-- | tools/iap/README | 23 | ||||
-rw-r--r-- | tools/iap/device-ipod.t | 74 | ||||
-rw-r--r-- | tools/iap/iap-verbose.pl | 1856 | ||||
-rw-r--r-- | tools/iap/ipod-001-general.t | 133 | ||||
-rw-r--r-- | tools/iap/ipod-002-lingo0.t | 277 | ||||
-rw-r--r-- | tools/iap/ipod-003-lingo2.t | 220 |
8 files changed, 2976 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 @@ | |||
1 | package Device::iPod; | ||
2 | |||
3 | use Device::SerialPort; | ||
4 | use POSIX qw(isgraph); | ||
5 | use strict; | ||
6 | |||
7 | sub 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 | |||
22 | sub 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 | |||
39 | sub 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 | |||
56 | sub 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 | |||
89 | sub 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 | |||
98 | sub 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 | |||
126 | sub 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 | |||
135 | sub error { | ||
136 | my $self = shift; | ||
137 | |||
138 | return $self->{-error}; | ||
139 | } | ||
140 | |||
141 | sub _nosetup { | ||
142 | my $self = shift; | ||
143 | |||
144 | $self->{-error} = 'Serial port not setup'; | ||
145 | return undef; | ||
146 | } | ||
147 | |||
148 | sub _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 | |||
174 | sub _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 | |||
259 | sub _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 | |||
281 | sub _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 | |||
313 | sub _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 | |||
359 | sub _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 | |||
378 | sub _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 | |||
386 | 1; | ||
diff --git a/tools/iap/Makefile b/tools/iap/Makefile new file mode 100644 index 0000000000..86f3760de6 --- /dev/null +++ b/tools/iap/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | default: test | ||
2 | |||
3 | test: | ||
4 | perl -MTest::Harness -e '$$Test::Harness::verbose=1; runtests @ARGV' ipod*.t | ||
5 | |||
6 | moduletest: | ||
7 | perl -MTest::Harness -e '$$Test::Harness::verbose=1; runtests @ARGV' device-ipod.t | ||
diff --git a/tools/iap/README b/tools/iap/README new file mode 100644 index 0000000000..dbd050feb4 --- /dev/null +++ b/tools/iap/README | |||
@@ -0,0 +1,23 @@ | |||
1 | These are perl test scripts for validating the IAP implementation. | ||
2 | Also included is a perl class for talking to an iPod via the serial | ||
3 | port. You will probably need Linux to use this. | ||
4 | |||
5 | Run "make moduletest" to test the perl module itself. This will not | ||
6 | require any serial connection, or even an iPod, for that matter. | ||
7 | |||
8 | Run "make test" to run the iPod communication tests themselves. | ||
9 | |||
10 | In order to test make sure | ||
11 | |||
12 | - the iPod is connected to a serial port | ||
13 | - the test scripts assume that this port is /dev/ttyUSB0. Change | ||
14 | as neccessary | ||
15 | |||
16 | Sometimes, tests will time out instead of giving the desired result. | ||
17 | As long as the timeouts are not reproducable this is usually not a | ||
18 | problem. The serial port is known to be unreliable, and devices will | ||
19 | retransmit. This happens even with the OF. | ||
20 | |||
21 | The tests were designed against an iPod Touch 2G as a reference device. | ||
22 | Some older iPods fail some of the test, even with the OF, because of | ||
23 | behaviour changes in later firmware releases by Apple. | ||
diff --git a/tools/iap/device-ipod.t b/tools/iap/device-ipod.t new file mode 100644 index 0000000000..607184e331 --- /dev/null +++ b/tools/iap/device-ipod.t | |||
@@ -0,0 +1,74 @@ | |||
1 | use Test::More qw( no_plan ); | ||
2 | use strict; | ||
3 | |||
4 | BEGIN { use_ok('Device::iPod'); } | ||
5 | require_ok('Device::iPod'); | ||
6 | |||
7 | my $ipod = Device::iPod->new(); | ||
8 | my $m; | ||
9 | my ($l, $c, $p); | ||
10 | |||
11 | isa_ok($ipod, 'Device::iPod'); | ||
12 | |||
13 | # Frame a short command | ||
14 | $m = $ipod->_frame_cmd("\x00\x02\x00\x06"); | ||
15 | ok(defined($m) && ($m eq "\xFF\x55\x04\x00\x02\x00\x06\xF4"), "Framed command valid"); | ||
16 | |||
17 | # Frame a long command | ||
18 | $m = $ipod->_frame_cmd("\x00" x 1024); | ||
19 | ok(defined($m) && ($m eq "\xFF\x55\x00\x04\x00" . ("\x00" x 1024) . "\xFC"), "Long framed command valid"); | ||
20 | |||
21 | # Frame an overly long command | ||
22 | $m = $ipod->_frame_cmd("\x00" x 65537); | ||
23 | ok(!defined($m) && ($ipod->error() =~ 'Command too long'), "Overly long command failed"); | ||
24 | |||
25 | # Unframe a short command | ||
26 | ($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x04\x00\x02\x00\x06\xF4"); | ||
27 | ok(defined($l) && ($l == 0x00) && ($c == 0x02) && ($p eq "\x00\x06"), "Unframed short command"); | ||
28 | |||
29 | # Unframe a long command | ||
30 | ($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x00\x04\x00" . ("\x00" x 1024) . "\xFC"); | ||
31 | ok(defined($l) && ($l == 0x00) && ($c == 0x00) && ($p eq "\x00" x 1022), "Unframed long command"); | ||
32 | |||
33 | # Frame without sync byte | ||
34 | ($l, $c, $p) = $ipod->_unframe_cmd("\x00"); | ||
35 | ok(!defined($l) && ($ipod->error() =~ /Could not unframe data/), "Frame without sync byte failed"); | ||
36 | |||
37 | # Frame without SOP byte | ||
38 | ($l, $c, $p) = $ipod->_unframe_cmd("\xFF"); | ||
39 | ok(!defined($l) && ($ipod->error() =~ /Could not unframe data/), "Frame without SOP byte failed"); | ||
40 | |||
41 | # Frame with length 0 | ||
42 | ($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x00\x00\x00"); | ||
43 | ok(!defined($l) && ($ipod->error() =~ /Length is 0/), "Frame with length 0 failed"); | ||
44 | |||
45 | # Too short frame | ||
46 | ($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x03\x00\x00"); | ||
47 | ok(!defined($l) && ($ipod->error() =~ /Could not unframe data/), "Too short frame failed"); | ||
48 | |||
49 | # Invalid checksum | ||
50 | ($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x03\x00\x00\x00\x00"); | ||
51 | ok(!defined($l) && ($ipod->error() =~ /Invalid checksum/), "Invalid checksum failed"); | ||
52 | |||
53 | # Find a message in a string | ||
54 | $ipod->{-inbuf} = "\x00\xFF\x55\x00\xFF\xFF\xFF\x55\x04\x00\x02\x00\x06\xF4"; | ||
55 | ($c, $l) = $ipod->_message_in_buffer(); | ||
56 | ok(defined($l) && ($c == 6) && ($l == 8), "Found message in buffer"); | ||
57 | |||
58 | # Return message from a string | ||
59 | $ipod->{-inbuf} = "\x00\xFF\x55\x00\xFF\xFF\xFF\x55\x04\x00\x02\x00\x06\xF4\x00"; | ||
60 | $m = $ipod->_message(); | ||
61 | ok(defined($m) && ($ipod->{-inbuf} eq "\x00"), "Retrieved message from buffer"); | ||
62 | |||
63 | # Return two messages from buffer | ||
64 | $ipod->{-inbuf} = "\xffU\x04\x00\x02\x00\x13\xe7\xffU\x02\x00\x14\xea"; | ||
65 | $m = $ipod->_message(); | ||
66 | subtest "First message" => sub { | ||
67 | ok(defined($m), "Message received"); | ||
68 | is($m, "\xffU\x04\x00\x02\x00\x13\xe7"); | ||
69 | }; | ||
70 | $m = $ipod->_message(); | ||
71 | subtest "Second message" => sub { | ||
72 | ok(defined($m), "Message received"); | ||
73 | is($m, "\xffU\x02\x00\x14\xea"); | ||
74 | }; | ||
diff --git a/tools/iap/iap-verbose.pl b/tools/iap/iap-verbose.pl new file mode 100644 index 0000000000..ed25de7548 --- /dev/null +++ b/tools/iap/iap-verbose.pl | |||
@@ -0,0 +1,1856 @@ | |||
1 | #!/usr/bin/perl -w | ||
2 | |||
3 | package iap::decode; | ||
4 | |||
5 | use Device::iPod; | ||
6 | use Data::Dumper; | ||
7 | use strict; | ||
8 | |||
9 | sub new { | ||
10 | my $class = shift; | ||
11 | my $self = {-state => {}}; | ||
12 | |||
13 | return bless($self, $class); | ||
14 | } | ||
15 | |||
16 | sub display { | ||
17 | my $self = shift; | ||
18 | my $lingo = shift; | ||
19 | my $command = shift; | ||
20 | my $data = shift; | ||
21 | my $name; | ||
22 | my $handler; | ||
23 | my $r; | ||
24 | |||
25 | |||
26 | $name = sprintf("_h_%02x_%04x", $lingo, $command); | ||
27 | $handler = $self->can($name); | ||
28 | if ($handler) { | ||
29 | unless(exists($self->{-state}->{$name})) { | ||
30 | $self->{-state}->{$name} = {}; | ||
31 | } | ||
32 | $r = $handler->($self, $data, $self->{-state}->{$name}); | ||
33 | } else { | ||
34 | $r = $self->generic($lingo, $command, $data); | ||
35 | } | ||
36 | |||
37 | printf("\n"); | ||
38 | return $r; | ||
39 | } | ||
40 | |||
41 | sub generic { | ||
42 | my $self = shift; | ||
43 | my $lingo = shift; | ||
44 | my $command = shift; | ||
45 | my $data = shift; | ||
46 | |||
47 | printf("Unknown command\n"); | ||
48 | printf(" Lingo: 0x%02x\n", $lingo); | ||
49 | printf(" Command 0x%04x\n", $command); | ||
50 | printf(" Data: %s\n", Device::iPod->_hexstring($data)); | ||
51 | |||
52 | exit(1); | ||
53 | |||
54 | return 1; | ||
55 | } | ||
56 | |||
57 | sub _h_00_0001 { | ||
58 | my $self = shift; | ||
59 | my $data = shift; | ||
60 | my $state = shift; | ||
61 | my $lingo = shift; | ||
62 | |||
63 | $lingo = unpack("C", $data); | ||
64 | |||
65 | printf("Identify (0x00, 0x01) D->I\n"); | ||
66 | printf(" Lingo: 0x%02x\n", $lingo); | ||
67 | |||
68 | return 1; | ||
69 | } | ||
70 | |||
71 | sub _h_00_0002 { | ||
72 | my $self = shift; | ||
73 | my $data = shift; | ||
74 | my $state = shift; | ||
75 | my $res; | ||
76 | my $cmd; | ||
77 | my $delay; | ||
78 | |||
79 | ($res, $cmd, $delay) = unpack("CCN", $data); | ||
80 | |||
81 | printf("ACK (0x00, 0x02) I->D\n"); | ||
82 | printf(" Acknowledged command: 0x%02x\n", $cmd); | ||
83 | printf(" Status: %s (%d)\n", | ||
84 | ("Success", | ||
85 | "ERROR: Unknown Database Category", | ||
86 | "ERROR: Command Failed", | ||
87 | "ERROR: Out Of Resource", | ||
88 | "ERROR: Bad Parameter", | ||
89 | "ERROR: Unknown ID", | ||
90 | "Command Pending", | ||
91 | "ERROR: Not Authenticated", | ||
92 | "ERROR: Bad Authentication Version", | ||
93 | "ERROR: Accessory Power Mode Request Failed", | ||
94 | "ERROR: Certificate Invalid", | ||
95 | "ERROR: Certificate permissions invalid", | ||
96 | "ERROR: File is in use", | ||
97 | "ERROR: Invalid file handle", | ||
98 | "ERROR: Directory not empty", | ||
99 | "ERROR: Operation timed out", | ||
100 | "ERROR: Command unavailable in this iPod mode", | ||
101 | "ERROR: Invalid accessory resistor ID", | ||
102 | "Reserved", | ||
103 | "Reserved", | ||
104 | "Reserved", | ||
105 | "ERROR: Maximum number of accessory connections already reached")[$res], $res); | ||
106 | if ($res == 6) { | ||
107 | $delay = unpack("xxN", $data); | ||
108 | printf(" Delay: %d ms\n", $delay); | ||
109 | } | ||
110 | |||
111 | return 1; | ||
112 | } | ||
113 | |||
114 | sub _h_00_0005 { | ||
115 | my $self = shift; | ||
116 | my $data = shift; | ||
117 | my $state = shift; | ||
118 | |||
119 | printf("EnterRemoteUIMode (0x00, 0x05) D->I\n"); | ||
120 | |||
121 | return 1; | ||
122 | } | ||
123 | |||
124 | sub _h_00_000d { | ||
125 | my $self = shift; | ||
126 | my $data = shift; | ||
127 | my $state = shift; | ||
128 | |||
129 | printf("RequestiPodModelNum (0x00, 0x0D) D->I\n"); | ||
130 | |||
131 | return 1; | ||
132 | } | ||
133 | |||
134 | sub _h_00_000e { | ||
135 | my $self = shift; | ||
136 | my $data = shift; | ||
137 | my $state = shift; | ||
138 | my ($modelnum, $name); | ||
139 | |||
140 | ($modelnum, $name) = unpack("NZ*", $data); | ||
141 | |||
142 | printf("ReturniPodModelNum (0x00, 0x0E) I->D\n"); | ||
143 | printf(" Model number: %08x\n", $modelnum); | ||
144 | printf(" Model name: %s\n", $name); | ||
145 | |||
146 | return 1; | ||
147 | } | ||
148 | |||
149 | sub _h_00_000f { | ||
150 | my $self = shift; | ||
151 | my $data = shift; | ||
152 | my $state = shift; | ||
153 | my $lingo; | ||
154 | |||
155 | $lingo = unpack("C", $data); | ||
156 | |||
157 | printf("RequestLingoProtocolVersion (0x00, 0x0F) D->I\n"); | ||
158 | printf(" Lingo: 0x%02x\n", $lingo); | ||
159 | |||
160 | return 1; | ||
161 | } | ||
162 | |||
163 | sub _h_00_0010 { | ||
164 | my $self = shift; | ||
165 | my $data = shift; | ||
166 | my $state = shift; | ||
167 | my ($lingo, $maj, $min); | ||
168 | |||
169 | ($lingo, $maj, $min) = unpack("CCC", $data); | ||
170 | |||
171 | printf("ReturnLingoProtocolVersion (0x00, 0x10) I->D\n"); | ||
172 | printf(" Lingo: 0x%02x\n", $lingo); | ||
173 | printf(" Version: %d.%02d\n", $maj, $min); | ||
174 | |||
175 | return 1; | ||
176 | } | ||
177 | |||
178 | |||
179 | sub _h_00_0013 { | ||
180 | my $self = shift; | ||
181 | my $data = shift; | ||
182 | my $state = shift; | ||
183 | my @lingolist; | ||
184 | my ($lingoes, $options, $devid); | ||
185 | |||
186 | ($lingoes, $options, $devid) = unpack("N3", $data); | ||
187 | |||
188 | foreach (0..31) { | ||
189 | push(@lingolist, $_) if ($lingoes & (1 << $_)); | ||
190 | } | ||
191 | |||
192 | printf("IdentifyDeviceLingoes (0x00, 0x13) D->I\n"); | ||
193 | printf(" Supported lingoes: %s\n", join(", ", @lingolist)); | ||
194 | printf(" Options:\n"); | ||
195 | printf(" Authentication: %s\n", ("None", "Defer (1.0)", "Immediate (2.0)", "Reserved")[$options & 0x03]); | ||
196 | printf(" Power: %s\n", ("Low", "Intermittent high", "Reserved", "Constant high")[($options & 0x0C) >> 2]); | ||
197 | printf(" Device ID: 0x%08x\n", $devid); | ||
198 | |||
199 | delete($self->{-state}->{'_h_00_0015'}); | ||
200 | |||
201 | return 1; | ||
202 | } | ||
203 | |||
204 | sub _h_00_0014 { | ||
205 | my $self = shift; | ||
206 | my $data = shift; | ||
207 | my $state = shift; | ||
208 | |||
209 | printf("GetDevAuthenticationInfo (0x00, 0x14) I->D\n"); | ||
210 | |||
211 | return 1; | ||
212 | } | ||
213 | |||
214 | sub _h_00_0015 { | ||
215 | my $self = shift; | ||
216 | my $data = shift; | ||
217 | my $state = shift; | ||
218 | my ($amaj, $amin, $curidx, $maxidx, $certdata); | ||
219 | |||
220 | $state->{-curidx} = -1 unless(exists($state->{-curidx})); | ||
221 | $state->{-maxidx} = -1 unless(exists($state->{-maxidx})); | ||
222 | $state->{-cert} = '' unless(exists($state->{-cert})); | ||
223 | |||
224 | ($amaj, $amin, $curidx, $maxidx, $certdata) = unpack("CCCCa*", $data); | ||
225 | |||
226 | printf("RetDevAuthenticationInfo (0x00, 0x15) D->I\n"); | ||
227 | printf(" Authentication version: %d.%d\n", $amaj, $amin); | ||
228 | printf(" Segment: %d of %d\n", $curidx, $maxidx); | ||
229 | |||
230 | if ($curidx-1 != $state->{-curidx}) { | ||
231 | printf(" WARNING! Out of order segment\n"); | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | if (($maxidx != $state->{-maxidx}) && ($state->{-maxidx} != -1)) { | ||
236 | printf(" WARNING! maxidx changed midstream\n"); | ||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | if ($curidx > $maxidx) { | ||
241 | printf(" WARNING! Too many segments\n"); | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | $state->{-curidx} = $curidx; | ||
246 | $state->{-maxidx} = $maxidx; | ||
247 | $state->{-cert} .= $certdata; | ||
248 | |||
249 | if ($curidx == $maxidx) { | ||
250 | printf(" Certificate: %s\n", Device::iPod->_hexstring($state->{-cert})); | ||
251 | } | ||
252 | |||
253 | return 1; | ||
254 | } | ||
255 | |||
256 | sub _h_00_0016 { | ||
257 | my $self = shift; | ||
258 | my $data = shift; | ||
259 | my $state = shift; | ||
260 | my $res; | ||
261 | |||
262 | $res = unpack("C", $data); | ||
263 | |||
264 | printf("AckDevAuthenticationInfo (0x00, 0x16) I->D\n"); | ||
265 | printf(" Result: "); | ||
266 | if ($res == 0x00) { | ||
267 | printf("Authentication information supported\n"); | ||
268 | } elsif ($res == 0x08) { | ||
269 | printf("Authentication information unpported\n"); | ||
270 | } elsif ($res == 0x0A) { | ||
271 | printf("Certificate invalid\n"); | ||
272 | } elsif ($res == 0x0B) { | ||
273 | printf("Certificate permissions are invalid\n"); | ||
274 | } else { | ||
275 | printf("Unknown result 0x%02x\n", $res); | ||
276 | } | ||
277 | |||
278 | return 1; | ||
279 | } | ||
280 | |||
281 | sub _h_00_0017 { | ||
282 | my $self = shift; | ||
283 | my $data = shift; | ||
284 | my $state = shift; | ||
285 | my ($challenge, $retry); | ||
286 | |||
287 | printf("GetDevAuthenticationSignature (0x00, 0x17) I->D\n"); | ||
288 | |||
289 | if (length($data) == 17) { | ||
290 | ($challenge, $retry) = unpack("a16C", $data); | ||
291 | } elsif (length($data) == 21) { | ||
292 | ($challenge, $retry) = unpack("a20C", $data); | ||
293 | } else { | ||
294 | printf(" WARNING! Unsupported data length: %d\n", length($data)); | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | printf(" Challenge: %s (%d bytes)\n", Device::iPod->_hexstring($challenge), length($challenge)); | ||
299 | printf(" Retry counter: %d\n", $retry); | ||
300 | |||
301 | return 1; | ||
302 | } | ||
303 | |||
304 | sub _h_00_0018 { | ||
305 | my $self = shift; | ||
306 | my $data = shift; | ||
307 | my $state = shift; | ||
308 | my $reply; | ||
309 | |||
310 | printf("RetDevAuthenticationSignature (0x00, 0x18) D->I\n"); | ||
311 | printf(" Data: %s\n", Device::iPod->_hexstring($data)); | ||
312 | |||
313 | return 1; | ||
314 | } | ||
315 | |||
316 | sub _h_00_0019 { | ||
317 | my $self = shift; | ||
318 | my $data = shift; | ||
319 | my $state = shift; | ||
320 | my $res; | ||
321 | |||
322 | $res = unpack("C", $data); | ||
323 | |||
324 | printf("AckiPodAuthenticationInfo (0x00, 0x19) I->D\n"); | ||
325 | printf(" Status: %s (%d)\n", ( | ||
326 | "OK")[$res], $res); | ||
327 | |||
328 | return 1; | ||
329 | } | ||
330 | |||
331 | sub _h_00_0024 { | ||
332 | my $self = shift; | ||
333 | my $data = shift; | ||
334 | my $state = shift; | ||
335 | |||
336 | printf("GetiPodOptions (0x00, 0x24) D->I\n"); | ||
337 | |||
338 | return 1; | ||
339 | } | ||
340 | |||
341 | sub _h_00_0025 { | ||
342 | my $self = shift; | ||
343 | my $data = shift; | ||
344 | my $state = shift; | ||
345 | my ($ophi, $oplo); | ||
346 | |||
347 | ($ophi, $oplo) = unpack("NN", $data); | ||
348 | |||
349 | printf("RetiPodOptions (0x00, 0x25) I->D\n"); | ||
350 | printf(" Options:\n"); | ||
351 | printf(" iPod supports SetiPodPreferences\n") if ($oplo & 0x02); | ||
352 | printf(" iPod supports video\n") if ($oplo & 0x01); | ||
353 | |||
354 | return 1; | ||
355 | } | ||
356 | |||
357 | |||
358 | sub _h_00_0027 { | ||
359 | my $self = shift; | ||
360 | my $data = shift; | ||
361 | my $state = shift; | ||
362 | my $acctype; | ||
363 | my $accparam; | ||
364 | |||
365 | $acctype = unpack("C", $data); | ||
366 | |||
367 | printf("GetAccessoryInfo (0x00, 0x27) I->D\n"); | ||
368 | printf(" Accessory Info Type: %s (%d)\n", ( | ||
369 | "Info capabilities", | ||
370 | "Name", | ||
371 | "Minimum supported iPod firmware version", | ||
372 | "Minimum supported lingo version", | ||
373 | "Firmware version", | ||
374 | "Hardware version", | ||
375 | "Manufacturer", | ||
376 | "Model Number", | ||
377 | "Serial Number", | ||
378 | "Maximum payload size")[$acctype], $acctype); | ||
379 | if ($acctype == 0x02) { | ||
380 | my ($modelid, $maj, $min, $rev); | ||
381 | |||
382 | ($modelid, $maj, $min, $rev) = unpack("xNCCC", $data); | ||
383 | printf(" Model ID: 0x%04x\n", $modelid); | ||
384 | printf(" iPod Firmware: %d.%d.%d\n", $maj, $min, $rev); | ||
385 | } elsif ($acctype == 0x03) { | ||
386 | my $lingo; | ||
387 | |||
388 | $lingo = unpack("xC", $data); | ||
389 | printf(" Lingo: 0x%02x\n", $lingo); | ||
390 | } | ||
391 | |||
392 | return 1; | ||
393 | } | ||
394 | |||
395 | sub _h_00_0028 { | ||
396 | my $self = shift; | ||
397 | my $data = shift; | ||
398 | my $state = shift; | ||
399 | my $acctype; | ||
400 | my $accparam; | ||
401 | |||
402 | $acctype = unpack("C", $data); | ||
403 | |||
404 | printf("RetAccessoryInfo (0x00, 0x28) D->I\n"); | ||
405 | printf(" Accessory Info Type: %s (%d)\n", ( | ||
406 | "Info capabilities", | ||
407 | "Name", | ||
408 | "Minimum supported iPod firmware version", | ||
409 | "Minimum supported lingo version", | ||
410 | "Firmware version", | ||
411 | "Hardware version", | ||
412 | "Manufacturer", | ||
413 | "Model Number", | ||
414 | "Serial Number", | ||
415 | "Maximum payload size")[$acctype], $acctype); | ||
416 | |||
417 | if ($acctype == 0x00) { | ||
418 | $accparam = unpack("xN", $data); | ||
419 | printf(" Accessory Info capabilities\n") if ($accparam & 0x01); | ||
420 | printf(" Accessory name\n") if ($accparam & 0x02); | ||
421 | printf(" Accessory minimum supported iPod firmware\n") if ($accparam & 0x04); | ||
422 | printf(" Accessory minimum supported lingo version\n") if ($accparam & 0x08); | ||
423 | printf(" Accessory firmware version\n") if ($accparam & 0x10); | ||
424 | printf(" Accessory hardware version\n") if ($accparam & 0x20); | ||
425 | printf(" Accessory manufacturer\n") if ($accparam & 0x40); | ||
426 | printf(" Accessory model number\n") if ($accparam & 0x80); | ||
427 | printf(" Accessory serial number\n") if ($accparam & 0x100); | ||
428 | printf(" Accessory incoming max packet size\n") if ($accparam & 0x200); | ||
429 | } | ||
430 | |||
431 | if ($acctype ~~ [0x01, 0x06, 0x07, 0x08]) { | ||
432 | $accparam = unpack("xZ*", $data); | ||
433 | printf(" Data: %s\n", $accparam); | ||
434 | } | ||
435 | |||
436 | if ($acctype == 0x02) { | ||
437 | $accparam = [ unpack("xNCCC", $data) ]; | ||
438 | printf(" Model ID: %08x\n", $accparam->[0]); | ||
439 | printf(" Firmware version: %d.%02d.%02d\n", $accparam->[1], $accparam->[2], $accparam->[3]); | ||
440 | } | ||
441 | |||
442 | if ($acctype == 0x03) { | ||
443 | $accparam = [ unpack("xCCC", $data) ]; | ||
444 | printf(" Lingo: %02x\n", $accparam->[0]); | ||
445 | printf(" Version: %d.%02d\n", $accparam->[1], $accparam->[2]); | ||
446 | } | ||
447 | |||
448 | if ($acctype ~~ [0x04, 0x05]) { | ||
449 | $accparam = [ unpack("xCCC", $data) ]; | ||
450 | printf(" Version: %d.%02d.%02d\n", @{$accparam}); | ||
451 | } | ||
452 | |||
453 | return 1; | ||
454 | } | ||
455 | |||
456 | sub _h_00_0029 { | ||
457 | my $self = shift; | ||
458 | my $data = shift; | ||
459 | my $state = shift; | ||
460 | my $class; | ||
461 | |||
462 | $class = unpack("C", $data); | ||
463 | |||
464 | printf("GetiPodPreferences (0x00, 0x29) D->I\n"); | ||
465 | printf(" Class: %s (%d)\n", ( | ||
466 | "Video out setting", | ||
467 | "Screen configuration", | ||
468 | "Video signal format", | ||
469 | "Line Out usage", | ||
470 | "(Reserved)", | ||
471 | "(Reserved)", | ||
472 | "(Reserved)", | ||
473 | "(Reserved)", | ||
474 | "Video out connection", | ||
475 | "Closed captioning", | ||
476 | "Video aspect ratio", | ||
477 | "(Reserved)", | ||
478 | "Subtitles", | ||
479 | "Video alternate audio channel")[$class], $class); | ||
480 | |||
481 | return 1; | ||
482 | } | ||
483 | |||
484 | sub _h_00_002b { | ||
485 | my $self = shift; | ||
486 | my $data = shift; | ||
487 | my $state = shift; | ||
488 | my ($class, $setting); | ||
489 | |||
490 | ($class, $setting) = unpack("CC", $data); | ||
491 | |||
492 | printf("SetiPodPreferences (0x00, 0x2B) D->I\n"); | ||
493 | printf(" Class: %s (%d)\n", ( | ||
494 | "Video out setting", | ||
495 | "Screen configuration", | ||
496 | "Video signal format", | ||
497 | "Line out usage", | ||
498 | "Reserved", | ||
499 | "Reserved", | ||
500 | "Reserved", | ||
501 | "Reserved", | ||
502 | "Video-out connection", | ||
503 | "Closed captioning", | ||
504 | "Video monitor aspect ratio", | ||
505 | "Reserved", | ||
506 | "Subtitles", | ||
507 | "Video alternate audio channel")[$class], $class); | ||
508 | printf(" Setting: %s (%d)\n", ( | ||
509 | ["Off", | ||
510 | "On", | ||
511 | "Ask user"], | ||
512 | ["Fill screen", | ||
513 | "Fit to screen edge"], | ||
514 | ["NTSC", | ||
515 | "PAL"], | ||
516 | ["Not used", | ||
517 | "Used"], | ||
518 | [], | ||
519 | [], | ||
520 | [], | ||
521 | [], | ||
522 | ["None", | ||
523 | "Composite", | ||
524 | "S-video", | ||
525 | "Component"], | ||
526 | ["Off", | ||
527 | "On"], | ||
528 | ["4:3", | ||
529 | "16:9"], | ||
530 | [], | ||
531 | ["Off", | ||
532 | "On"], | ||
533 | ["Off", | ||
534 | "On"])[$class][$setting], $setting); | ||
535 | |||
536 | return 1; | ||
537 | } | ||
538 | |||
539 | sub _h_00_0038 { | ||
540 | my $self = shift; | ||
541 | my $data = shift; | ||
542 | my $state = shift; | ||
543 | my $transid; | ||
544 | |||
545 | $transid = unpack("n", $data); | ||
546 | |||
547 | printf("StartIDPS (0x00, 0x38) D->I\n"); | ||
548 | printf(" TransID: %d\n", $transid); | ||
549 | |||
550 | return 1; | ||
551 | } | ||
552 | |||
553 | sub _h_00_003b { | ||
554 | my $self = shift; | ||
555 | my $data = shift; | ||
556 | my $state = shift; | ||
557 | my ($transid, $status); | ||
558 | |||
559 | ($transid, $status) = unpack("nC", $data); | ||
560 | |||
561 | printf("EndIDPS (0x00, 0x3B) D->I\n"); | ||
562 | printf(" TransID: %d\n", $transid); | ||
563 | printf(" Action: %s (%d)\n", ( | ||
564 | "Finished", | ||
565 | "Reset")[$status], $status); | ||
566 | |||
567 | return 1; | ||
568 | } | ||
569 | |||
570 | sub _h_00_004b { | ||
571 | my $self = shift; | ||
572 | my $data = shift; | ||
573 | my $state = shift; | ||
574 | my $lingo; | ||
575 | |||
576 | $lingo = unpack("C", $data); | ||
577 | |||
578 | printf("GetiPodOptionsForLingo (0x00, 0x4B) D->I\n"); | ||
579 | printf(" Lingo: 0x%02x\n", $lingo); | ||
580 | |||
581 | return 1; | ||
582 | } | ||
583 | |||
584 | sub _h_02_0000 { | ||
585 | my $self = shift; | ||
586 | my $data = shift; | ||
587 | my $state = shift; | ||
588 | my @keys; | ||
589 | |||
590 | @keys = unpack("CCCC", $data); | ||
591 | |||
592 | printf("ContextButtonStatus (0x02, 0x00) D->I\n"); | ||
593 | printf(" Buttons:\n"); | ||
594 | printf(" Play/Pause\n") if ($keys[0] & 0x01); | ||
595 | printf(" Volume Up\n") if ($keys[0] & 0x02); | ||
596 | printf(" Volume Down\n") if ($keys[0] & 0x04); | ||
597 | printf(" Next Track\n") if ($keys[0] & 0x08); | ||
598 | printf(" Previous Track\n") if ($keys[0] & 0x10); | ||
599 | printf(" Next Album\n") if ($keys[0] & 0x20); | ||
600 | printf(" Previous Album\n") if ($keys[0] & 0x40); | ||
601 | printf(" Stop\n") if ($keys[0] & 0x80); | ||
602 | |||
603 | if (exists($keys[1])) { | ||
604 | printf(" Play/Resume\n") if ($keys[1] & 0x01); | ||
605 | printf(" Pause\n") if ($keys[1] & 0x02); | ||
606 | printf(" Mute toggle\n") if ($keys[1] & 0x04); | ||
607 | printf(" Next Chapter\n") if ($keys[1] & 0x08); | ||
608 | printf(" Previous Chapter\n") if ($keys[1] & 0x10); | ||
609 | printf(" Next Playlist\n") if ($keys[1] & 0x20); | ||
610 | printf(" Previous Playlist\n") if ($keys[1] & 0x40); | ||
611 | printf(" Shuffle Setting Advance\n") if ($keys[1] & 0x80); | ||
612 | } | ||
613 | |||
614 | if (exists($keys[2])) { | ||
615 | printf(" Repeat Setting Advance\n") if ($keys[2] & 0x01); | ||
616 | printf(" Power On\n") if ($keys[2] & 0x02); | ||
617 | printf(" Power Off\n") if ($keys[2] & 0x04); | ||
618 | printf(" Backlight for 30 seconds\n") if ($keys[2] & 0x08); | ||
619 | printf(" Begin FF\n") if ($keys[2] & 0x10); | ||
620 | printf(" Begin REW\n") if ($keys[2] & 0x20); | ||
621 | printf(" Menu\n") if ($keys[2] & 0x40); | ||
622 | printf(" Select\n") if ($keys[2] & 0x80); | ||
623 | } | ||
624 | |||
625 | if (exists($keys[3])) { | ||
626 | printf(" Up Arrow\n") if ($keys[3] & 0x01); | ||
627 | printf(" Down Arrow\n") if ($keys[3] & 0x02); | ||
628 | printf(" Backlight off\n") if ($keys[3] & 0x04); | ||
629 | } | ||
630 | |||
631 | return 1; | ||
632 | } | ||
633 | |||
634 | sub _h_02_0001 { | ||
635 | my $self = shift; | ||
636 | my $data = shift; | ||
637 | my $state = shift; | ||
638 | my $res; | ||
639 | my $cmd; | ||
640 | |||
641 | ($res, $cmd) = unpack("CC", $data); | ||
642 | |||
643 | printf("ACK (0x02, 0x01) I->D\n"); | ||
644 | printf(" Acknowledged command: 0x%02x\n", $cmd); | ||
645 | printf(" Status: %s (%d)\n", | ||
646 | ("Success", | ||
647 | "ERROR: Unknown Database Category", | ||
648 | "ERROR: Command Failed", | ||
649 | "ERROR: Out Of Resource", | ||
650 | "ERROR: Bad Parameter", | ||
651 | "ERROR: Unknown ID", | ||
652 | "Command Pending", | ||
653 | "ERROR: Not Authenticated", | ||
654 | "ERROR: Bad Authentication Version", | ||
655 | "ERROR: Accessory Power Mode Request Failed", | ||
656 | "ERROR: Certificate Invalid", | ||
657 | "ERROR: Certificate permissions invalid", | ||
658 | "ERROR: File is in use", | ||
659 | "ERROR: Invalid file handle", | ||
660 | "ERROR: Directory not empty", | ||
661 | "ERROR: Operation timed out", | ||
662 | "ERROR: Command unavailable in this iPod mode", | ||
663 | "ERROR: Invalid accessory resistor ID", | ||
664 | "Reserved", | ||
665 | "Reserved", | ||
666 | "Reserved", | ||
667 | "ERROR: Maximum number of accessory connections already reached")[$res], $res); | ||
668 | |||
669 | return 1; | ||
670 | } | ||
671 | |||
672 | sub _h_03_0000 { | ||
673 | my $self = shift; | ||
674 | my $data = shift; | ||
675 | my $state = shift; | ||
676 | my $res; | ||
677 | my $cmd; | ||
678 | |||
679 | ($res, $cmd) = unpack("CC", $data); | ||
680 | |||
681 | printf("ACK (0x03, 0x00) I->D\n"); | ||
682 | printf(" Acknowledged command: 0x%02x\n", $cmd); | ||
683 | printf(" Status: %s (%d)\n", | ||
684 | ("Success", | ||
685 | "ERROR: Unknown Database Category", | ||
686 | "ERROR: Command Failed", | ||
687 | "ERROR: Out Of Resource", | ||
688 | "ERROR: Bad Parameter", | ||
689 | "ERROR: Unknown ID", | ||
690 | "Command Pending", | ||
691 | "ERROR: Not Authenticated", | ||
692 | "ERROR: Bad Authentication Version", | ||
693 | "ERROR: Accessory Power Mode Request Failed", | ||
694 | "ERROR: Certificate Invalid", | ||
695 | "ERROR: Certificate permissions invalid", | ||
696 | "ERROR: File is in use", | ||
697 | "ERROR: Invalid file handle", | ||
698 | "ERROR: Directory not empty", | ||
699 | "ERROR: Operation timed out", | ||
700 | "ERROR: Command unavailable in this iPod mode", | ||
701 | "ERROR: Invalid accessory resistor ID", | ||
702 | "Reserved", | ||
703 | "Reserved", | ||
704 | "Reserved", | ||
705 | "ERROR: Maximum number of accessory connections already reached")[$res], $res); | ||
706 | |||
707 | return 1; | ||
708 | } | ||
709 | |||
710 | sub _h_03_0008 { | ||
711 | my $self = shift; | ||
712 | my $data = shift; | ||
713 | my $state = shift; | ||
714 | my $events;; | ||
715 | |||
716 | $events = unpack("N", $data); | ||
717 | |||
718 | printf("SetRemoteEventsNotification (0x03, 0x08) D->I\n"); | ||
719 | printf(" Events:\n"); | ||
720 | printf(" Track position in ms\n") if ($events & 0x00000001); | ||
721 | printf(" Track playback index\n") if ($events & 0x00000002); | ||
722 | printf(" Chapter index\n") if ($events & 0x00000004); | ||
723 | printf(" Play status\n") if ($events & 0x00000008); | ||
724 | printf(" Mute/UI volume\n") if ($events & 0x00000010); | ||
725 | printf(" Power/Battery\n") if ($events & 0x00000020); | ||
726 | printf(" Equalizer setting\n") if ($events & 0x00000040); | ||
727 | printf(" Shuffle setting\n") if ($events & 0x00000080); | ||
728 | printf(" Repeat setting\n") if ($events & 0x00000100); | ||
729 | printf(" Date and time setting\n") if ($events & 0x00000200); | ||
730 | printf(" Alarm setting\n") if ($events & 0x00000400); | ||
731 | printf(" Backlight state\n") if ($events & 0x00000800); | ||
732 | printf(" Hold switch state\n") if ($events & 0x00001000); | ||
733 | printf(" Sound check state\n") if ($events & 0x00002000); | ||
734 | printf(" Audiobook speed\n") if ($events & 0x00004000); | ||
735 | printf(" Track position in s\n") if ($events & 0x00008000); | ||
736 | printf(" Mute/UI/Absolute volume\n") if ($events & 0x00010000); | ||
737 | |||
738 | return 1; | ||
739 | } | ||
740 | |||
741 | sub _h_03_0009 { | ||
742 | my $self = shift; | ||
743 | my $data = shift; | ||
744 | my $state = shift; | ||
745 | my $event; | ||
746 | my $eventdata; | ||
747 | |||
748 | $event = unpack("C", $data); | ||
749 | |||
750 | printf("RemoteEventNotification (0x03, 0x09) I->D\n"); | ||
751 | printf(" Event: %s (%d)\n", ( | ||
752 | "Track position in ms", | ||
753 | "Track playback index", | ||
754 | "Chapter index", | ||
755 | "Play status", | ||
756 | "Mute/UI volume", | ||
757 | "Power/Battery", | ||
758 | "Equalizer setting", | ||
759 | "Shuffle setting", | ||
760 | "Repeat setting", | ||
761 | "Date and time setting", | ||
762 | "Alarm setting", | ||
763 | "Backlight state", | ||
764 | "Hold switch state", | ||
765 | "Sound check state", | ||
766 | "Audiobook speed", | ||
767 | "Track position in s", | ||
768 | "Mute/UI/Absolute volume")[$event], $event); | ||
769 | |||
770 | if ($event == 0x00) { | ||
771 | $eventdata = unpack("xN", $data); | ||
772 | printf(" Position: %d ms\n", $eventdata); | ||
773 | } elsif ($event == 0x01) { | ||
774 | $eventdata = unpack("xN", $data); | ||
775 | printf(" Track: %d\n", $eventdata); | ||
776 | } elsif ($event == 0x02) { | ||
777 | $eventdata = [ unpack("xNnn", $data) ]; | ||
778 | printf(" Track: %d\n", $eventdata->[0]); | ||
779 | printf(" Chapter count: %d\n", $eventdata->[1]); | ||
780 | printf(" Chapter index: %d\n", $eventdata->[2]); | ||
781 | } elsif ($event == 0x03) { | ||
782 | $eventdata = unpack("xC", $data); | ||
783 | printf(" Status: %s (%d)\n", ( | ||
784 | "Stopped", | ||
785 | "Playing", | ||
786 | "Paused", | ||
787 | "FF", | ||
788 | "REW", | ||
789 | "End FF/REW")[$eventdata], $eventdata); | ||
790 | } elsif ($event == 0x04) { | ||
791 | $eventdata = [ unpack("xCC") ]; | ||
792 | printf(" Mute: %s\n", $eventdata->[0]?"Off":"On"); | ||
793 | printf(" Volume: %d\n", $eventdata->[1]); | ||
794 | } elsif ($event == 0x0F) { | ||
795 | $eventdata = unpack("xn", $data); | ||
796 | printf(" Position: %d s\n", $eventdata); | ||
797 | } | ||
798 | |||
799 | return 1; | ||
800 | } | ||
801 | |||
802 | sub _h_03_000c { | ||
803 | my $self = shift; | ||
804 | my $data = shift; | ||
805 | my $state = shift; | ||
806 | my $info; | ||
807 | |||
808 | $info = unpack("C", $data); | ||
809 | |||
810 | printf("GetiPodStateInfo (0x03, 0x0C) D->I\n"); | ||
811 | printf(" Info to get: %s (%d)\n", ( | ||
812 | "Track time in ms", | ||
813 | "Track playback index", | ||
814 | "Chapter information", | ||
815 | "Play status", | ||
816 | "Mute and volume information", | ||
817 | "Power and battery status", | ||
818 | "Equalizer setting", | ||
819 | "Shuffle setting", | ||
820 | "Repeat setting", | ||
821 | "Date and time", | ||
822 | "Alarm state and time", | ||
823 | "Backlight state", | ||
824 | "Hold switch state", | ||
825 | "Audiobook speed", | ||
826 | "Track time in seconds", | ||
827 | "Mute/UI/Absolute volume")[$info], $info); | ||
828 | |||
829 | return 1; | ||
830 | } | ||
831 | |||
832 | sub _h_03_000d { | ||
833 | my $self = shift; | ||
834 | my $data = shift; | ||
835 | my $state = shift; | ||
836 | my $type; | ||
837 | my $info; | ||
838 | |||
839 | $type = unpack("C", $data); | ||
840 | |||
841 | printf("RetiPodStateInfo (0x03, 0x0E) D->I\n"); | ||
842 | |||
843 | if ($type == 0x00) { | ||
844 | $info = unpack("xN", $data); | ||
845 | printf(" Type: Track position\n"); | ||
846 | printf(" Position: %d ms\n", $info); | ||
847 | } elsif ($type == 0x01) { | ||
848 | $info = unpack("xN", $data); | ||
849 | printf(" Type: Track index\n"); | ||
850 | printf(" Index: %d\n", $info); | ||
851 | } elsif ($type == 0x02) { | ||
852 | $info = [ unpack("xNnn", $data) ]; | ||
853 | printf(" Type: Chapter Information\n"); | ||
854 | printf(" Playing Track: %d\n", $info->[0]); | ||
855 | printf(" Chapter count: %d\n", $info->[1]); | ||
856 | printf(" Chapter index: %d\n", $info->[2]); | ||
857 | } elsif ($type == 0x03) { | ||
858 | $info = unpack("xC", $data); | ||
859 | printf(" Type: Play status\n"); | ||
860 | printf(" Status: %s (%d)\n", ( | ||
861 | "Stopped", | ||
862 | "Playing", | ||
863 | "Paused", | ||
864 | "FF", | ||
865 | "REW", | ||
866 | "End FF/REW")[$info], $info); | ||
867 | } elsif ($type == 0x04) { | ||
868 | $info = [unpack("xCC", $data)]; | ||
869 | printf(" Type: Mute/Volume\n"); | ||
870 | printf(" Mute State: %s\n", $info->[0]?"On":"Off"); | ||
871 | printf(" Volume level: %d\n", $info->[1]); | ||
872 | } elsif ($type == 0x05) { | ||
873 | $info = [unpack("xCC", $data)]; | ||
874 | printf(" Type: Battery Information\n"); | ||
875 | printf(" Power state: %s (%d)\n", ( | ||
876 | "Internal, low power (<30%)", | ||
877 | "Internal", | ||
878 | "External battery pack, no charging", | ||
879 | "External power, no charging", | ||
880 | "External power, charging", | ||
881 | "External power, charged")[$info->[0]], $info->[0]); | ||
882 | printf(" Battery level: %d%%\n", $info->[1]*100/255); | ||
883 | } elsif ($type == 0x06) { | ||
884 | $info = [unpack("xN", $data)]; | ||
885 | printf(" Type: Equalizer\n"); | ||
886 | printf(" Index: %d\n", $info->[0]); | ||
887 | } elsif ($type == 0x07) { | ||
888 | $info = [unpack("xC", $data)]; | ||
889 | printf(" Type: Shuffle\n"); | ||
890 | printf(" Shuffle State: %s\n", $info->[0]?"On":"Off"); | ||
891 | } elsif ($type == 0x08) { | ||
892 | $info = [unpack("xC", $data)]; | ||
893 | printf(" Type: Repeat\n"); | ||
894 | printf(" Repeat State: %s\n", $info->[0]?"On":"Off"); | ||
895 | } elsif ($type == 0x09) { | ||
896 | $info = [unpack("xnCCCC", $data)]; | ||
897 | printf(" Type: Date\n"); | ||
898 | printf(" Date: %02d.%02d.%04d %02d:%02d\n", $info->[2], $info->[1], $info->[0], $info->[3], $info->[4]); | ||
899 | } elsif ($type == 0x0A) { | ||
900 | $info = [unpack("xCCC", $data)]; | ||
901 | printf(" Type: Alarm\n"); | ||
902 | printf(" Alarm State: %s\n", $info->[0]?"On":"Off"); | ||
903 | printf(" Time: %02d:%02d\n", $info->[1], $info->[2]); | ||
904 | } elsif ($type == 0x0B) { | ||
905 | $info = [unpack("xC", $data)]; | ||
906 | printf(" Type: Backlight\n"); | ||
907 | printf(" Backlight State: %s\n", $info->[0]?"On":"Off"); | ||
908 | } elsif ($type == 0x0C) { | ||
909 | $info = [unpack("xC", $data)]; | ||
910 | printf(" Type: Hold switch\n"); | ||
911 | printf(" Switch State: %s\n", $info->[0]?"On":"Off"); | ||
912 | } elsif ($type == 0x0D) { | ||
913 | $info = [unpack("xC", $data)]; | ||
914 | printf(" Type: Sound check\n"); | ||
915 | printf(" Sound check: %s\n", $info->[0]?"On":"Off"); | ||
916 | } elsif ($type == 0x0E) { | ||
917 | $info = [unpack("xC", $data)]; | ||
918 | printf(" Type: Audiobook speed\n"); | ||
919 | printf(" Speed: %s\n", $info->[0]==0x00?"Normal":$info->[0]==0x01?"Faster":$info->[0]==0xFF?"Slower":"Reserved"); | ||
920 | } elsif ($type == 0x0F) { | ||
921 | $info = unpack("xN", $data); | ||
922 | printf(" Type: Track position\n"); | ||
923 | printf(" Position: %d s\n", $info); | ||
924 | } elsif ($type == 0x10) { | ||
925 | $info = [unpack("xCCC", $data)]; | ||
926 | printf(" Type: Mute/UI/Absolute volume\n"); | ||
927 | printf(" Mute State: %s\n", $info->[0]?"On":"Off"); | ||
928 | printf(" UI Volume level: %d\n", $info->[1]); | ||
929 | printf(" Absolute Volume: %d\n", $info->[2]); | ||
930 | } else { | ||
931 | printf(" Reserved\n"); | ||
932 | } | ||
933 | |||
934 | return 1; | ||
935 | } | ||
936 | |||
937 | |||
938 | sub _h_03_000e { | ||
939 | my $self = shift; | ||
940 | my $data = shift; | ||
941 | my $state = shift; | ||
942 | my $type; | ||
943 | my $info; | ||
944 | |||
945 | $type = unpack("C", $data); | ||
946 | |||
947 | printf("SetiPodStateInfo (0x03, 0x0E) D->I\n"); | ||
948 | |||
949 | if ($type == 0x00) { | ||
950 | $info = unpack("xN", $data); | ||
951 | printf(" Type: Track position\n"); | ||
952 | printf(" Position: %d ms\n", $info); | ||
953 | } elsif ($type == 0x01) { | ||
954 | $info = unpack("xN", $data); | ||
955 | printf(" Type: Track index\n"); | ||
956 | printf(" Index: %d\n", $info); | ||
957 | } elsif ($type == 0x02) { | ||
958 | $info = unpack("xn", $data); | ||
959 | printf(" Type: Chapter index\n"); | ||
960 | printf(" Index: %d\n", $info); | ||
961 | } elsif ($type == 0x03) { | ||
962 | $info = unpack("xC", $data); | ||
963 | printf(" Type: Play status\n"); | ||
964 | printf(" Status: %s (%d)\n", ( | ||
965 | "Stopped", | ||
966 | "Playing", | ||
967 | "Paused", | ||
968 | "FF", | ||
969 | "REW", | ||
970 | "End FF/REW")[$info], $info); | ||
971 | } elsif ($type == 0x04) { | ||
972 | $info = [unpack("xCCC", $data)]; | ||
973 | printf(" Type: Mute/Volume\n"); | ||
974 | printf(" Mute State: %s\n", $info->[0]?"On":"Off"); | ||
975 | printf(" Volume level: %d\n", $info->[1]); | ||
976 | printf(" Restore on exit: %s\n", $info->[2]?"Yes":"No"); | ||
977 | } elsif ($type == 0x06) { | ||
978 | $info = [unpack("xNC", $data)]; | ||
979 | printf(" Type: Equalizer\n"); | ||
980 | printf(" Index: %d\n", $info->[0]); | ||
981 | printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); | ||
982 | } elsif ($type == 0x07) { | ||
983 | $info = [unpack("xCC", $data)]; | ||
984 | printf(" Type: Shuffle\n"); | ||
985 | printf(" Shuffle State: %s\n", $info->[0]?"On":"Off"); | ||
986 | printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); | ||
987 | } elsif ($type == 0x08) { | ||
988 | $info = [unpack("xCC", $data)]; | ||
989 | printf(" Type: Repeat\n"); | ||
990 | printf(" Repeat State: %s\n", $info->[0]?"On":"Off"); | ||
991 | printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); | ||
992 | } elsif ($type == 0x09) { | ||
993 | $info = [unpack("xnCCCC", $data)]; | ||
994 | printf(" Type: Date\n"); | ||
995 | printf(" Date: %02d.%02d.%04d %02d:%02d\n", $info->[2], $info->[1], $info->[0], $info->[3], $info->[4]); | ||
996 | } elsif ($type == 0x0A) { | ||
997 | $info = [unpack("xCCCC", $data)]; | ||
998 | printf(" Type: Alarm\n"); | ||
999 | printf(" Alarm State: %s\n", $info->[0]?"On":"Off"); | ||
1000 | printf(" Time: %02d:%02d\n", $info->[1], $info->[2]); | ||
1001 | printf(" Restore on exit: %s\n", $info->[3]?"Yes":"No"); | ||
1002 | } elsif ($type == 0x0B) { | ||
1003 | $info = [unpack("xCC", $data)]; | ||
1004 | printf(" Type: Backlight\n"); | ||
1005 | printf(" Backlight State: %s\n", $info->[0]?"On":"Off"); | ||
1006 | printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); | ||
1007 | } elsif ($type == 0x0D) { | ||
1008 | $info = [unpack("xCC", $data)]; | ||
1009 | printf(" Type: Sound check\n"); | ||
1010 | printf(" Sound check: %s\n", $info->[0]?"On":"Off"); | ||
1011 | printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); | ||
1012 | } elsif ($type == 0x0E) { | ||
1013 | $info = [unpack("xCC", $data)]; | ||
1014 | printf(" Type: Audiobook speed\n"); | ||
1015 | printf(" Speed: %s\n", $info->[0]==0x00?"Normal":$info->[0]==0x01?"Faster":$info->[0]==0xFF?"Slower":"Reserved"); | ||
1016 | printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); | ||
1017 | } elsif ($type == 0x0F) { | ||
1018 | $info = unpack("xN", $data); | ||
1019 | printf(" Type: Track position\n"); | ||
1020 | printf(" Position: %d s\n", $info); | ||
1021 | } elsif ($type == 0x10) { | ||
1022 | $info = [unpack("xCCCC", $data)]; | ||
1023 | printf(" Type: Mute/UI/Absolute volume\n"); | ||
1024 | printf(" Mute State: %s\n", $info->[0]?"On":"Off"); | ||
1025 | printf(" UI Volume level: %d\n", $info->[1]); | ||
1026 | printf(" Absolute Volume: %d\n", $info->[2]); | ||
1027 | printf(" Restore on exit: %s\n", $info->[3]?"Yes":"No"); | ||
1028 | } else { | ||
1029 | printf(" Reserved\n"); | ||
1030 | } | ||
1031 | |||
1032 | return 1; | ||
1033 | } | ||
1034 | |||
1035 | sub _h_03_000f { | ||
1036 | my $self = shift; | ||
1037 | my $data = shift; | ||
1038 | my $state = shift; | ||
1039 | |||
1040 | print("GetPlayStatus (0x03, 0x0F) D->I\n"); | ||
1041 | |||
1042 | return 1; | ||
1043 | } | ||
1044 | |||
1045 | sub _h_03_0010 { | ||
1046 | my $self = shift; | ||
1047 | my $data = shift; | ||
1048 | my $state = shift; | ||
1049 | my ($status, $idx, $len, $pos); | ||
1050 | |||
1051 | ($status, $idx, $len, $pos) = unpack("CNNN", $data); | ||
1052 | |||
1053 | printf("RetPlayStatus (0x03, 0x10) I->D\n"); | ||
1054 | printf(" Status: %s (%d)\n", ( | ||
1055 | "Stopped", | ||
1056 | "Playing", | ||
1057 | "Paused")[$status], $status); | ||
1058 | if ($status != 0x00) { | ||
1059 | printf(" Track index: %d\n", $idx); | ||
1060 | printf(" Track length: %d\n", $len); | ||
1061 | printf(" Track position: %d\n", $pos); | ||
1062 | } | ||
1063 | |||
1064 | return 1; | ||
1065 | } | ||
1066 | |||
1067 | sub _h_03_0012 { | ||
1068 | my $self = shift; | ||
1069 | my $data = shift; | ||
1070 | my $state = shift; | ||
1071 | my ($info, $tidx, $cidx); | ||
1072 | |||
1073 | ($info, $tidx, $cidx) = unpack("CNn", $data); | ||
1074 | |||
1075 | printf("GetIndexedPlayingTrackInfo (0x03, 0x12) D->I\n"); | ||
1076 | printf(" Requested info: %s (%d)\n", ( | ||
1077 | "Track caps/info", | ||
1078 | "Chapter time/name", | ||
1079 | "Artist name", | ||
1080 | "Album name", | ||
1081 | "Genre name", | ||
1082 | "Track title", | ||
1083 | "Composer name", | ||
1084 | "Lyrics", | ||
1085 | "Artwork count")[$info], $info); | ||
1086 | printf(" Track index: %d\n", $tidx); | ||
1087 | printf(" Chapter index: %d\n", $cidx); | ||
1088 | |||
1089 | return 1; | ||
1090 | } | ||
1091 | |||
1092 | sub _h_03_0013 { | ||
1093 | my $self = shift; | ||
1094 | my $data = shift; | ||
1095 | my $state = shift; | ||
1096 | my $info; | ||
1097 | |||
1098 | $info = unpack("C", $data); | ||
1099 | printf("RetIndexedPlayingTrackInfo (0x03, 0x13) I->D\n"); | ||
1100 | printf(" Returned info: %s (%d)\n", ( | ||
1101 | "Track caps/info", | ||
1102 | "Chapter time/name", | ||
1103 | "Artist name", | ||
1104 | "Album name", | ||
1105 | "Genre name", | ||
1106 | "Track title", | ||
1107 | "Composer name", | ||
1108 | "Lyrics", | ||
1109 | "Artwork count")[$info], $info); | ||
1110 | if ($info == 0x00) { | ||
1111 | my ($caps, $len, $chap) = unpack("xNNn", $data); | ||
1112 | printf(" Track is audiobook\n") if ($caps & 0x01); | ||
1113 | printf(" Track has chapters\n") if ($caps & 0x02); | ||
1114 | printf(" Track has artwork\n") if ($caps & 0x04); | ||
1115 | printf(" Track contains video\n") if ($caps & 0x80); | ||
1116 | printf(" Track queued as video\n") if ($caps & 0x100); | ||
1117 | |||
1118 | printf(" Track length: %d ms\n", $len); | ||
1119 | printf(" Track chapters: %d\n", $chap); | ||
1120 | } elsif ($info == 0x01) { | ||
1121 | my ($len, $name) = unpack("xNZ*"); | ||
1122 | printf(" Chapter time: %d ms\n", $len); | ||
1123 | printf(" Chapter name: %s\n", $name); | ||
1124 | } elsif ($info >= 0x02 && $info <= 0x06) { | ||
1125 | my $name = unpack("xZ*", $data); | ||
1126 | printf(" Name/Title: %s\n", $name) | ||
1127 | } elsif ($info == 0x07) { | ||
1128 | my ($info, $index, $data) = unpack("xCnZ*"); | ||
1129 | printf(" Part of multiple packets\n") if ($info & 0x01); | ||
1130 | printf(" Is last packet\n") if ($info & 0x02); | ||
1131 | printf(" Packet index: %d\n", $index); | ||
1132 | printf(" Data: %s\n", $data); | ||
1133 | } elsif ($info == 0x08) { | ||
1134 | |||
1135 | } | ||
1136 | |||
1137 | return 1; | ||
1138 | } | ||
1139 | |||
1140 | sub _h_04_0001 { | ||
1141 | my $self = shift; | ||
1142 | my $data = shift; | ||
1143 | my $state = shift; | ||
1144 | my $res; | ||
1145 | my $cmd; | ||
1146 | |||
1147 | ($res, $cmd) = unpack("Cn", $data); | ||
1148 | |||
1149 | printf("ACK (0x04, 0x0001) I->D\n"); | ||
1150 | printf(" Acknowledged command: 0x%02x\n", $cmd); | ||
1151 | printf(" Status: %s (%d)\n", | ||
1152 | ("Success", | ||
1153 | "ERROR: Unknown Database Category", | ||
1154 | "ERROR: Command Failed", | ||
1155 | "ERROR: Out Of Resource", | ||
1156 | "ERROR: Bad Parameter", | ||
1157 | "ERROR: Unknown ID", | ||
1158 | "Reserved", | ||
1159 | "ERROR: Not Authenticated")[$res], $res); | ||
1160 | |||
1161 | return 1; | ||
1162 | } | ||
1163 | |||
1164 | sub _h_04_000c { | ||
1165 | my $self = shift; | ||
1166 | my $data = shift; | ||
1167 | my $state = shift; | ||
1168 | my ($info, $track, $chapter); | ||
1169 | |||
1170 | ($info, $track, $chapter) = unpack("CNn", $data); | ||
1171 | |||
1172 | printf("GetIndexedPlayingTrackInfo (0x04, 0x000C) D->I\n"); | ||
1173 | printf(" Track: %d\n", $track); | ||
1174 | printf(" Chapter: %d\n", $chapter); | ||
1175 | printf(" Info requested: %s (%d)\n", ( | ||
1176 | "Capabilities and information", | ||
1177 | "Podcast name", | ||
1178 | "Track release date", | ||
1179 | "Track description", | ||
1180 | "Track song lyrics", | ||
1181 | "Track genre", | ||
1182 | "Track Composer", | ||
1183 | "Tracn Artwork count")[$info], $info); | ||
1184 | |||
1185 | return 1; | ||
1186 | } | ||
1187 | |||
1188 | sub _h_04_000d { | ||
1189 | my $self = shift; | ||
1190 | my $data = shift; | ||
1191 | my $state = shift; | ||
1192 | my $info; | ||
1193 | |||
1194 | $info = unpack("C", $data); | ||
1195 | |||
1196 | printf("ReturnIndexedPlayingTrackInfo (0x04, 0x000D) I->D\n"); | ||
1197 | if ($info == 0x00) { | ||
1198 | my ($capability, $length, $chapter); | ||
1199 | |||
1200 | ($capability, $length, $chapter) = unpack("xNNn", $data); | ||
1201 | printf(" Capabilities:\n"); | ||
1202 | printf(" Is audiobook\n") if ($capability & 0x00000001); | ||
1203 | printf(" Has chapters\n") if ($capability & 0x00000002); | ||
1204 | printf(" Has album artwork\n") if ($capability & 0x00000004); | ||
1205 | printf(" Has song lyrics\n") if ($capability & 0x00000008); | ||
1206 | printf(" Is a podcast episode\n") if ($capability & 0x00000010); | ||
1207 | printf(" Has release date\n") if ($capability & 0x00000020); | ||
1208 | printf(" Has description\n") if ($capability & 0x00000040); | ||
1209 | printf(" Contains video\n") if ($capability & 0x00000080); | ||
1210 | printf(" Queued to play as video\n") if ($capability & 0x00000100); | ||
1211 | |||
1212 | printf(" Length: %d ms\n", $length); | ||
1213 | printf(" Chapters: %d\n", $chapter); | ||
1214 | } else { | ||
1215 | printf(" WARNING: Unknown info\n"); | ||
1216 | return 1; | ||
1217 | } | ||
1218 | |||
1219 | return 1; | ||
1220 | } | ||
1221 | |||
1222 | sub _h_04_0012 { | ||
1223 | my $self = shift; | ||
1224 | my $data = shift; | ||
1225 | my $state = shift; | ||
1226 | |||
1227 | printf("RequestProtocolVersion (0x04, 0x0012) D->I\n"); | ||
1228 | |||
1229 | return 1; | ||
1230 | } | ||
1231 | |||
1232 | sub _h_04_0013 { | ||
1233 | my $self = shift; | ||
1234 | my $data = shift; | ||
1235 | my $state = shift; | ||
1236 | my ($maj, $min); | ||
1237 | |||
1238 | ($maj, $min) = unpack("CC", $data); | ||
1239 | |||
1240 | printf("ReturnProtocolVersion (0x04, 0x0013) I->D\n"); | ||
1241 | printf(" Lingo 0x04 version: %d.%02d\n", $maj, $min); | ||
1242 | |||
1243 | return 1; | ||
1244 | } | ||
1245 | |||
1246 | sub _h_04_0016 { | ||
1247 | my $self = shift; | ||
1248 | my $data = shift; | ||
1249 | my $state = shift; | ||
1250 | |||
1251 | printf("ResetDBSelection (0x04, 0x0016) D->I\n"); | ||
1252 | |||
1253 | return 1; | ||
1254 | } | ||
1255 | |||
1256 | sub _h_04_0018 { | ||
1257 | my $self = shift; | ||
1258 | my $data = shift; | ||
1259 | my $state = shift; | ||
1260 | my $category; | ||
1261 | |||
1262 | $category = unpack("C", $data); | ||
1263 | |||
1264 | printf("GetNumberCategorizedDBRecords (0x04, 0x0018) D->I\n"); | ||
1265 | printf(" Category: %s (%d)\n", ( | ||
1266 | "Reserved", | ||
1267 | "Playlist", | ||
1268 | "Artist", | ||
1269 | "Album", | ||
1270 | "Genre", | ||
1271 | "Track", | ||
1272 | "Composer", | ||
1273 | "Audiobook", | ||
1274 | "Podcast", | ||
1275 | "Nested Playlist")[$category], $category); | ||
1276 | |||
1277 | return 1; | ||
1278 | } | ||
1279 | |||
1280 | sub _h_04_0019 { | ||
1281 | my $self = shift; | ||
1282 | my $data = shift; | ||
1283 | my $state = shift; | ||
1284 | my $count; | ||
1285 | |||
1286 | $count = unpack("N", $data); | ||
1287 | |||
1288 | printf("ReturnNumberCategorizedDBRecords (0x04, 0x0019) I->D\n"); | ||
1289 | printf(" Count: %d\n", $count); | ||
1290 | |||
1291 | return 1; | ||
1292 | } | ||
1293 | |||
1294 | sub _h_04_001c { | ||
1295 | my $self = shift; | ||
1296 | my $data = shift; | ||
1297 | my $state = shift; | ||
1298 | |||
1299 | printf("GetPlayStatus (0x04, 0x001C) D->I\n"); | ||
1300 | |||
1301 | return 1; | ||
1302 | } | ||
1303 | |||
1304 | sub _h_04_001d { | ||
1305 | my $self = shift; | ||
1306 | my $data = shift; | ||
1307 | my $state = shift; | ||
1308 | my ($len, $pos, $s); | ||
1309 | |||
1310 | ($len, $pos, $s) = unpack("NNC", $data); | ||
1311 | |||
1312 | printf("ReturnPlayStatus (0x04, 0x001D) I->D\n"); | ||
1313 | printf(" Song length: %d ms\n", $len); | ||
1314 | printf(" Song position: %d ms\n", $pos); | ||
1315 | printf(" Player state: "); | ||
1316 | if ($s == 0x00) { | ||
1317 | printf("Stopped\n"); | ||
1318 | } elsif ($s == 0x01) { | ||
1319 | printf("Playing\n"); | ||
1320 | } elsif ($s == 0x02) { | ||
1321 | printf("Paused\n"); | ||
1322 | } elsif ($s == 0xFF) { | ||
1323 | printf("Error\n"); | ||
1324 | } else { | ||
1325 | printf("Reserved\n"); | ||
1326 | } | ||
1327 | |||
1328 | return 1; | ||
1329 | } | ||
1330 | |||
1331 | sub _h_04_001e { | ||
1332 | my $self = shift; | ||
1333 | my $data = shift; | ||
1334 | my $state = shift; | ||
1335 | |||
1336 | printf("GetCurrentPlayingTrackIndex (0x04, 0x001E) D->I\n"); | ||
1337 | |||
1338 | return 1; | ||
1339 | } | ||
1340 | |||
1341 | sub _h_04_001f { | ||
1342 | my $self = shift; | ||
1343 | my $data = shift; | ||
1344 | my $state = shift; | ||
1345 | my $num; | ||
1346 | |||
1347 | $num = unpack("N", $data); | ||
1348 | |||
1349 | printf("ReturnCurrentPlayingTrackIndex (0x04, 0x001F) I->D\n"); | ||
1350 | printf(" Index: %d\n", $num); | ||
1351 | |||
1352 | return 1; | ||
1353 | } | ||
1354 | |||
1355 | sub _h_04_0020 { | ||
1356 | my $self = shift; | ||
1357 | my $data = shift; | ||
1358 | my $state = shift; | ||
1359 | my $track; | ||
1360 | |||
1361 | $track = unpack("N", $data); | ||
1362 | |||
1363 | printf("GetIndexedPlayingTrackTitle (0x04, 0x0020) D->I\n"); | ||
1364 | printf(" Track: %d\n", $track); | ||
1365 | |||
1366 | return 1; | ||
1367 | } | ||
1368 | |||
1369 | sub _h_04_0021 { | ||
1370 | my $self = shift; | ||
1371 | my $data = shift; | ||
1372 | my $state = shift; | ||
1373 | my $title; | ||
1374 | |||
1375 | $title = unpack("Z*", $data); | ||
1376 | |||
1377 | printf("ReturnIndexedPlayingTrackTitle (0x04, 0x0021) I->D\n"); | ||
1378 | printf(" Title: %s\n", $title); | ||
1379 | |||
1380 | return 1; | ||
1381 | } | ||
1382 | |||
1383 | sub _h_04_0022 { | ||
1384 | my $self = shift; | ||
1385 | my $data = shift; | ||
1386 | my $state = shift; | ||
1387 | my $track; | ||
1388 | |||
1389 | $track = unpack("N", $data); | ||
1390 | |||
1391 | printf("GetIndexedPlayingTrackArtistName (0x04, 0x0022) D->I\n"); | ||
1392 | printf(" Track: %d\n", $track); | ||
1393 | |||
1394 | return 1; | ||
1395 | } | ||
1396 | |||
1397 | sub _h_04_0023 { | ||
1398 | my $self = shift; | ||
1399 | my $data = shift; | ||
1400 | my $state = shift; | ||
1401 | my $artist; | ||
1402 | |||
1403 | $artist = unpack("Z*", $data); | ||
1404 | |||
1405 | printf("ReturnIndexedPlayingTrackArtistName (0x04, 0x0023) I->D\n"); | ||
1406 | printf(" Artist: %s\n", $artist); | ||
1407 | |||
1408 | return 1; | ||
1409 | } | ||
1410 | |||
1411 | sub _h_04_0024 { | ||
1412 | my $self = shift; | ||
1413 | my $data = shift; | ||
1414 | my $state = shift; | ||
1415 | my $track; | ||
1416 | |||
1417 | $track = unpack("N", $data); | ||
1418 | |||
1419 | printf("GetIndexedPlayingTrackAlbumName (0x04, 0x0024) D->I\n"); | ||
1420 | printf(" Track: %d\n", $track); | ||
1421 | |||
1422 | return 1; | ||
1423 | } | ||
1424 | |||
1425 | sub _h_04_0025 { | ||
1426 | my $self = shift; | ||
1427 | my $data = shift; | ||
1428 | my $state = shift; | ||
1429 | my $title; | ||
1430 | |||
1431 | $title = unpack("Z*", $data); | ||
1432 | |||
1433 | printf("ReturnIndexedPlayingTrackAlbumName (0x04, 0x0025) I->D\n"); | ||
1434 | printf(" Album: %s\n", $title); | ||
1435 | |||
1436 | return 1; | ||
1437 | } | ||
1438 | |||
1439 | sub _h_04_0026 { | ||
1440 | my $self = shift; | ||
1441 | my $data = shift; | ||
1442 | my $state = shift; | ||
1443 | my $notification; | ||
1444 | |||
1445 | if (length($data) == 1) { | ||
1446 | $notification = unpack("C", $data); | ||
1447 | } elsif (length($data) == 4) { | ||
1448 | $notification = unpack("N", $data); | ||
1449 | } | ||
1450 | |||
1451 | printf("SetPlayStatusChangeNotification (0x04, 0x0026) D->I\n"); | ||
1452 | |||
1453 | if (length($data) == 1) { | ||
1454 | printf(" Events for: %s (%d)\n", ( | ||
1455 | "Disable all", | ||
1456 | "Basic play state, track index, track time position, FFW/REW seek stop, and chapter index changes")[$notification], $notification); | ||
1457 | } elsif (length($data) == 4) { | ||
1458 | printf(" Events for:\n"); | ||
1459 | printf(" Basic play state changes\n") if ($notification & 0x00000001); | ||
1460 | printf(" Extended play state changes\n") if ($notification & 0x00000002); | ||
1461 | printf(" Track index\n") if ($notification & 0x00000004); | ||
1462 | printf(" Track time offset (ms)\n") if ($notification & 0x00000008); | ||
1463 | printf(" Track time offset (s)\n") if ($notification & 0x00000010); | ||
1464 | printf(" Chapter index\n") if ($notification & 0x00000020); | ||
1465 | printf(" Chapter time offset (ms)\n") if ($notification & 0x00000040); | ||
1466 | printf(" Chapter time offset (s)\n") if ($notification & 0x00000080); | ||
1467 | printf(" Track unique identifier\n") if ($notification & 0x00000100); | ||
1468 | printf(" Track media tyoe\n") if ($notification & 0x00000200); | ||
1469 | printf(" Track lyrics\n") if ($notification & 0x00000400); | ||
1470 | } else { | ||
1471 | printf(" WARNING: Unknown length for state\n"); | ||
1472 | return 0; | ||
1473 | } | ||
1474 | |||
1475 | return 1; | ||
1476 | } | ||
1477 | |||
1478 | sub _h_04_0027 { | ||
1479 | my $self = shift; | ||
1480 | my $data = shift; | ||
1481 | my $state = shift; | ||
1482 | my $info; | ||
1483 | |||
1484 | $info = unpack("C", $data); | ||
1485 | |||
1486 | printf("PlayStatusChangeNotification (0x04, 0x0029) I->D\n"); | ||
1487 | printf(" Status:\n"); | ||
1488 | if ($info == 0x00) { | ||
1489 | printf(" Playback stopped\n"); | ||
1490 | } elsif ($info == 0x01) { | ||
1491 | my $index = unpack("xN", $data); | ||
1492 | |||
1493 | printf(" Track index: %d\n", $index); | ||
1494 | } elsif ($info == 0x02) { | ||
1495 | printf(" Playback FF seek stop\n"); | ||
1496 | } elsif ($info == 0x03) { | ||
1497 | printf(" Playback REW seek stop\n"); | ||
1498 | } elsif ($info == 0x04) { | ||
1499 | my $offset = unpack("xN", $data); | ||
1500 | |||
1501 | printf(" Track time offset: %d ms\n", $offset); | ||
1502 | } elsif ($info == 0x05) { | ||
1503 | my $index = unpack("xN", $data); | ||
1504 | |||
1505 | printf(" Chapter index: %d\n", $index); | ||
1506 | } elsif ($info == 0x06) { | ||
1507 | my $status = unpack("xC", $data); | ||
1508 | |||
1509 | printf(" Playback status extended: %s (%d)\n", ( | ||
1510 | "Reserved", | ||
1511 | "Reserved", | ||
1512 | "Stopped", | ||
1513 | "Reserved", | ||
1514 | "Reserved", | ||
1515 | "FF seek started", | ||
1516 | "REW seek started", | ||
1517 | "FF/REW seek stopped", | ||
1518 | "Reserved", | ||
1519 | "Reserved", | ||
1520 | "Playing", | ||
1521 | "Paused")[$status], $status); | ||
1522 | } elsif ($info == 0x07) { | ||
1523 | my $offset = unpack("xN", $data); | ||
1524 | |||
1525 | printf(" Track time offset: %d s\n", $offset); | ||
1526 | } elsif ($info == 0x08) { | ||
1527 | my $offset = unpack("xN", $data); | ||
1528 | |||
1529 | printf(" Chapter time offset %d ms\n", $offset); | ||
1530 | } elsif ($info == 0x09) { | ||
1531 | my $offset = unpack("xN", $data); | ||
1532 | |||
1533 | printf(" Chapter time offset %d s\n", $offset); | ||
1534 | } elsif ($info == 0x0A) { | ||
1535 | my ($uidhi, $uidlo) = unpack("xNN", $data); | ||
1536 | |||
1537 | printf(" Track UID: %08x%08x\n", $uidhi, $uidlo); | ||
1538 | } elsif ($info == 0x0B) { | ||
1539 | my $mode = unpack("xC", $data); | ||
1540 | |||
1541 | printf(" Track mode: %s (%d)\n", ( | ||
1542 | "Audio track", | ||
1543 | "Video track")[$mode], $mode); | ||
1544 | } elsif ($info == 0x0C) { | ||
1545 | printf(" Track lyrics ready\n"); | ||
1546 | } else { | ||
1547 | printf(" Reserved\n"); | ||
1548 | } | ||
1549 | |||
1550 | return 1; | ||
1551 | } | ||
1552 | |||
1553 | sub _h_04_0029 { | ||
1554 | my $self = shift; | ||
1555 | my $data = shift; | ||
1556 | my $state = shift; | ||
1557 | my $control; | ||
1558 | |||
1559 | $control = unpack("C", $data); | ||
1560 | |||
1561 | printf("PlayControl (0x04, 0x0029) D->I\n"); | ||
1562 | printf(" Command: %s (%d)\n", ( | ||
1563 | "Reserved", | ||
1564 | "Toggle Play/Pause", | ||
1565 | "Stop", | ||
1566 | "Next track", | ||
1567 | "Previous track", | ||
1568 | "Start FF", | ||
1569 | "Start Rev", | ||
1570 | "Stop FF/Rev", | ||
1571 | "Next", | ||
1572 | "Previous", | ||
1573 | "Play", | ||
1574 | "Pause", | ||
1575 | "Next chapter", | ||
1576 | "Previous chapter")[$control], $control); | ||
1577 | |||
1578 | return 1; | ||
1579 | } | ||
1580 | |||
1581 | sub _h_04_002c { | ||
1582 | my $self = shift; | ||
1583 | my $data = shift; | ||
1584 | my $state = shift; | ||
1585 | |||
1586 | printf("GetShuffle (0x04, 0x002C) D->I\n"); | ||
1587 | |||
1588 | return 1; | ||
1589 | } | ||
1590 | |||
1591 | sub _h_04_002d { | ||
1592 | my $self = shift; | ||
1593 | my $data = shift; | ||
1594 | my $state = shift; | ||
1595 | my $mode; | ||
1596 | |||
1597 | $mode = unpack("C", $data); | ||
1598 | |||
1599 | printf("ReturnShuffle (0x04, 0x002D) I->D\n"); | ||
1600 | printf(" Mode: %s (%d)\n", ( | ||
1601 | "Off", | ||
1602 | "Tracks", | ||
1603 | "Albums")[$mode], $mode); | ||
1604 | |||
1605 | return 1; | ||
1606 | } | ||
1607 | |||
1608 | sub _h_04_002f { | ||
1609 | my $self = shift; | ||
1610 | my $data = shift; | ||
1611 | my $state = shift; | ||
1612 | |||
1613 | printf("GetRepeat (0x04, 0x002F) D->I\n"); | ||
1614 | |||
1615 | return 1; | ||
1616 | } | ||
1617 | |||
1618 | sub _h_04_0030 { | ||
1619 | my $self = shift; | ||
1620 | my $data = shift; | ||
1621 | my $state = shift; | ||
1622 | my $mode; | ||
1623 | |||
1624 | $mode = unpack("C", $data); | ||
1625 | |||
1626 | printf("ReturnRepeat (0x04, 0x0030) I->D\n"); | ||
1627 | printf(" Mode: %s (%d)\n", ( | ||
1628 | "Off", | ||
1629 | "One Track", | ||
1630 | "All Tracks")[$mode], $mode); | ||
1631 | |||
1632 | return 1; | ||
1633 | } | ||
1634 | |||
1635 | sub _h_04_0032 { | ||
1636 | my $self = shift; | ||
1637 | my $data = shift; | ||
1638 | my $state = shift; | ||
1639 | my ($index, $format, $width, $height, $stride, $imagedata); | ||
1640 | |||
1641 | $state->{-index} = -1 unless(exists($state->{-index})); | ||
1642 | $state->{-image} = '' unless(exists($state->{-image})); | ||
1643 | |||
1644 | ($index, $format, $width, $height, $stride, $imagedata) = unpack("nCnnNa*", $data); | ||
1645 | |||
1646 | printf("SetDisplayImage (0x04, 0x0032) D->I\n"); | ||
1647 | if ($index == 0) { | ||
1648 | printf(" Width: %d\n", $width); | ||
1649 | printf(" Height: %d\n", $height); | ||
1650 | printf(" Stride: %d\n", $stride); | ||
1651 | printf(" Format: %s (%d)\n", ( | ||
1652 | "Reserved", | ||
1653 | "Monochrome, 2 bps", | ||
1654 | "RGB 565, little endian", | ||
1655 | "RGB 565, big endian")[$format], $format); | ||
1656 | |||
1657 | $state->{-imagelength} = $height * $stride; | ||
1658 | } else { | ||
1659 | ($index, $imagedata) = unpack("na*", $data); | ||
1660 | } | ||
1661 | |||
1662 | if ($index-1 != $state->{-index}) { | ||
1663 | printf(" WARNING! Out of order segment\n"); | ||
1664 | return 0; | ||
1665 | } | ||
1666 | |||
1667 | $state->{-index} = $index; | ||
1668 | $state->{-image} .= $imagedata; | ||
1669 | |||
1670 | if (length($state->{-image}) >= $state->{-imagelength}) { | ||
1671 | printf(" Image data: %s\n", Device::iPod->_hexstring($state->{-image})); | ||
1672 | } | ||
1673 | |||
1674 | return 1; | ||
1675 | } | ||
1676 | |||
1677 | sub _h_04_0033 { | ||
1678 | my $self = shift; | ||
1679 | my $data = shift; | ||
1680 | my $state = shift; | ||
1681 | |||
1682 | printf("GetMonoDisplayImageLimits (0x04, 0x0033) D->I\n"); | ||
1683 | |||
1684 | return 1; | ||
1685 | } | ||
1686 | |||
1687 | sub _h_04_0034 { | ||
1688 | my $self = shift; | ||
1689 | my $data = shift; | ||
1690 | my $state = shift; | ||
1691 | my ($width, $height, $format); | ||
1692 | |||
1693 | ($width, $height, $format) = unpack("nnC", $data); | ||
1694 | |||
1695 | printf("ReturnMonoDisplayImageLimits (0x04, 0x0034) I->D\n"); | ||
1696 | printf(" Width: %d\n", $width); | ||
1697 | printf(" Height: %d\n", $height); | ||
1698 | printf(" Format: %s (%d)\n", ( | ||
1699 | "Reserved", | ||
1700 | "Monochrome, 2 bps", | ||
1701 | "RGB 565, little endian", | ||
1702 | "RGB 565, big endian")[$format], $format); | ||
1703 | |||
1704 | return 1; | ||
1705 | } | ||
1706 | |||
1707 | sub _h_04_0035 { | ||
1708 | my $self = shift; | ||
1709 | my $data = shift; | ||
1710 | my $state = shift; | ||
1711 | |||
1712 | printf("GetNumPlayingTracks (0x04, 0x0035) D->I\n"); | ||
1713 | |||
1714 | return 1; | ||
1715 | } | ||
1716 | |||
1717 | sub _h_04_0036 { | ||
1718 | my $self = shift; | ||
1719 | my $data = shift; | ||
1720 | my $state = shift; | ||
1721 | my $num; | ||
1722 | |||
1723 | $num = unpack("N", $data); | ||
1724 | |||
1725 | printf("ReturnNumPlayingTracks (0x04, 0x0036) I->D\n"); | ||
1726 | printf(" Number: %d\n", $num); | ||
1727 | |||
1728 | return 1; | ||
1729 | } | ||
1730 | |||
1731 | sub _h_04_0037 { | ||
1732 | my $self = shift; | ||
1733 | my $data = shift; | ||
1734 | my $state = shift; | ||
1735 | my $num; | ||
1736 | |||
1737 | $num = unpack("N", $data); | ||
1738 | |||
1739 | printf("SetCurrentPlayingTrack (0x04, 0x0037) D->I\n"); | ||
1740 | printf(" Track: %d\n", $num); | ||
1741 | |||
1742 | return 1; | ||
1743 | } | ||
1744 | |||
1745 | sub _h_07_0005 { | ||
1746 | my $self = shift; | ||
1747 | my $data = shift; | ||
1748 | my $state = shift; | ||
1749 | my $control; | ||
1750 | |||
1751 | $control = unpack("C", $data); | ||
1752 | |||
1753 | printf("SetTunerCtrl (0x07, 0x05) I->D\n"); | ||
1754 | printf(" Options:\n"); | ||
1755 | printf(" Power %s\n", ($control & 0x01)?"on":"off"); | ||
1756 | printf(" Status change notifications %s\n", ($control & 0x02)?"on":"off"); | ||
1757 | printf(" Raw mode %s\n", ($control & 0x04)?"on":"off"); | ||
1758 | } | ||
1759 | |||
1760 | sub _h_07_0020 { | ||
1761 | my $self = shift; | ||
1762 | my $data = shift; | ||
1763 | my $state = shift; | ||
1764 | my $options; | ||
1765 | |||
1766 | $options = unpack("N", $data); | ||
1767 | |||
1768 | printf("SetRDSNotifyMask (0x07, 0x20) I->D\n"); | ||
1769 | printf(" Options:\n"); | ||
1770 | printf(" Radiotext\n") if ($options & 0x00000010); | ||
1771 | printf(" Program Service Name\n") if ($options & 0x40000000); | ||
1772 | printf(" Reserved\n") if ($options & 0xBFFFFFEF); | ||
1773 | |||
1774 | return 1; | ||
1775 | } | ||
1776 | |||
1777 | sub _h_07_0024 { | ||
1778 | my $self = shift; | ||
1779 | my $data = shift; | ||
1780 | my $state = shift; | ||
1781 | |||
1782 | printf("Reserved command (0x07, 0x24) I->D\n"); | ||
1783 | |||
1784 | return 1; | ||
1785 | } | ||
1786 | |||
1787 | |||
1788 | package main; | ||
1789 | |||
1790 | use Device::iPod; | ||
1791 | use Getopt::Long; | ||
1792 | use strict; | ||
1793 | |||
1794 | my $decoder; | ||
1795 | my $device; | ||
1796 | my $unpacker; | ||
1797 | my $line; | ||
1798 | |||
1799 | sub unpack_hexstring { | ||
1800 | my $line = shift; | ||
1801 | my $m; | ||
1802 | my @m; | ||
1803 | |||
1804 | $line =~ s/(..)/chr(hex($1))/ge; | ||
1805 | $device->{-inbuf} = $line; | ||
1806 | |||
1807 | $m = $device->_message(); | ||
1808 | next unless defined($m); | ||
1809 | @m = $device->_unframe_cmd($m); | ||
1810 | unless(@m) { | ||
1811 | printf("Line %d: Error decoding frame: %s\n", $., $device->error()); | ||
1812 | return (); | ||
1813 | } | ||
1814 | |||
1815 | return @m; | ||
1816 | } | ||
1817 | |||
1818 | sub unpack_iaplog { | ||
1819 | my $line = shift; | ||
1820 | my @m; | ||
1821 | |||
1822 | unless ($line =~ /^(?:\[\d+\] )?[RT]::? /) { | ||
1823 | printf("Skipped: %s\n", $line); | ||
1824 | return (); | ||
1825 | } | ||
1826 | |||
1827 | $line =~ s/^(?:\[\d+\] )?[RT]::? //; | ||
1828 | $line =~ s/\\x(..)/chr(hex($1))/ge; | ||
1829 | $line =~ s/\\\\/\\/g; | ||
1830 | |||
1831 | @m = unpack("CCa*", $line); | ||
1832 | if ($m[0] == 0x04) { | ||
1833 | @m = unpack("Cna*", $line); | ||
1834 | } | ||
1835 | |||
1836 | return @m; | ||
1837 | } | ||
1838 | |||
1839 | |||
1840 | $decoder = iap::decode->new(); | ||
1841 | $device = Device::iPod->new(); | ||
1842 | $unpacker = \&unpack_iaplog; | ||
1843 | |||
1844 | GetOptions("hexstring" => sub {$unpacker = \&unpack_hexstring}); | ||
1845 | |||
1846 | while ($line = <>) { | ||
1847 | my @m; | ||
1848 | |||
1849 | chomp($line); | ||
1850 | |||
1851 | @m = $unpacker->($line); | ||
1852 | next unless (@m); | ||
1853 | |||
1854 | printf("Line %d: ", $.); | ||
1855 | $decoder->display(@m); | ||
1856 | } | ||
diff --git a/tools/iap/ipod-001-general.t b/tools/iap/ipod-001-general.t new file mode 100644 index 0000000000..f2b5451dbc --- /dev/null +++ b/tools/iap/ipod-001-general.t | |||
@@ -0,0 +1,133 @@ | |||
1 | use Test::More qw( no_plan ); | ||
2 | use strict; | ||
3 | |||
4 | BEGIN { use_ok('Device::iPod'); } | ||
5 | require_ok('Device::iPod'); | ||
6 | |||
7 | my $ipod = Device::iPod->new(); | ||
8 | my $m; | ||
9 | my ($l, $c, $p); | ||
10 | |||
11 | isa_ok($ipod, 'Device::iPod'); | ||
12 | |||
13 | $ipod->{-debug} = 1; | ||
14 | $ipod->open("/dev/ttyUSB0"); | ||
15 | |||
16 | $m = $ipod->sendraw("\xFF" x 16); # Wake up and sync | ||
17 | ok($m == 1, "Wakeup sent"); | ||
18 | |||
19 | # Empty the buffer | ||
20 | $ipod->emptyrecv(); | ||
21 | |||
22 | # Send a command with wrong checksum | ||
23 | # We expect no response (Timeout) | ||
24 | $m = $ipod->sendraw("\xff\x55\x02\x00\x03\x00"); | ||
25 | ok($m == 1, "Broken checksum sent"); | ||
26 | ($l, $c, $p) = $ipod->recvmsg(); | ||
27 | subtest "Timeout" => sub { | ||
28 | ok(!defined($l), "No response received"); | ||
29 | like($ipod->error(), '/Timeout/', "Timeout reading response"); | ||
30 | }; | ||
31 | |||
32 | # Empty the buffer | ||
33 | $ipod->emptyrecv(); | ||
34 | |||
35 | # Send a too short command | ||
36 | # We expect an ACK Bad Parameter as response | ||
37 | $m = $ipod->sendmsg(0x00, 0x13, ""); | ||
38 | ok($m == 1, "Short command sent"); | ||
39 | ($l, $c, $p) = $ipod->recvmsg(); | ||
40 | subtest "ACK Bad Parameter" => sub { | ||
41 | ok(defined($l), "Response received"); | ||
42 | is($l, 0x00, "Response lingo"); | ||
43 | is($c, 0x02, "Response command"); | ||
44 | is($p, "\x04\x13", "Response payload"); | ||
45 | }; | ||
46 | |||
47 | # Empty the buffer | ||
48 | $ipod->emptyrecv(); | ||
49 | |||
50 | # Send an undefined lingo | ||
51 | # We expect a timeout | ||
52 | $m = $ipod->sendmsg(0x1F, 0x00); | ||
53 | ok($m == 1, "Undefined lingo sent"); | ||
54 | ($l, $c, $p) = $ipod->recvmsg(); | ||
55 | subtest "Timeout" => sub { | ||
56 | ok(!defined($l), "No response received"); | ||
57 | like($ipod->error(), '/Timeout/', "Timeout reading response"); | ||
58 | }; | ||
59 | |||
60 | # Empty the buffer | ||
61 | $ipod->emptyrecv(); | ||
62 | |||
63 | # IdentifyDeviceLingoes(lingos=0x80000011, options=0x00, deviceid=0x00) | ||
64 | # We expect an ACK Command Failed message as response | ||
65 | $m = $ipod->sendmsg(0x00, 0x13, "\x80\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00"); | ||
66 | ok($m == 1, "IdentifyDeviceLingoes(lingos=0x80000011, options=0x00, deviceid=0x00) sent"); | ||
67 | ($l, $c, $p) = $ipod->recvmsg(); | ||
68 | subtest "ACK Command Failed" => sub { | ||
69 | ok(defined($l), "Response received"); | ||
70 | is($l, 0x00, "Response lingo"); | ||
71 | is($c, 0x02, "Response command"); | ||
72 | is($p, "\x02\x13", "Response payload"); | ||
73 | }; | ||
74 | |||
75 | # Empty the buffer | ||
76 | $ipod->emptyrecv(); | ||
77 | |||
78 | # Identify(lingo=0xFF) | ||
79 | # We expect no response (timeout) | ||
80 | $m = $ipod->sendmsg(0x00, 0x01, "\xFF"); | ||
81 | ok($m == 1, "Identify(lingo=0xFF) sent"); | ||
82 | ($l, $c, $p) = $ipod->recvmsg(); | ||
83 | subtest "Timeout" => sub { | ||
84 | ok(!defined($l), "No response received"); | ||
85 | like($ipod->error(), '/Timeout/', "Timeout reading response"); | ||
86 | }; | ||
87 | |||
88 | # Empty the buffer | ||
89 | $ipod->emptyrecv(); | ||
90 | |||
91 | # IdentifyDeviceLingoes(lingos=0x10, options=0x00, deviceid=0x00) | ||
92 | # We expect an ACK Command Failed message as response | ||
93 | $m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00"); | ||
94 | ok($m == 1, "IdentifyDeviceLingoes(lingos=0x10, options=0x00, deviceid=0x00) sent"); | ||
95 | ($l, $c, $p) = $ipod->recvmsg(); | ||
96 | subtest "ACK Command Failed" => sub { | ||
97 | ok(defined($l), "Response received"); | ||
98 | is($l, 0x00, "Response lingo"); | ||
99 | is($c, 0x02, "Response command"); | ||
100 | is($p, "\x02\x13", "Response payload"); | ||
101 | }; | ||
102 | |||
103 | # Empty the buffer | ||
104 | $ipod->emptyrecv(); | ||
105 | # IdentifyDeviceLingoes(lingos=0x00, options=0x00, deviceid=0x00) | ||
106 | # We expect an ACK Command Failed message as response | ||
107 | $m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); | ||
108 | ok($m == 1, "IdentifyDeviceLingoes(lingos=0x00, options=0x00, deviceid=0x00) sent"); | ||
109 | ($l, $c, $p) = $ipod->recvmsg(); | ||
110 | subtest "ACK Command Failed" => sub { | ||
111 | ok(defined($l), "Response received"); | ||
112 | is($l, 0x00, "Response lingo"); | ||
113 | is($c, 0x02, "Response command"); | ||
114 | is($p, "\x02\x13", "Response payload"); | ||
115 | }; | ||
116 | |||
117 | # Empty the buffer | ||
118 | $ipod->emptyrecv(); | ||
119 | |||
120 | # RequestLingoProtocolVersion(lingo=0xFF) | ||
121 | # We expect an ACK Bad Parameter message as response | ||
122 | $m = $ipod->sendmsg(0x00, 0x0F, "\xFF"); | ||
123 | ok($m == 1, "RequestLingoProtocolVersion(lingo=0xFF) sent"); | ||
124 | ($l, $c, $p) = $ipod->recvmsg(); | ||
125 | subtest "ACK Bad Parameter" => sub { | ||
126 | ok(defined($l), "Response received"); | ||
127 | is($l, 0x00, "Response lingo"); | ||
128 | is($c, 0x02, "Response command"); | ||
129 | is($p, "\x04\x0F", "Response payload"); | ||
130 | }; | ||
131 | |||
132 | # Empty the buffer | ||
133 | $ipod->emptyrecv(); | ||
diff --git a/tools/iap/ipod-002-lingo0.t b/tools/iap/ipod-002-lingo0.t new file mode 100644 index 0000000000..c3bb676553 --- /dev/null +++ b/tools/iap/ipod-002-lingo0.t | |||
@@ -0,0 +1,277 @@ | |||
1 | use Test::More qw( no_plan ); | ||
2 | use strict; | ||
3 | |||
4 | BEGIN { use_ok('Device::iPod'); } | ||
5 | require_ok('Device::iPod'); | ||
6 | |||
7 | my $ipod = Device::iPod->new(); | ||
8 | my $m; | ||
9 | my ($l, $c, $p); | ||
10 | |||
11 | isa_ok($ipod, 'Device::iPod'); | ||
12 | |||
13 | $ipod->{-debug} = 1; | ||
14 | $ipod->open("/dev/ttyUSB0"); | ||
15 | |||
16 | $m = $ipod->sendraw("\xFF" x 16); # Wake up and sync | ||
17 | ok($m == 1, "Wakeup sent"); | ||
18 | |||
19 | # Empty the buffer | ||
20 | $ipod->emptyrecv(); | ||
21 | |||
22 | # IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00) | ||
23 | # We expect an ACK OK message as response | ||
24 | $m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"); | ||
25 | ok($m == 1, "IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00) sent"); | ||
26 | ($l, $c, $p) = $ipod->recvmsg(); | ||
27 | subtest "ACK OK" => sub { | ||
28 | ok(defined($l), "Response received"); | ||
29 | is($l, 0x00, "Response lingo"); | ||
30 | is($c, 0x02, "Response command"); | ||
31 | is($p, "\x00\x13", "Response payload"); | ||
32 | }; | ||
33 | |||
34 | # Empty the buffer | ||
35 | $ipod->emptyrecv(); | ||
36 | |||
37 | # RequestRemoteUIMode | ||
38 | # We expect an ACK Bad Parameter as response, as we have not | ||
39 | # negotiated lingo 0x04 | ||
40 | $m = $ipod->sendmsg(0x00, 0x03); | ||
41 | ok($m == 1, "RequestRemoteUIMode sent"); | ||
42 | ($l, $c, $p) = $ipod->recvmsg(); | ||
43 | subtest "ACK Bad Parameter" => sub { | ||
44 | ok(defined($l), "Response received"); | ||
45 | is($l, 0x00, "Response lingo"); | ||
46 | is($c, 0x02, "Response command"); | ||
47 | is($p, "\x04\x03", "Response payload"); | ||
48 | }; | ||
49 | |||
50 | # Empty the buffer | ||
51 | $ipod->emptyrecv(); | ||
52 | |||
53 | # EnterRemoteUIMode | ||
54 | # We expect an ACK Bad Parameter as response, as we have not | ||
55 | # negotiated lingo 0x04 | ||
56 | $m = $ipod->sendmsg(0x00, 0x05); | ||
57 | ok($m == 1, "EnterRemoteUIMode sent"); | ||
58 | ($l, $c, $p) = $ipod->recvmsg(); | ||
59 | subtest "ACK Bad Parameter" => sub { | ||
60 | ok(defined($l), "Response received"); | ||
61 | is($l, 0x00, "Response lingo"); | ||
62 | is($c, 0x02, "Response command"); | ||
63 | is($p, "\x04\x05", "Response payload"); | ||
64 | }; | ||
65 | |||
66 | # Empty the buffer | ||
67 | $ipod->emptyrecv(); | ||
68 | |||
69 | # ExitRemoteUIMode | ||
70 | # We expect an ACK Bad Parameter as response, as we have not | ||
71 | # negotiated lingo 0x04 | ||
72 | $m = $ipod->sendmsg(0x00, 0x06); | ||
73 | ok($m == 1, "ExitRemoteUIMode sent"); | ||
74 | ($l, $c, $p) = $ipod->recvmsg(); | ||
75 | subtest "ACK Bad Parameter" => sub { | ||
76 | ok(defined($l), "Response received"); | ||
77 | is($l, 0x00, "Response lingo"); | ||
78 | is($c, 0x02, "Response command"); | ||
79 | is($p, "\x04\x06", "Response payload"); | ||
80 | }; | ||
81 | |||
82 | # Empty the buffer | ||
83 | $ipod->emptyrecv(); | ||
84 | |||
85 | # RequestiPodName | ||
86 | # We expect a ReturniPodName packet | ||
87 | $m = $ipod->sendmsg(0x00, 0x07); | ||
88 | ok($m == 1, "RequestiPodName sent"); | ||
89 | ($l, $c, $p) = $ipod->recvmsg(); | ||
90 | subtest "ReturniPodName" => sub { | ||
91 | ok(defined($l), "Response received"); | ||
92 | is($l, 0x00, "Response lingo"); | ||
93 | is($c, 0x08, "Response command"); | ||
94 | like($p, "/^[^\\x00]*\\x00\$/", "Response payload"); | ||
95 | }; | ||
96 | |||
97 | # Empty the buffer | ||
98 | $ipod->emptyrecv(); | ||
99 | |||
100 | # RequestiPodSoftwareVersion | ||
101 | # We expect a ReturniPodSoftwareVersion packet | ||
102 | $m = $ipod->sendmsg(0x00, 0x09); | ||
103 | ok($m == 1, "RequestiPodSoftwareVersion sent"); | ||
104 | ($l, $c, $p) = $ipod->recvmsg(); | ||
105 | subtest "ReturniPodSoftwareVersion" => sub { | ||
106 | ok(defined($l), "Response received"); | ||
107 | is($l, 0x00, "Response lingo"); | ||
108 | is($c, 0x0A, "Response command"); | ||
109 | like($p, "/^...\$/", "Response payload"); | ||
110 | }; | ||
111 | |||
112 | # Empty the buffer | ||
113 | $ipod->emptyrecv(); | ||
114 | |||
115 | # RequestiPodSerialNumber | ||
116 | # We expect a ReturniPodSerialNumber packet | ||
117 | $m = $ipod->sendmsg(0x00, 0x0B); | ||
118 | ok($m == 1, "RequestiPodSerialNumber sent"); | ||
119 | ($l, $c, $p) = $ipod->recvmsg(); | ||
120 | subtest "ReturniPodSerialNumber" => sub { | ||
121 | ok(defined($l), "Response received"); | ||
122 | is($l, 0x00, "Response lingo"); | ||
123 | is($c, 0x0C, "Response command"); | ||
124 | like($p, "/^[^\\x00]*\\x00\$/", "Response payload"); | ||
125 | }; | ||
126 | |||
127 | # Empty the buffer | ||
128 | $ipod->emptyrecv(); | ||
129 | |||
130 | # RequestiPodModelNum | ||
131 | # We expect a ReturniPodModelNum packet | ||
132 | $m = $ipod->sendmsg(0x00, 0x0D); | ||
133 | ok($m == 1, "RequestiPodModelNum sent"); | ||
134 | ($l, $c, $p) = $ipod->recvmsg(); | ||
135 | subtest "ReturniPodModelNum" => sub { | ||
136 | ok(defined($l), "Response received"); | ||
137 | is($l, 0x00, "Response lingo"); | ||
138 | is($c, 0x0E, "Response command"); | ||
139 | like($p, "/^....[^\\x00]*\\x00\$/", "Response payload"); | ||
140 | }; | ||
141 | |||
142 | # Empty the buffer | ||
143 | $ipod->emptyrecv(); | ||
144 | |||
145 | # RequestLingoProtocolVersion(lingo=0x00) | ||
146 | # We expect a ReturnLingoProtocolVersion packet | ||
147 | $m = $ipod->sendmsg(0x00, 0x0F, "\x00"); | ||
148 | ok($m == 1, "RequestLingoProtocolVersion(lingo=0x00) sent"); | ||
149 | ($l, $c, $p) = $ipod->recvmsg(); | ||
150 | subtest "ReturnLingoProtocolVersion" => sub { | ||
151 | ok(defined($l), "Response received"); | ||
152 | is($l, 0x00, "Response lingo"); | ||
153 | is($c, 0x10, "Response command"); | ||
154 | like($p, "/^\\x00..\$/", "Response payload"); | ||
155 | }; | ||
156 | |||
157 | # Empty the buffer | ||
158 | $ipod->emptyrecv(); | ||
159 | |||
160 | # IdentifyDeviceLingoes(lingos=0x11, options=0x00, deviceid=0x00) | ||
161 | # We expect an ACK OK message as response | ||
162 | $m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00"); | ||
163 | ok($m == 1, "IdentifyDeviceLingoes(lingos=0x11, options=0x00, deviceid=0x00) sent"); | ||
164 | ($l, $c, $p) = $ipod->recvmsg(); | ||
165 | subtest "ACK OK" => sub { | ||
166 | ok(defined($l), "Response received"); | ||
167 | is($l, 0x00, "Response lingo"); | ||
168 | is($c, 0x02, "Response command"); | ||
169 | is($p, "\x00\x13", "Response payload"); | ||
170 | }; | ||
171 | |||
172 | # Empty the buffer | ||
173 | $ipod->emptyrecv(); | ||
174 | |||
175 | # RequestRemoteUIMode | ||
176 | # We expect an ReturnRemoteUIMode packet specifying standard mode | ||
177 | $m = $ipod->sendmsg(0x00, 0x03); | ||
178 | ok($m == 1, "RequestRemoteUIMode sent"); | ||
179 | ($l, $c, $p) = $ipod->recvmsg(); | ||
180 | subtest "ReturnRemoteUIMode" => sub { | ||
181 | ok(defined($l), "Response received"); | ||
182 | is($l, 0x00, "Response lingo"); | ||
183 | is($c, 0x04, "Response command"); | ||
184 | is($p, "\x00", "Response payload"); | ||
185 | }; | ||
186 | |||
187 | # Empty the buffer | ||
188 | $ipod->emptyrecv(); | ||
189 | |||
190 | # EnterRemoteUIMode | ||
191 | # We expect an ACK Pending packet, followed by an ACK OK packet | ||
192 | $m = $ipod->sendmsg(0x00, 0x05); | ||
193 | ok($m == 1, "EnterRemoteUIMode sent"); | ||
194 | ($l, $c, $p) = $ipod->recvmsg(); | ||
195 | subtest "ACK Pending" => sub { | ||
196 | ok(defined($l), "Response received"); | ||
197 | is($l, 0x00, "Response lingo"); | ||
198 | is($c, 0x02, "Response command"); | ||
199 | like($p, "/^\\x06\\x05/", "Response payload"); | ||
200 | }; | ||
201 | ($l, $c, $p) = $ipod->recvmsg(); | ||
202 | subtest "ACK OK" => sub { | ||
203 | ok(defined($l), "Response received"); | ||
204 | is($l, 0x00, "Response lingo"); | ||
205 | is($c, 0x02, "Response command"); | ||
206 | is($p, "\x00\x05", "Response payload"); | ||
207 | }; | ||
208 | |||
209 | # Empty the buffer | ||
210 | $ipod->emptyrecv(); | ||
211 | |||
212 | # RequestRemoteUIMode | ||
213 | # We expect an ReturnRemoteUIMode packet specifying extended mode | ||
214 | $m = $ipod->sendmsg(0x00, 0x03); | ||
215 | ok($m == 1, "RequestRemoteUIMode sent"); | ||
216 | ($l, $c, $p) = $ipod->recvmsg(); | ||
217 | subtest "ReturnRemoteUIMode" => sub { | ||
218 | ok(defined($l), "Response received"); | ||
219 | is($l, 0x00, "Response lingo"); | ||
220 | is($c, 0x04, "Response command"); | ||
221 | isnt($p, "\x00", "Response payload"); | ||
222 | }; | ||
223 | |||
224 | # Empty the buffer | ||
225 | $ipod->emptyrecv(); | ||
226 | |||
227 | # ExitRemoteUIMode | ||
228 | # We expect an ACK Pending packet, followed by an ACK OK packet | ||
229 | $m = $ipod->sendmsg(0x00, 0x06); | ||
230 | ok($m == 1, "ExitRemoteUIMode sent"); | ||
231 | ($l, $c, $p) = $ipod->recvmsg(); | ||
232 | subtest "ACK Pending" => sub { | ||
233 | ok(defined($l), "Response received"); | ||
234 | is($l, 0x00, "Response lingo"); | ||
235 | is($c, 0x02, "Response command"); | ||
236 | like($p, "/^\\x06\\x06/", "Response payload"); | ||
237 | }; | ||
238 | ($l, $c, $p) = $ipod->recvmsg(); | ||
239 | subtest "ACK OK" => sub { | ||
240 | ok(defined($l), "Response received"); | ||
241 | is($l, 0x00, "Response lingo"); | ||
242 | is($c, 0x02, "Response command"); | ||
243 | is($p, "\x00\x06", "Response payload"); | ||
244 | }; | ||
245 | |||
246 | # Empty the buffer | ||
247 | $ipod->emptyrecv(); | ||
248 | |||
249 | # RequestRemoteUIMode | ||
250 | # We expect an ReturnRemoteUIMode packet specifying standard mode | ||
251 | $m = $ipod->sendmsg(0x00, 0x03); | ||
252 | ok($m == 1, "RequestRemoteUIMode sent"); | ||
253 | ($l, $c, $p) = $ipod->recvmsg(); | ||
254 | subtest "ReturnRemoteUIMode" => sub { | ||
255 | ok(defined($l), "Response received"); | ||
256 | is($l, 0x00, "Response lingo"); | ||
257 | is($c, 0x04, "Response command"); | ||
258 | is($p, "\x00", "Response payload"); | ||
259 | }; | ||
260 | |||
261 | # Empty the buffer | ||
262 | $ipod->emptyrecv(); | ||
263 | |||
264 | # Send an undefined command | ||
265 | # We expect an ACK Bad Parameter as response | ||
266 | $m = $ipod->sendmsg(0x00, 0xFF); | ||
267 | ok($m == 1, "Undefined command sent"); | ||
268 | ($l, $c, $p) = $ipod->recvmsg(); | ||
269 | subtest "ACK Bad Parameter" => sub { | ||
270 | ok(defined($l), "Response received"); | ||
271 | is($l, 0x00, "Response lingo"); | ||
272 | is($c, 0x02, "Response command"); | ||
273 | is($p, "\x04\xFF", "Response payload"); | ||
274 | }; | ||
275 | |||
276 | # Empty the buffer | ||
277 | $ipod->emptyrecv(); | ||
diff --git a/tools/iap/ipod-003-lingo2.t b/tools/iap/ipod-003-lingo2.t new file mode 100644 index 0000000000..ee0bd6972e --- /dev/null +++ b/tools/iap/ipod-003-lingo2.t | |||
@@ -0,0 +1,220 @@ | |||
1 | use Test::More qw( no_plan ); | ||
2 | use strict; | ||
3 | |||
4 | BEGIN { use_ok('Device::iPod'); } | ||
5 | require_ok('Device::iPod'); | ||
6 | |||
7 | my $ipod = Device::iPod->new(); | ||
8 | my $m; | ||
9 | my ($l, $c, $p); | ||
10 | |||
11 | isa_ok($ipod, 'Device::iPod'); | ||
12 | |||
13 | $ipod->{-debug} = 1; | ||
14 | $ipod->open("/dev/ttyUSB0"); | ||
15 | |||
16 | $m = $ipod->sendraw("\xFF" x 16); # Wake up and sync | ||
17 | ok($m == 1, "Wakeup sent"); | ||
18 | |||
19 | # Empty the buffer | ||
20 | $ipod->emptyrecv(); | ||
21 | |||
22 | # IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00) | ||
23 | # We expect an ACK OK message as response | ||
24 | $m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"); | ||
25 | ok($m == 1, "IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00) sent"); | ||
26 | ($l, $c, $p) = $ipod->recvmsg(); | ||
27 | subtest "ACK OK" => sub { | ||
28 | ok(defined($l), "Response received"); | ||
29 | is($l, 0x00, "Response lingo"); | ||
30 | is($c, 0x02, "Response command"); | ||
31 | is($p, "\x00\x13", "Response payload"); | ||
32 | }; | ||
33 | |||
34 | # Empty the buffer | ||
35 | $ipod->emptyrecv(); | ||
36 | |||
37 | # ContextButtonStatus(0x00) | ||
38 | # We expect an ACK Bad Parameter message as response | ||
39 | $m = $ipod->sendmsg(0x02, 0x00, "\x00"); | ||
40 | ok($m == 1, "ContextButtonStatus(0x00)"); | ||
41 | ($l, $c, $p) = $ipod->recvmsg(); | ||
42 | subtest "ACK Bad Parameter" => sub { | ||
43 | ok(defined($l), "Response received"); | ||
44 | is($l, 0x02, "Response lingo"); | ||
45 | is($c, 0x01, "Response command"); | ||
46 | is($p, "\x04\x00", "Response payload"); | ||
47 | }; | ||
48 | |||
49 | # Empty the buffer | ||
50 | $ipod->emptyrecv(); | ||
51 | |||
52 | # Identify(lingo=0x00) | ||
53 | # We expect no response (timeout) | ||
54 | $m = $ipod->sendmsg(0x00, 0x01, "\x00"); | ||
55 | ok($m == 1, "Identify(lingo=0x00) sent"); | ||
56 | ($l, $c, $p) = $ipod->recvmsg(); | ||
57 | subtest "Timeout" => sub { | ||
58 | ok(!defined($l), "No response received"); | ||
59 | like($ipod->error(), '/Timeout/', "Timeout reading response"); | ||
60 | }; | ||
61 | |||
62 | # Empty the buffer | ||
63 | $ipod->emptyrecv(); | ||
64 | |||
65 | # ContextButtonStatus(0x00) | ||
66 | # We expect a timeout as response | ||
67 | $m = $ipod->sendmsg(0x02, 0x00, "\x00"); | ||
68 | ok($m == 1, "ContextButtonStatus(0x00)"); | ||
69 | ($l, $c, $p) = $ipod->recvmsg(); | ||
70 | subtest "Timeout" => sub { | ||
71 | ok(!defined($l), "Response received"); | ||
72 | like($ipod->error(), '/Timeout/', "Timeout reading response"); | ||
73 | }; | ||
74 | |||
75 | # Empty the buffer | ||
76 | $ipod->emptyrecv(); | ||
77 | |||
78 | # Identify(lingo=0x02) | ||
79 | # We expect no response (timeout) | ||
80 | $m = $ipod->sendmsg(0x00, 0x01, "\x02"); | ||
81 | ok($m == 1, "Identify(lingo=0x02) sent"); | ||
82 | ($l, $c, $p) = $ipod->recvmsg(); | ||
83 | subtest "Timeout" => sub { | ||
84 | ok(!defined($l), "No response received"); | ||
85 | like($ipod->error(), '/Timeout/', "Timeout reading response"); | ||
86 | }; | ||
87 | |||
88 | # Empty the buffer | ||
89 | $ipod->emptyrecv(); | ||
90 | |||
91 | # ContextButtonStatus(0x00) | ||
92 | # We expect a timeout as response | ||
93 | $m = $ipod->sendmsg(0x02, 0x00, "\x00"); | ||
94 | ok($m == 1, "ContextButtonStatus(0x00)"); | ||
95 | ($l, $c, $p) = $ipod->recvmsg(); | ||
96 | subtest "Timeout" => sub { | ||
97 | ok(!defined($l), "Response received"); | ||
98 | like($ipod->error(), '/Timeout/', "Timeout reading response"); | ||
99 | }; | ||
100 | |||
101 | # Empty the buffer | ||
102 | $ipod->emptyrecv(); | ||
103 | |||
104 | # IdentifyDeviceLingoes(lingos=0x05, options=0x00, deviceid=0x00) | ||
105 | # We expect an ACK OK message as response | ||
106 | $m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00"); | ||
107 | ok($m == 1, "IdentifyDeviceLingoes(lingos=0x05, options=0x00, deviceid=0x00) sent"); | ||
108 | ($l, $c, $p) = $ipod->recvmsg(); | ||
109 | subtest "ACK OK" => sub { | ||
110 | ok(defined($l), "Response received"); | ||
111 | is($l, 0x00, "Response lingo"); | ||
112 | is($c, 0x02, "Response command"); | ||
113 | is($p, "\x00\x13", "Response payload"); | ||
114 | }; | ||
115 | |||
116 | # Empty the buffer | ||
117 | $ipod->emptyrecv(); | ||
118 | |||
119 | # RequestLingoProtocolVersion(lingo=0x02) | ||
120 | # We expect a ReturnLingoProtocolVersion packet | ||
121 | $m = $ipod->sendmsg(0x00, 0x0F, "\x02"); | ||
122 | ok($m == 1, "RequestLingoProtocolVersion(lingo=0x02) sent"); | ||
123 | ($l, $c, $p) = $ipod->recvmsg(); | ||
124 | subtest "ReturnLingoProtocolVersion" => sub { | ||
125 | ok(defined($l), "Response received"); | ||
126 | is($l, 0x00, "Response lingo"); | ||
127 | is($c, 0x10, "Response command"); | ||
128 | like($p, "/^\\x02..\$/", "Response payload"); | ||
129 | }; | ||
130 | |||
131 | # Empty the buffer | ||
132 | $ipod->emptyrecv(); | ||
133 | |||
134 | # Send an undefined command | ||
135 | # We expect an ACK Bad Parameter as response | ||
136 | $m = $ipod->sendmsg(0x02, 0xFF); | ||
137 | ok($m == 1, "Undefined command sent"); | ||
138 | ($l, $c, $p) = $ipod->recvmsg(); | ||
139 | subtest "ACK Bad Parameter" => sub { | ||
140 | ok(defined($l), "Response received"); | ||
141 | is($l, 0x02, "Response lingo"); | ||
142 | is($c, 0x01, "Response command"); | ||
143 | is($p, "\x04\xFF", "Response payload"); | ||
144 | }; | ||
145 | |||
146 | # Empty the buffer | ||
147 | $ipod->emptyrecv(); | ||
148 | |||
149 | # ContextButtonStatus(0x00) | ||
150 | # We expect a timeout as response | ||
151 | $m = $ipod->sendmsg(0x02, 0x00, "\x00"); | ||
152 | ok($m == 1, "ContextButtonStatus(0x00)"); | ||
153 | ($l, $c, $p) = $ipod->recvmsg(); | ||
154 | subtest "Timeout" => sub { | ||
155 | ok(!defined($l), "Response received"); | ||
156 | like($ipod->error(), '/Timeout/', "Timeout reading response"); | ||
157 | }; | ||
158 | |||
159 | # Empty the buffer | ||
160 | $ipod->emptyrecv(); | ||
161 | |||
162 | # Send a too short command | ||
163 | # We expect an ACK Bad Parameter as response | ||
164 | $m = $ipod->sendmsg(0x02, 0x00, ""); | ||
165 | ok($m == 1, "Short command sent"); | ||
166 | ($l, $c, $p) = $ipod->recvmsg(); | ||
167 | subtest "ACK Bad Parameter" => sub { | ||
168 | ok(defined($l), "Response received"); | ||
169 | is($l, 0x02, "Response lingo"); | ||
170 | is($c, 0x01, "Response command"); | ||
171 | is($p, "\x04\x00", "Response payload"); | ||
172 | }; | ||
173 | |||
174 | # Empty the buffer | ||
175 | $ipod->emptyrecv(); | ||
176 | |||
177 | # ImageButtonStatus(0x00) | ||
178 | # We expect an ACK Not Authenticated as response | ||
179 | $m = $ipod->sendmsg(0x02, 0x02, "\x00"); | ||
180 | ok($m == 1, "ImageButtonStatus(0x00)"); | ||
181 | ($l, $c, $p) = $ipod->recvmsg(); | ||
182 | subtest "ACK Not Authenticated" => sub { | ||
183 | ok(defined($l), "Response received"); | ||
184 | is($l, 0x02, "Response lingo"); | ||
185 | is($c, 0x01, "Response command"); | ||
186 | is($p, "\x07\x02", "Response payload"); | ||
187 | }; | ||
188 | |||
189 | # Empty the buffer | ||
190 | $ipod->emptyrecv(); | ||
191 | |||
192 | # VideoButtonStatus(0x00) | ||
193 | # We expect an ACK Not Authenticated as response | ||
194 | $m = $ipod->sendmsg(0x02, 0x03, "\x00"); | ||
195 | ok($m == 1, "VideoButtonStatus(0x00)"); | ||
196 | ($l, $c, $p) = $ipod->recvmsg(); | ||
197 | subtest "ACK Not Authenticated" => sub { | ||
198 | ok(defined($l), "Response received"); | ||
199 | is($l, 0x02, "Response lingo"); | ||
200 | is($c, 0x01, "Response command"); | ||
201 | is($p, "\x07\x03", "Response payload"); | ||
202 | }; | ||
203 | |||
204 | # Empty the buffer | ||
205 | $ipod->emptyrecv(); | ||
206 | |||
207 | # AudioButtonStatus(0x00) | ||
208 | # We expect an ACK Not Authenticated as response | ||
209 | $m = $ipod->sendmsg(0x02, 0x04, "\x00"); | ||
210 | ok($m == 1, "AudioButtonStatus(0x00)"); | ||
211 | ($l, $c, $p) = $ipod->recvmsg(); | ||
212 | subtest "ACK Not Authenticated" => sub { | ||
213 | ok(defined($l), "Response received"); | ||
214 | is($l, 0x02, "Response lingo"); | ||
215 | is($c, 0x01, "Response command"); | ||
216 | is($p, "\x07\x04", "Response payload"); | ||
217 | }; | ||
218 | |||
219 | # Empty the buffer | ||
220 | $ipod->emptyrecv(); | ||