Updated IAP commands.

Originally written and uploaded by Lalufu (Ralf Ertzinger) in Feb 2012.
They have been condensed into a single patch and some further additions
by Andy Potter.

Currently includes Authentication V2 support from iPod to Accessory,
RF/BlueTooth transmitter support, selecting a playlist and selecting a
track from the current playlist. Does not support uploading Album Art
or podcasts. Has been tested on the following iPods,
4th Gen Grayscale, 4th Gen Color/Photo, Mini 2nd Gen, Nano 1st Gen and
Video 5.5Gen.

Change-Id: Ie8fc098361844132f0228ecbe3c48da948726f5e
Co-Authored by: Andy Potter <liveboxandy@gmail.com>
Reviewed-on: http://gerrit.rockbox.org/533
Reviewed-by: Frank Gevaerts <frank@gevaerts.be>
This commit is contained in:
Ralf Ertzinger 2013-06-22 10:08:23 +01:00 committed by Frank Gevaerts
parent 500b137308
commit b170c73f92
21 changed files with 10629 additions and 1125 deletions

386
tools/iap/Device/iPod.pm Normal file
View file

@ -0,0 +1,386 @@
package Device::iPod;
use Device::SerialPort;
use POSIX qw(isgraph);
use strict;
sub new {
my $class = shift;
my $port = shift;
my $self = {};
my $s;
$self->{-serial} = undef;
$self->{-inbuf} = '';
$self->{-error} = undef;
$self->{-baudrate} = 57600;
$self->{-debug} = 0;
return bless($self, $class);
}
sub open {
my $self = shift;
my $port = shift;
$self->{-serial} = new Device::SerialPort($port);
unless(defined($self->{-serial})) {
$self->{-error} = $!;
return undef;
}
$self->{-serial}->parity('none');
$self->{-serial}->databits(8);
$self->{-serial}->stopbits(1);
$self->{-serial}->handshake('none');
return $self->baudrate($self->{-baudrate});
}
sub baudrate {
my $self = shift;
my $baudrate = shift;
if ($baudrate < 1) {
$self->{-error} = "Invalid baudrate";
return undef;
}
$self->{-baudrate} = $baudrate;
if (defined($self->{-serial})) {
$self->{-serial}->baudrate($baudrate);
}
return 1;
}
sub sendmsg {
my $self = shift;
my $lingo = shift;
my $command = shift;
my $data = shift || '';
return $self->_nosetup() unless(defined($self->{-serial}));
if (($lingo < 0) || ($lingo > 255)) {
$self->{-error} = 'Invalid lingo';
return undef;
}
if ($command < 0) {
$self->{-error} = 'Invalid command';
return undef;
}
if ($lingo == 4) {
if ($command > 0xffff) {
$self->{-error} = 'Invalid command';
return undef;
}
return $self->_send($self->_frame_cmd(pack("Cn", $lingo, $command) . $data));
} else {
if ($command > 0xff) {
$self->{-error} = 'Invalid command';
return undef;
}
return $self->_send($self->_frame_cmd(pack("CC", $lingo, $command) . $data));
}
}
sub sendraw {
my $self = shift;
my $data = shift;
return $self->_nosetup() unless(defined($self->{-serial}));
return $self->_send($data);
}
sub recvmsg {
my $self = shift;
my $m;
my @m;
return $self->_nosetup() unless(defined($self->{-serial}));
$m = $self->_fillbuf();
unless(defined($m)) {
# Error was set by lower levels
return wantarray?():undef;
}
printf("Fetched %s\n", $self->_hexstring($m)) if $self->{-debug};
@m = $self->_unframe_cmd($m);
unless(@m) {
return undef;
}
if (wantarray()) {
return @m;
} else {
return {-lingo => $m[0], -cmd => $m[1], -payload => $m[2]};
}
}
sub emptyrecv {
my $self = shift;
my $m;
while ($m = $self->_fillbuf()) {
printf("Discarded %s\n", $self->_hexstring($m)) if (defined($m) && $self->{-debug});
}
}
sub error {
my $self = shift;
return $self->{-error};
}
sub _nosetup {
my $self = shift;
$self->{-error} = 'Serial port not setup';
return undef;
}
sub _frame_cmd {
my $self = shift;
my $data = shift;
my $l = length($data);
my $csum;
if ($l > 0xffff) {
$self->{-error} = 'Command too long';
return undef;
}
if ($l > 255) {
$data = pack("Cn", 0, length($data)) . $data;
} else {
$data = pack("C", length($data)) . $data;
}
foreach (unpack("C" x length($data), $data)) {
$csum += $_;
}
$csum &= 0xFF;
$csum = 0x100 - $csum;
return "\xFF\x55" . $data . pack("C", $csum);
}
sub _unframe_cmd {
my $self = shift;
my $data = shift;
my $payload = '';
my ($count, $length, $csum);
my $state = 0;
my $c;
my ($lingo, $cmd);
return () unless(defined($data));
foreach $c (unpack("C" x length($data), $data)) {
if ($state == 0) {
# Wait for sync
next unless($c == 255);
$state = 1;
} elsif ($state == 1) {
# Wait for sop
next unless($c == 85);
$state = 2;
} elsif ($state == 2) {
# Length (short frame)
$csum = $c;
if ($c == 0) {
# Large frame
$state = 3;
} else {
$state = 5;
}
$length = $c;
$count = 0;
next;
} elsif ($state == 3) {
# Large frame, hi
$csum += $c;
$length = ($c << 8);
$state = 4;
next;
} elsif ($state == 4) {
# Large frame, lo
$csum += $c;
$length |= $c;
if ($length == 0) {
$self->{-error} = 'Length is 0';
return ();
}
$state = 5;
next;
} elsif ($state == 5) {
# Data bytes
$csum += $c;
$payload .= chr($c);
$count += 1;
if ($count == $length) {
$state = 6;
}
} elsif ($state == 6) {
# Checksum byte
$csum += $c;
if (($csum & 0xFF) != 0) {
$self->{-error} = 'Invalid checksum';
return ();
}
$state = 7;
last;
} else {
$self->{-error} = 'Invalid state';
return ();
}
}
# If we get here, we either have data or not. Check.
if ($state != 7) {
$self->{-error} = 'Could not unframe data';
return ();
}
$lingo = unpack("C", $payload);
if ($lingo == 4) {
return unpack("Cna*", $payload);
} else {
return unpack("CCa*", $payload);
}
}
sub _send {
my $self = shift;
my $data = shift;
my $l = length($data);
my $c;
printf("Sending %s\n", $self->_hexstring($data)) if $self->{-debug};
$c = $self->{-serial}->write($data);
unless(defined($c)) {
$self->{-error} = 'write failed';
return undef;
}
if ($c != $l) {
$self->{-error} = 'incomplete write';
return undef;
}
return 1;
}
sub _fillbuf {
my $self = shift;
my $timeout = shift || 2;
my $to;
# Read from the port until we have a complete message in the buffer,
# or until we haven't read any new data for $timeout seconds, whatever
# comes first.
$to = $timeout;
while(!$self->_message_in_buffer() && $to > 0) {
my ($c, $s) = $self->{-serial}->read(255);
if ($c == 0) {
# No data read
select(undef, undef, undef, 0.1);
$to -= 0.1;
} else {
$self->{-inbuf} .= $s;
$to = $timeout;
}
}
if ($self->_message_in_buffer()) {
# There is a complete message in the buffer
return $self->_message();
} else {
# Timeout occured
$self->{-error} = 'Timeout reading from port';
return undef;
}
}
sub _message_in_buffer {
my $self = shift;
my $sp = 0;
my $i;
$i = index($self->{-inbuf}, "\xFF\x55", $sp);
while ($i != -1) {
my $header;
my $len;
my $large = 0;
$header = substr($self->{-inbuf}, $i, 3);
if (length($header) != 3) {
# Runt frame
return ();
}
$len = unpack("x2C", $header);
if ($len == 0) {
# Possible large frame
$header = substr($self->{-inbuf}, $i, 5);
if (length($header) != 5) {
# Runt frame
return ();
}
$large = 1;
$len = unpack("x3n", $header);
}
# Add framing, checksum and length
$len = $len+3+($large?3:1);
if (length($self->{-inbuf}) < ($i+$len)) {
# Buffer too short to hold rest of frame. Try again.
$sp = $i+1;
$i = index($self->{-inbuf}, "\xFF\x55", $sp);
} else {
return ($i, $len);
}
}
# No complete message found
return ();
}
sub _message {
my $self = shift;
my $start;
my $len;
my $m;
# Return the first complete message in the buffer, removing the message
# and everything before it from the buffer.
($start, $len) = $self->_message_in_buffer();
unless(defined($start)) {
$self->{-error} = 'No complete message in buffer';
return undef;
}
$m = substr($self->{-inbuf}, $start, $len);
$self->{-inbuf} = substr($self->{-inbuf}, $start+$len);
return $m;
}
sub _hexstring {
my $self = shift;
my $s = shift;
return join("", map { (($_ == 0x20) || isgraph(chr($_)))?chr($_):sprintf("\\x%02x", $_) }
unpack("C" x length($s), $s));
}
1;

