using System; using System.Collections.Generic; using System.Text; using LibUsbDotNet; using LibUsbDotNet.Main; namespace NavNet { public class NspireService { #region Declarations private UsbRegistry _reg; private UsbDevice _device; private UsbEndpointReader _reader; private UsbEndpointWriter _writer; private Queue _queue; private List _incompletePacket; private List _incompletePacketData; private List _responses; private byte _nextSequenceId = 0; private ushort _sourceAddress = 0x6400; private ushort _destinationAddress = 0x6401; #endregion #region Events //private event EventHandler RawPacketReceived; #endregion #region Constructors / Teardown public NspireService(UsbRegistry reg) { _reg = reg; _queue = new Queue(); _incompletePacket = new List(); _incompletePacketData = new List(); _responses = new List(); } #endregion #region Public Methods public void Connect() { if (_reg != null) { if (_device == null || !_device.IsOpen) { _device = _reg.Device; IUsbDevice dev = _device as IUsbDevice; if (!ReferenceEquals(dev, null)) { //Select configuration 1 dev.SetConfiguration(1); //Claim interface 0 dev.ClaimInterface(0); _reader = dev.OpenEndpointReader(ReadEndpointID.Ep01); _writer = dev.OpenEndpointWriter(WriteEndpointID.Ep01); _reader.DataReceived += _reader_DataReceived; _reader.DataReceivedEnabled = true; } } } } public void Disconnect() { if (_device != null) { if (_device.IsOpen) { IUsbDevice dev = _device as IUsbDevice; if (!ReferenceEquals(dev, null)) { //Release interface 0 dev.ReleaseInterface(0); } _device.Close(); } _device = null; } } public static List GetAttachedDevices(int[] vendorIds) { List ret = new List(); foreach (UsbRegistry reg in UsbDevice.AllDevices) { bool found = false; if (vendorIds.Length > 0) { foreach (int id in vendorIds) { if (reg.Vid == id) { found = true; break; } } } else { found = true; } if (found) ret.Add(new NspireService(reg)); } return ret; } #endregion #region Event Handlers private void _reader_DataReceived(object sender, EndpointDataEventArgs e) { //Receive and queue up the data if (e != null) { for (int i = 0; i < e.Count; i++) { lock (_queue) { _queue.Enqueue(e.Buffer[i]); } } } lock (_incompletePacket) { lock (_incompletePacketData) { _TryConstructPacket(); } } } #endregion #region Local Methods private long _OSSize; private bool _receivedFirstPacket; private bool _HandlePacket(RawPacket packet) { bool handled = false; //Deal with packets where ACK byte takes priority if (packet.ACK == 0x0A) handled = true; //Deal with packets where source service ID takes priority if (!handled) { switch (packet.SourceServiceId) { case RawPacket.ServiceId.ServiceDisconnection: { //Acknowledge the request _SendData(new RawPacket(_sourceAddress, (RawPacket.ServiceId)(packet.Sequence.Value == 0 ? 0x00FE : 0x00FF), _destinationAddress, (RawPacket.ServiceId)((packet.Data[0] << 8) | packet.Data[1]), 0x0A, new byte[] { (byte)(((ushort)packet.DestinationServiceId >> 8) & 0xFF), (byte)((ushort)packet.DestinationServiceId & 0xFF) }), packet.Sequence.Value); handled = true; break; } default: break; } } //Deal with packets where destination service ID takes priority if (!handled) { switch (packet.DestinationServiceId) { case RawPacket.ServiceId.InstallOS: { //Acknowledge the request _SendData(new RawPacket(_sourceAddress, (RawPacket.ServiceId)(packet.Sequence.Value == 0 ? 0x00FE : 0x00FF), _destinationAddress, packet.SourceServiceId, 0x0A, new byte[] { (byte)(((ushort)packet.DestinationServiceId >> 8) & 0xFF), (byte)((ushort)packet.DestinationServiceId & 0xFF) }), packet.Sequence.Value); //Determine what request this is byte requestType; if (packet.Data != null && packet.Data.Length > 1) requestType = packet.Data[0]; else throw new InvalidOperationException("InstallOS: Invalid request type"); switch (requestType) { case 0x03: { //This is the start of the OS install, fetch the size bytes if (packet.Data == null || packet.Data.Length < 5) throw new InvalidOperationException("InstallOS: Invalid initial request size"); _OSSize = (((long)packet.Data[1] << 24) & 0xFF000000) | (((long)packet.Data[2] << 16) & 0x00FF0000) | (((long)packet.Data[3] << 8) & 0x0000FFFF) | ((long)packet.Data[4] & 0xFF); _receivedFirstPacket = false; //Flush the temp file if (System.IO.File.Exists("C:\\temp.tnc")) System.IO.File.Delete("C:\\temp.tnc"); //Let the other device know we're ready to start receiving the OS data _SendData(new RawPacket(_sourceAddress, RawPacket.ServiceId.InstallOS, _destinationAddress, packet.SourceServiceId, 0x00, new byte[] { 0x04 }), ++_nextSequenceId); handled = true; break; } case 0x05: { //This is OS data, receive and write it var file = new System.IO.FileStream("C:\\temp.tnc", System.IO.FileMode.Append); file.Write(packet.Data, 1, packet.Data.Length - 1); file.Close(); _OSSize -= (packet.Data.Length - 1); if (!_receivedFirstPacket) { _SendData(new RawPacket(_sourceAddress, RawPacket.ServiceId.InstallOS, _destinationAddress, packet.SourceServiceId, 0x00, new byte[] { 0xFF, 0x00 }), ++_nextSequenceId); _receivedFirstPacket = true; } if (_OSSize <= 0 || packet.Data.Length < 0xFE) { //We're done receiving the OS, so let the other calculator know we're done (100%) _SendData(new RawPacket(_sourceAddress, RawPacket.ServiceId.InstallOS, _destinationAddress, RawPacket.ServiceId.InstallOS, 0x00, new byte[] {0x06, 0x64}), ++_nextSequenceId); } handled = true; break; } default: break; } handled = true; break; } case RawPacket.ServiceId.DeviceAddressAssignment: { _SendData(new RawPacket(_sourceAddress, RawPacket.ServiceId.DeviceAddressAssignment, _destinationAddress, RawPacket.ServiceId.DeviceAddressAssignment, 0x00, new byte[] { (byte)((_destinationAddress >> 8) & 0xFF), (byte)(_destinationAddress & 0xFF), 0xFF, 0x00 }), ++_nextSequenceId); handled = true; break; } case RawPacket.ServiceId.Login: { _SendData(new RawPacket(_sourceAddress, RawPacket.ServiceId.NACK, _destinationAddress, packet.SourceServiceId, 0x0A, new byte[] { ((ushort)RawPacket.ServiceId.Login >> 8) & 0xFF, ((ushort)RawPacket.ServiceId.Login & 0xFF) }), packet.Sequence.Value); handled = true; break; } case RawPacket.ServiceId.DeviceInformation: { //Acknowledge the request _SendData(new RawPacket(_sourceAddress, (RawPacket.ServiceId)(packet.Sequence.Value == 0 ? 0x00FE : 0x00FF), _destinationAddress, packet.SourceServiceId, 0x0A, new byte[] { (byte)(((ushort)packet.DestinationServiceId >> 8) & 0xFF), (byte)((ushort)packet.DestinationServiceId & 0xFF) }), packet.Sequence.Value); //Determine the type of request this is byte requestType; if (packet.Data != null && packet.Data.Length >= 1) requestType = packet.Data[0]; else throw new InvalidOperationException("DeviceInformation request: invalid packet type"); byte[] data = null; switch (requestType) { case 0x01: //return general device information { data = new byte[0x6F] {0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x62, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xA7, 0x9F, 0x74, 0x00, 0x00, 0x00, 0x00, 0x03, 0x39, 0x61, 0xC0, 0xFF, 0x01, 0x00, 0x84, 0x03, 0x02, 0x07, 0x01, 0x03, 0x00, 0x00, 0x63, 0x03, 0x02, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0xF0, 0x10, 0x01, 0x1F, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x35, 0x32, 0x31, 0x46, 0x33, 0x43, 0x30, 0x00, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x35, 0x32, 0x31, 0x46, 0x33, 0x43, 0x30, 0x36, 0x34, 0x34, 0x45, 0x30, 0x33, 0x30, 0x43, 0x00}; _SendData(new RawPacket(_sourceAddress, RawPacket.ServiceId.DeviceInformation, _destinationAddress, packet.SourceServiceId, 0x00, data), ++_nextSequenceId); break; } case 0x02: //return device name { break; } case 0x03: //return list of supported file extensions { break; } default: break; } handled = true; break; } default: break; } } return handled; } private RawPacket _WaitForResponse() { RawPacket response = null; DateTime start = DateTime.Now; //Wait indefinitely (for now) while (true) { lock (_responses) { foreach (var r in _responses) { _responses.Remove(r); response = r; } } if (response != null) break; //if ((DateTime.Now - start).Seconds >= 3) // throw new TimeoutException("Timed out waiting for calculator respose"); } return response; } private int _SendData(RawPacket packet, byte sequenceId) { //Send it! int bytesWritten; byte[] data = packet.GetRawData(sequenceId); _writer.Write(data, 0, data.Length, 5000, out bytesWritten); /*Console.WriteLine(); Console.Write("Sending: "); foreach (byte b in data) Console.Write(b.ToString("X2") + " "); Console.WriteLine("Written: " + bytesWritten.ToString()); Console.WriteLine();*/ return bytesWritten; } private void _TryConstructPacket() { const int PACKET_HEADER_SIZE = 16; //Get the raw packet header while (_incompletePacket.Count < PACKET_HEADER_SIZE) { int count; lock (_queue) { count = _queue.Count; } if (count == 0) break; lock (_queue) { _incompletePacket.Add(_queue.Dequeue()); } } if (_incompletePacket.Count == PACKET_HEADER_SIZE) { //We have a valid packet header //Get the data int size = _incompletePacket[12]; while (_incompletePacketData.Count < size) { int count; lock (_queue) { count = _queue.Count; } if (count == 0) break; lock (_queue) { _incompletePacketData.Add(_queue.Dequeue()); } } if (_incompletePacketData.Count >= size) { //We have a whole packet! var packet = new RawPacket(_incompletePacket.ToArray(), _incompletePacketData.ToArray()); lock (_responses) { _responses.Add(packet); } //Now reset everything _incompletePacket.Clear(); _incompletePacketData.Clear(); _HandlePacket(packet); } } } #endregion } }