Topic: Problems writing to Skylander RFID portal
For fun I wanted to to play around with the skylander rfid portal. This is an USB Hid device and the "protocol" is quite simple or so it appears from SKyDumper project (h**ps://github.com/capull0/SkyDumper). This tool SkyDumper works on my linux computer, but when I try to convert it to hidsharp it won't work.
To be more specific.
* I can detect the portal just fine and get the device.
* I can open a HidStream
* I then try to send the "restart" message. Basically report Zero and the letter R, the rest all zeros.
* As soon as this is send out the portal starts sending status messages which I receive continuously. So the message did wake up something.
* However the write errors out with an exception. On my linux laptop its and I/O exception hardcoded in the output source code of hidsharp and on windows its a timeout exception.
I'm no usb export and have little clue why this happens.
I converted portalIO.cpp to the C# code below. I must be missing something silly I think. No reason for it to work using the original cpp code and not thru HidSharp. The first write failure occurs on the call to restartportal. After that I am buried in status output from the portal but nothing else. Any further write messages give the same result
If anyone has a clue or suggestion on what to try next? That would be appreciated.
using System;
using System.Text;
using HidSharp;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using HidSharp.Reports;
using HidSharp.Reports.Input;
namespace SkyReaderLib
{
public class PortalIO
{
public const int rw_buffer_size = 0x21;
public const int TIMEOUT = 30000;
public const int SKYLANDER_SIZE = 1024;
public const int MAX_STR = 255;
private HidDevice portal;
private HidStream portalStream;
private bool initialized = false;
public event EventHandler<StatusChangedEventArgs> StatusEvent;
public static IEnumerable<HidDevice> ListPortals()
{
IEnumerable<HidDevice> devices = DeviceList.Local.GetHidDevices()
.Where(x => (x.VendorID == 0x12ba || x.VendorID == 0x54c || x.VendorID == 0x1430)
&& (x.ProductID == 0x150 || x.ProductID == 0x967));
#if DEBUG
if (devices.Any())
{
Console.WriteLine("Found portal usb devices:");
foreach (HidDevice device in devices)
{
Console.WriteLine($"\t{device.GetFriendlyName()}");
}
}
#endif
return devices;
}
// Send a command to the portal
private void Write(byte[] message)
{
try
{
byte[] buffer = new byte[portal.GetMaxOutputReportLength()];
message.CopyTo(buffer, 0);
#if DEBUG
Console.WriteLine("Issued report out:");
Console.WriteLine(string.Join(" ", buffer));
#endif
portalStream.Write(buffer);
}
catch (Exception e)
{
Console.WriteLine($"Exception while issuing a report occured: {e}");
}
}
private bool CheckResponse(out RWBlock response, byte expect)
{
response = new RWBlock();
#if DEBUG
Console.WriteLine(">>> CheckResponse\n");
#endif
int result = portalStream.Read(response.buffer, 0, rw_buffer_size);
if (result < 0)
throw new Exception("Could not read response");
#if DEBUG
Console.WriteLine("CheckResponse read = {0} bytes\n", result);
#endif
response.dwBytesTransferred = result;
// found wireless USB but portal is not connected
if (response.buffer[0] == 'Z')
throw new Exception("Found portal dongle, but portal not connected");
// Status says no skylander on portal
// if (response->buffer[0] == 'Q' && response->buffer[1] == 0)
// throw 11;
#if DEBUG
Console.WriteLine("<<< CheckResponse\n");
#endif
return (response.buffer[0] != expect);
}
//
public void PortalStatus()
{
byte[] buffer = new byte[portal.GetMaxOutputReportLength()];
buffer[0] = 0;
buffer[1] = (byte)'S';
Write(buffer);
}
// Start portal
public void RestartPortal()
{
byte[] buffer = new byte[portal.GetMaxOutputReportLength()];
buffer[0] = 0;
buffer[1] = (byte)'R';
Write(buffer);
}
// Antenna up / activate
public void ActivatePortal(int active)
{
byte[] buffer = new byte[portal.GetMaxOutputReportLength()];
buffer[0] = 0;
buffer[1] = (byte)'A';
buffer[2] = (byte)active;
Write(buffer);
}
// Set the portal color
public void SetPortalColor(byte r, byte g, byte b)
{
byte[] buffer = new byte[portal.GetMaxOutputReportLength()];
buffer[0] = 0;
buffer[1] = (byte)'C';
buffer[2] = r; // R
buffer[3] = g; // G
buffer[4] = b; // B
Write(buffer);
}
// Release hPortalInstance
~PortalIO()
{
ActivatePortal(0);
portalStream.Close();
}
public void Flash()
{
for (; ; )
{
ActivatePortal(1);
ActivatePortal(0);
}
}
public PortalIO(HidDevice portal)
{
Console.WriteLine("Connecting to portal.\n");
this.portal = portal;
initialized = OpenPortal(portal);
if (initialized)
{
RestartPortal();
ActivatePortal(1);
SetPortalColor(0xC8, 0xC8, 0xC8);
}
else
{
Console.WriteLine("Error initializing portal.");
}
}
private bool OpenPortal(HidDevice portal)
{
Console.WriteLine(portal.GetMaxInputReportLength());
ReportDescriptor descriptor = portal.GetReportDescriptor();
HidDeviceInputReceiver reciever = descriptor.CreateHidDeviceInputReceiver();
bool opened = portal.TryOpen(out portalStream);
var x = portal.GetReportDescriptor();
reciever.Start(portalStream);
reciever.Received += OnReceiverReceived;
portalStream.Closed += PortalStreamOnClosed;
return opened;
}
private void PortalStreamOnClosed(object? sender, EventArgs e)
{
Console.WriteLine("Closed!");
}
private void OnReceiverReceived(object sender, EventArgs args)
{
try
{
var receiver = (HidDeviceInputReceiver)sender;
if (receiver.Stream?.Device == null)
{
return;
}
var length = receiver.Stream.Device.GetMaxInputReportLength();
var buffer = new byte[length];
if (receiver.TryRead(buffer, 0, out Report report))
{
// Console.WriteLine($"Received report with id: {report.ReportID}, type: {report.ReportType}");
report.Read(buffer, 0, (bytes, offset, item, dataItem) =>
{
// Console.WriteLine(string.Join(" ", bytes));
});
switch((char)buffer[1])
{
case 'S':
StatusEvent?.Invoke(this, new StatusChangedEventArgs(buffer));
break;
default:
break;
}
}
else
{
Console.WriteLine("Failed to read report.");
}
}
catch (Exception e)
{
Console.WriteLine($"Exception while reading report occured: {e}");
}
}
}
}