7
tools/iap/Makefile Normal file
View file

@ -0,0 +1,7 @@
default: test
test:
perl -MTest::Harness -e '$$Test::Harness::verbose=1; runtests @ARGV' ipod*.t
moduletest:
perl -MTest::Harness -e '$$Test::Harness::verbose=1; runtests @ARGV' device-ipod.t

23
tools/iap/README Normal file
View file

@ -0,0 +1,23 @@
These are perl test scripts for validating the IAP implementation.
Also included is a perl class for talking to an iPod via the serial
port. You will probably need Linux to use this.
Run "make moduletest" to test the perl module itself. This will not
require any serial connection, or even an iPod, for that matter.
Run "make test" to run the iPod communication tests themselves.
In order to test make sure
- the iPod is connected to a serial port
- the test scripts assume that this port is /dev/ttyUSB0. Change
as neccessary
Sometimes, tests will time out instead of giving the desired result.
As long as the timeouts are not reproducable this is usually not a
problem. The serial port is known to be unreliable, and devices will
retransmit. This happens even with the OF.
The tests were designed against an iPod Touch 2G as a reference device.
Some older iPods fail some of the test, even with the OF, because of
behaviour changes in later firmware releases by Apple.

74
tools/iap/device-ipod.t Normal file
View file

@ -0,0 +1,74 @@
use Test::More qw( no_plan );
use strict;
BEGIN { use_ok('Device::iPod'); }
require_ok('Device::iPod');
my $ipod = Device::iPod->new();
my $m;
my ($l, $c, $p);
isa_ok($ipod, 'Device::iPod');
# Frame a short command
$m = $ipod->_frame_cmd("\x00\x02\x00\x06");
ok(defined($m) && ($m eq "\xFF\x55\x04\x00\x02\x00\x06\xF4"), "Framed command valid");
# Frame a long command
$m = $ipod->_frame_cmd("\x00" x 1024);
ok(defined($m) && ($m eq "\xFF\x55\x00\x04\x00" . ("\x00" x 1024) . "\xFC"), "Long framed command valid");
# Frame an overly long command
$m = $ipod->_frame_cmd("\x00" x 65537);
ok(!defined($m) && ($ipod->error() =~ 'Command too long'), "Overly long command failed");
# Unframe a short command
($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x04\x00\x02\x00\x06\xF4");
ok(defined($l) && ($l == 0x00) && ($c == 0x02) && ($p eq "\x00\x06"), "Unframed short command");
# Unframe a long command
($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x00\x04\x00" . ("\x00" x 1024) . "\xFC");
ok(defined($l) && ($l == 0x00) && ($c == 0x00) && ($p eq "\x00" x 1022), "Unframed long command");
# Frame without sync byte
($l, $c, $p) = $ipod->_unframe_cmd("\x00");
ok(!defined($l) && ($ipod->error() =~ /Could not unframe data/), "Frame without sync byte failed");
# Frame without SOP byte
($l, $c, $p) = $ipod->_unframe_cmd("\xFF");
ok(!defined($l) && ($ipod->error() =~ /Could not unframe data/), "Frame without SOP byte failed");
# Frame with length 0
($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x00\x00\x00");
ok(!defined($l) && ($ipod->error() =~ /Length is 0/), "Frame with length 0 failed");
# Too short frame
($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x03\x00\x00");
ok(!defined($l) && ($ipod->error() =~ /Could not unframe data/), "Too short frame failed");
# Invalid checksum
($l, $c, $p) = $ipod->_unframe_cmd("\xFF\x55\x03\x00\x00\x00\x00");
ok(!defined($l) && ($ipod->error() =~ /Invalid checksum/), "Invalid checksum failed");
# Find a message in a string
$ipod->{-inbuf} = "\x00\xFF\x55\x00\xFF\xFF\xFF\x55\x04\x00\x02\x00\x06\xF4";
($c, $l) = $ipod->_message_in_buffer();
ok(defined($l) && ($c == 6) && ($l == 8), "Found message in buffer");
# Return message from a string
$ipod->{-inbuf} = "\x00\xFF\x55\x00\xFF\xFF\xFF\x55\x04\x00\x02\x00\x06\xF4\x00";
$m = $ipod->_message();
ok(defined($m) && ($ipod->{-inbuf} eq "\x00"), "Retrieved message from buffer");
# Return two messages from buffer
$ipod->{-inbuf} = "\xffU\x04\x00\x02\x00\x13\xe7\xffU\x02\x00\x14\xea";
$m = $ipod->_message();
subtest "First message" => sub {
ok(defined($m), "Message received");
is($m, "\xffU\x04\x00\x02\x00\x13\xe7");
};
$m = $ipod->_message();
subtest "Second message" => sub {
ok(defined($m), "Message received");
is($m, "\xffU\x02\x00\x14\xea");
};

1856
tools/iap/iap-verbose.pl Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,133 @@
use Test::More qw( no_plan );
use strict;
BEGIN { use_ok('Device::iPod'); }
require_ok('Device::iPod');
my $ipod = Device::iPod->new();
my $m;
my ($l, $c, $p);
isa_ok($ipod, 'Device::iPod');
$ipod->{-debug} = 1;
$ipod->open("/dev/ttyUSB0");
$m = $ipod->sendraw("\xFF" x 16); # Wake up and sync
ok($m == 1, "Wakeup sent");
# Empty the buffer
$ipod->emptyrecv();
# Send a command with wrong checksum
# We expect no response (Timeout)
$m = $ipod->sendraw("\xff\x55\x02\x00\x03\x00");
ok($m == 1, "Broken checksum sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "Timeout" => sub {
ok(!defined($l), "No response received");
like($ipod->error(), '/Timeout/', "Timeout reading response");
};
# Empty the buffer
$ipod->emptyrecv();
# Send a too short command
# We expect an ACK Bad Parameter as response
$m = $ipod->sendmsg(0x00, 0x13, "");
ok($m == 1, "Short command sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Bad Parameter" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x04\x13", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# Send an undefined lingo
# We expect a timeout
$m = $ipod->sendmsg(0x1F, 0x00);
ok($m == 1, "Undefined lingo sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "Timeout" => sub {
ok(!defined($l), "No response received");
like($ipod->error(), '/Timeout/', "Timeout reading response");
};
# Empty the buffer
$ipod->emptyrecv();
# IdentifyDeviceLingoes(lingos=0x80000011, options=0x00, deviceid=0x00)
# We expect an ACK Command Failed message as response
$m = $ipod->sendmsg(0x00, 0x13, "\x80\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00");
ok($m == 1, "IdentifyDeviceLingoes(lingos=0x80000011, options=0x00, deviceid=0x00) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Command Failed" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x02\x13", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# Identify(lingo=0xFF)
# We expect no response (timeout)
$m = $ipod->sendmsg(0x00, 0x01, "\xFF");
ok($m == 1, "Identify(lingo=0xFF) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "Timeout" => sub {
ok(!defined($l), "No response received");
like($ipod->error(), '/Timeout/', "Timeout reading response");
};
# Empty the buffer
$ipod->emptyrecv();
# IdentifyDeviceLingoes(lingos=0x10, options=0x00, deviceid=0x00)
# We expect an ACK Command Failed message as response
$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00");
ok($m == 1, "IdentifyDeviceLingoes(lingos=0x10, options=0x00, deviceid=0x00) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Command Failed" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x02\x13", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# IdentifyDeviceLingoes(lingos=0x00, options=0x00, deviceid=0x00)
# We expect an ACK Command Failed message as response
$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
ok($m == 1, "IdentifyDeviceLingoes(lingos=0x00, options=0x00, deviceid=0x00) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Command Failed" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x02\x13", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# RequestLingoProtocolVersion(lingo=0xFF)
# We expect an ACK Bad Parameter message as response
$m = $ipod->sendmsg(0x00, 0x0F, "\xFF");
ok($m == 1, "RequestLingoProtocolVersion(lingo=0xFF) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Bad Parameter" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x04\x0F", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();

277
tools/iap/ipod-002-lingo0.t Normal file
View file

@ -0,0 +1,277 @@
use Test::More qw( no_plan );
use strict;
BEGIN { use_ok('Device::iPod'); }
require_ok('Device::iPod');
my $ipod = Device::iPod->new();
my $m;
my ($l, $c, $p);
isa_ok($ipod, 'Device::iPod');
$ipod->{-debug} = 1;
$ipod->open("/dev/ttyUSB0");
$m = $ipod->sendraw("\xFF" x 16); # Wake up and sync
ok($m == 1, "Wakeup sent");
# Empty the buffer
$ipod->emptyrecv();
# IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00)
# We expect an ACK OK message as response
$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00");
ok($m == 1, "IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK OK" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x00\x13", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# RequestRemoteUIMode
# We expect an ACK Bad Parameter as response, as we have not
# negotiated lingo 0x04
$m = $ipod->sendmsg(0x00, 0x03);
ok($m == 1, "RequestRemoteUIMode sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Bad Parameter" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x04\x03", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# EnterRemoteUIMode
# We expect an ACK Bad Parameter as response, as we have not
# negotiated lingo 0x04
$m = $ipod->sendmsg(0x00, 0x05);
ok($m == 1, "EnterRemoteUIMode sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Bad Parameter" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x04\x05", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# ExitRemoteUIMode
# We expect an ACK Bad Parameter as response, as we have not
# negotiated lingo 0x04
$m = $ipod->sendmsg(0x00, 0x06);
ok($m == 1, "ExitRemoteUIMode sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Bad Parameter" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x04\x06", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# RequestiPodName
# We expect a ReturniPodName packet
$m = $ipod->sendmsg(0x00, 0x07);
ok($m == 1, "RequestiPodName sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ReturniPodName" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x08, "Response command");
like($p, "/^[^\\x00]*\\x00\$/", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# RequestiPodSoftwareVersion
# We expect a ReturniPodSoftwareVersion packet
$m = $ipod->sendmsg(0x00, 0x09);
ok($m == 1, "RequestiPodSoftwareVersion sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ReturniPodSoftwareVersion" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x0A, "Response command");
like($p, "/^...\$/", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# RequestiPodSerialNumber
# We expect a ReturniPodSerialNumber packet
$m = $ipod->sendmsg(0x00, 0x0B);
ok($m == 1, "RequestiPodSerialNumber sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ReturniPodSerialNumber" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x0C, "Response command");
like($p, "/^[^\\x00]*\\x00\$/", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# RequestiPodModelNum
# We expect a ReturniPodModelNum packet
$m = $ipod->sendmsg(0x00, 0x0D);
ok($m == 1, "RequestiPodModelNum sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ReturniPodModelNum" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x0E, "Response command");
like($p, "/^....[^\\x00]*\\x00\$/", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# RequestLingoProtocolVersion(lingo=0x00)
# We expect a ReturnLingoProtocolVersion packet
$m = $ipod->sendmsg(0x00, 0x0F, "\x00");
ok($m == 1, "RequestLingoProtocolVersion(lingo=0x00) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ReturnLingoProtocolVersion" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x10, "Response command");
like($p, "/^\\x00..\$/", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# IdentifyDeviceLingoes(lingos=0x11, options=0x00, deviceid=0x00)
# We expect an ACK OK message as response
$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00");
ok($m == 1, "IdentifyDeviceLingoes(lingos=0x11, options=0x00, deviceid=0x00) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK OK" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x00\x13", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# RequestRemoteUIMode
# We expect an ReturnRemoteUIMode packet specifying standard mode
$m = $ipod->sendmsg(0x00, 0x03);
ok($m == 1, "RequestRemoteUIMode sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ReturnRemoteUIMode" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x04, "Response command");
is($p, "\x00", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# EnterRemoteUIMode
# We expect an ACK Pending packet, followed by an ACK OK packet
$m = $ipod->sendmsg(0x00, 0x05);
ok($m == 1, "EnterRemoteUIMode sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Pending" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
like($p, "/^\\x06\\x05/", "Response payload");
};
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK OK" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x00\x05", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# RequestRemoteUIMode
# We expect an ReturnRemoteUIMode packet specifying extended mode
$m = $ipod->sendmsg(0x00, 0x03);
ok($m == 1, "RequestRemoteUIMode sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ReturnRemoteUIMode" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x04, "Response command");
isnt($p, "\x00", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# ExitRemoteUIMode
# We expect an ACK Pending packet, followed by an ACK OK packet
$m = $ipod->sendmsg(0x00, 0x06);
ok($m == 1, "ExitRemoteUIMode sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Pending" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
like($p, "/^\\x06\\x06/", "Response payload");
};
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK OK" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x00\x06", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# RequestRemoteUIMode
# We expect an ReturnRemoteUIMode packet specifying standard mode
$m = $ipod->sendmsg(0x00, 0x03);
ok($m == 1, "RequestRemoteUIMode sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ReturnRemoteUIMode" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x04, "Response command");
is($p, "\x00", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# Send an undefined command
# We expect an ACK Bad Parameter as response
$m = $ipod->sendmsg(0x00, 0xFF);
ok($m == 1, "Undefined command sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Bad Parameter" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x04\xFF", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();

220
tools/iap/ipod-003-lingo2.t Normal file
View file

@ -0,0 +1,220 @@
use Test::More qw( no_plan );
use strict;
BEGIN { use_ok('Device::iPod'); }
require_ok('Device::iPod');
my $ipod = Device::iPod->new();
my $m;
my ($l, $c, $p);
isa_ok($ipod, 'Device::iPod');
$ipod->{-debug} = 1;
$ipod->open("/dev/ttyUSB0");
$m = $ipod->sendraw("\xFF" x 16); # Wake up and sync
ok($m == 1, "Wakeup sent");
# Empty the buffer
$ipod->emptyrecv();
# IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00)
# We expect an ACK OK message as response
$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00");
ok($m == 1, "IdentifyDeviceLingoes(lingos=0x01, options=0x00, deviceid=0x00) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK OK" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x00\x13", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# ContextButtonStatus(0x00)
# We expect an ACK Bad Parameter message as response
$m = $ipod->sendmsg(0x02, 0x00, "\x00");
ok($m == 1, "ContextButtonStatus(0x00)");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Bad Parameter" => sub {
ok(defined($l), "Response received");
is($l, 0x02, "Response lingo");
is($c, 0x01, "Response command");
is($p, "\x04\x00", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# Identify(lingo=0x00)
# We expect no response (timeout)
$m = $ipod->sendmsg(0x00, 0x01, "\x00");
ok($m == 1, "Identify(lingo=0x00) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "Timeout" => sub {
ok(!defined($l), "No response received");
like($ipod->error(), '/Timeout/', "Timeout reading response");
};
# Empty the buffer
$ipod->emptyrecv();
# ContextButtonStatus(0x00)
# We expect a timeout as response
$m = $ipod->sendmsg(0x02, 0x00, "\x00");
ok($m == 1, "ContextButtonStatus(0x00)");
($l, $c, $p) = $ipod->recvmsg();
subtest "Timeout" => sub {
ok(!defined($l), "Response received");
like($ipod->error(), '/Timeout/', "Timeout reading response");
};
# Empty the buffer
$ipod->emptyrecv();
# Identify(lingo=0x02)
# We expect no response (timeout)
$m = $ipod->sendmsg(0x00, 0x01, "\x02");
ok($m == 1, "Identify(lingo=0x02) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "Timeout" => sub {
ok(!defined($l), "No response received");
like($ipod->error(), '/Timeout/', "Timeout reading response");
};
# Empty the buffer
$ipod->emptyrecv();
# ContextButtonStatus(0x00)
# We expect a timeout as response
$m = $ipod->sendmsg(0x02, 0x00, "\x00");
ok($m == 1, "ContextButtonStatus(0x00)");
($l, $c, $p) = $ipod->recvmsg();
subtest "Timeout" => sub {
ok(!defined($l), "Response received");
like($ipod->error(), '/Timeout/', "Timeout reading response");
};
# Empty the buffer
$ipod->emptyrecv();
# IdentifyDeviceLingoes(lingos=0x05, options=0x00, deviceid=0x00)
# We expect an ACK OK message as response
$m = $ipod->sendmsg(0x00, 0x13, "\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00");
ok($m == 1, "IdentifyDeviceLingoes(lingos=0x05, options=0x00, deviceid=0x00) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK OK" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x02, "Response command");
is($p, "\x00\x13", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# RequestLingoProtocolVersion(lingo=0x02)
# We expect a ReturnLingoProtocolVersion packet
$m = $ipod->sendmsg(0x00, 0x0F, "\x02");
ok($m == 1, "RequestLingoProtocolVersion(lingo=0x02) sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ReturnLingoProtocolVersion" => sub {
ok(defined($l), "Response received");
is($l, 0x00, "Response lingo");
is($c, 0x10, "Response command");
like($p, "/^\\x02..\$/", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# Send an undefined command
# We expect an ACK Bad Parameter as response
$m = $ipod->sendmsg(0x02, 0xFF);
ok($m == 1, "Undefined command sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Bad Parameter" => sub {
ok(defined($l), "Response received");
is($l, 0x02, "Response lingo");
is($c, 0x01, "Response command");
is($p, "\x04\xFF", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# ContextButtonStatus(0x00)
# We expect a timeout as response
$m = $ipod->sendmsg(0x02, 0x00, "\x00");
ok($m == 1, "ContextButtonStatus(0x00)");
($l, $c, $p) = $ipod->recvmsg();
subtest "Timeout" => sub {
ok(!defined($l), "Response received");
like($ipod->error(), '/Timeout/', "Timeout reading response");
};
# Empty the buffer
$ipod->emptyrecv();
# Send a too short command
# We expect an ACK Bad Parameter as response
$m = $ipod->sendmsg(0x02, 0x00, "");
ok($m == 1, "Short command sent");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Bad Parameter" => sub {
ok(defined($l), "Response received");
is($l, 0x02, "Response lingo");
is($c, 0x01, "Response command");
is($p, "\x04\x00", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# ImageButtonStatus(0x00)
# We expect an ACK Not Authenticated as response
$m = $ipod->sendmsg(0x02, 0x02, "\x00");
ok($m == 1, "ImageButtonStatus(0x00)");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Not Authenticated" => sub {
ok(defined($l), "Response received");
is($l, 0x02, "Response lingo");
is($c, 0x01, "Response command");
is($p, "\x07\x02", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# VideoButtonStatus(0x00)
# We expect an ACK Not Authenticated as response
$m = $ipod->sendmsg(0x02, 0x03, "\x00");
ok($m == 1, "VideoButtonStatus(0x00)");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Not Authenticated" => sub {
ok(defined($l), "Response received");
is($l, 0x02, "Response lingo");
is($c, 0x01, "Response command");
is($p, "\x07\x03", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();
# AudioButtonStatus(0x00)
# We expect an ACK Not Authenticated as response
$m = $ipod->sendmsg(0x02, 0x04, "\x00");
ok($m == 1, "AudioButtonStatus(0x00)");
($l, $c, $p) = $ipod->recvmsg();
subtest "ACK Not Authenticated" => sub {
ok(defined($l), "Response received");
is($l, 0x02, "Response lingo");
is($c, 0x01, "Response command");
is($p, "\x07\x04", "Response payload");
};
# Empty the buffer
$ipod->emptyrecv();