using System; using System.Collections.Generic; using System.Text; using LibUsbDotNet; using LibUsbDotNet.Main; namespace DirectUSB { public class TIDevice { #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; #endregion #region Constructors / Teardown public TIDevice(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.Ep02); _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 TIDevice(reg)); } return ret; } public int SendNegotiateBufferSize(int requestedSize) { //Prepare packet var packet = new RawPackets.RequestBufferSizePacket(requestedSize); //Send it _SendData(packet); //Wait for response var response = _WaitForResponse(0x02); return new RawPackets.RequestBufferSizeResponse(response).ActualBufferSize; } public void SendPingSetMode(VirtualPackets.PingSetModePacket.CalculatorMode mode, uint value) { //Prepare packet var packet = new VirtualPackets.PingSetModePacket(mode, value); //Send it _SendPacket(packet); //Wait for response var response = _WaitForResponse(0x04); //Send acknowledgement _SendData(new RawPackets.AcknowledgementPacket()); } public List SendParameterRequest(List parameters) { //Prepare packet var packet = new VirtualPackets.ParameterRequestPacket(parameters); //Send it _SendPacket(packet); //Wait for dummy response var response = _WaitForResponse(0x04); //Send acknowledgement of that _SendData(new RawPackets.AcknowledgementPacket()); //Get parameter request response var r = _WaitForPacket(); return new VirtualPackets.ParameterRequestResponse(r.Data).Parameters; } public void SendSetClockRequest(DateTime time) { var data = new byte[4]; int seconds = (int)(time - new DateTime(1997, 1, 1)).TotalSeconds; data[0] = (byte)((seconds >> 24) & 0xFF); data[1] = (byte)((seconds >> 16) & 0xFF); data[2] = (byte)((seconds >> 8) & 0xFF); data[3] = (byte)(seconds & 0xFF); SendSetParameterRequest(DirectUSB.Parameters.Parameter.ParameterType.Time, data); } public void SendSetParameterRequest(Parameters.Parameter.ParameterType type, byte[] data) { //Prepare packet var packet = new VirtualPackets.SetParameterPacket(type, data); //Send it _SendPacket(packet); //Wait for dummy response var response = _WaitForPacket(); } public List SendDirectoryListing(List attributes) { //Prepare packet var packet = new VirtualPackets.DirectoryListingPacket(attributes); //Send it _SendPacket(packet); //Wait for response(s) var ret = new List(); VirtualPackets.VirtualPacket response = null; while (true) { response = _WaitForPacket(); if (response.Type == 0xDD00) break; var info = new VirtualPackets.VariableHeaderPacket(response.Data); ret.Add(new VariableInformation(info.VariableName, info.Attributes)); } return ret; } public Variable SendVariableRequest(string variableName, List requestedAttributes, List specifiedAttributes) { //Prepare packet var packet = new VirtualPackets.VariableRequestPacket(variableName, requestedAttributes, specifiedAttributes); //Send it _SendPacket(packet); //Wait for response(s) VirtualPackets.VirtualPacket response = null; response = _WaitForPacket(); var info = new VirtualPackets.VariableHeaderPacket(response.Data); var data = new VirtualPackets.VirtualPacket(0x000D, _WaitForPacket().Data); return new Variable(info.VariableName, info.Attributes, data.Data); } public void SendVariableDeleteRequest(string variableName, List specifiedAttributes) { //Prepare packet var packet = new VirtualPackets.VariableDeleteRequestPacket(variableName, specifiedAttributes); //Send it _SendPacket(packet); //Wait for response(s) VirtualPackets.VirtualPacket response = null; do { response = _WaitForPacket(); } while (response.Type == 0xBB00); } public void RemotelyExecuteProgram(string variableName) { //Prepare packet var packet = new VirtualPackets.RemoteExecutePacket(VirtualPackets.RemoteExecutePacket.ExecutionType.Program, variableName); //Send it _SendPacket(packet); //Wait for response(s) VirtualPackets.VirtualPacket response = null; do { response = _WaitForPacket(); } while (response.Type == 0xBB00); } public void RemotelyExecuteFlashApplication(string variableName) { //Prepare packet var packet = new VirtualPackets.RemoteExecutePacket( VirtualPackets.RemoteExecutePacket.ExecutionType.FlashApplication, variableName); //Send it _SendPacket(packet); //Wait for response(s) VirtualPackets.VirtualPacket response = null; do { response = _WaitForPacket(); } while (response.Type == 0xBB00); } public void SendRemoteKeypress(byte keyCode, byte keyExtend) { //Prepare packet var packet = new VirtualPackets.RemoteExecutePacket(keyCode, keyExtend); //Send it _SendPacket(packet); //Wait for response(s) VirtualPackets.VirtualPacket response = null; do { response = _WaitForPacket(); } while (response.Type == 0xBB00); } #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 VirtualPackets.VirtualPacket _WaitForPacket() { return _WaitForPacket(null); } private VirtualPackets.VirtualPacket _WaitForPacket(ushort? expectedPacketType) { RawPackets.RawPacket response; var bytes = new List(); int totalLength; ushort virtualType; //Receive the first raw packet response = _WaitForResponse(null); //Get the virtual type ID out of this virtualType = (ushort)((ushort)(response.Data[4] << 8) | (ushort)response.Data[5]); totalLength = response.Data.Length - 6; var firstData = new byte[totalLength]; for (int i = 0; i < totalLength; i++) firstData[i] = response.Data[i + 6]; bytes.Add(firstData); //Send acknowledgement _SendData(new RawPackets.AcknowledgementPacket()); //Keep receiving continuation packets while (response.PacketType == 0x03) { response = _WaitForResponse(null); totalLength += response.Data.Length; bytes.Add(response.Data); //Send acknowledgement _SendData(new RawPackets.AcknowledgementPacket()); } if (response.PacketType != 0x04) { //Receive last packet response = _WaitForResponse(0x04); totalLength += response.Data.Length; bytes.Add(response.Data); //Send acknowledgement for it _SendData(new RawPackets.AcknowledgementPacket()); } //Build one giant array from all the raw packets (ick) var data = new byte[totalLength]; int j = 0; foreach (var array in bytes) { for (int i = 0; i < array.Length; i++) { data[j] = array[i]; j++; } } var ret = new VirtualPackets.VirtualPacket(virtualType, data); if (expectedPacketType.HasValue && ret.Type != expectedPacketType) { //Uh-oh... throw new DirectUSBException( String.Format("Unexpected response: received '{0}', expected '{1}'", ret.Type.ToString("X4"), expectedPacketType.Value.ToString("X4"))); } return ret; } private RawPackets.RawPacket _WaitForResponse(byte? packetType) { RawPackets.RawPacket response = null; DateTime start = DateTime.Now; //Wait indefinitely (for now) while (true) { lock (_responses) { foreach (var r in _responses) { if (!packetType.HasValue || r.PacketType == packetType.Value) { _responses.Remove(r); response = r; break; } } } if (response != null) break; //if ((DateTime.Now - start).Seconds >= 3) // throw new TimeoutException("Timed out waiting for calculator respose"); } return response; } private int _SendPacket(VirtualPackets.VirtualPacket packet) { //Send all the raw packets foreach (var raw in packet.RawPackets(0x00FA)) { //Send raw packet _SendData(raw); //Get acknowledgement var ack = _WaitForResponse(null); } return 0; } private int _SendData(RawPackets.RawPacket packet) { var data = new byte[5+packet.Size]; //Fill in the size and type data[0] = (byte)(packet.Size << 24); data[1] = (byte)(packet.Size << 16); data[2] = (byte)(packet.Size << 8); data[3] = (byte)(packet.Size << 0); data[4] = packet.PacketType; //Fill in the data for (int i = 0; i < packet.Size; i++) data[i+5] = packet.Data[i]; //Send it! int bytesWritten; _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() { //Get the raw packet header while (_incompletePacket.Count < 5) { int count; lock (_queue) { count = _queue.Count; } if (count <= 0) break; lock (_queue) { _incompletePacket.Add(_queue.Dequeue()); } } if (_incompletePacket.Count == 5) { //We have a valid raw packet header //Get the data int size = (_incompletePacket[0] << 24) | (_incompletePacket[1] << 16) | (_incompletePacket[2] << 8) | _incompletePacket[3]; 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 RawPackets.RawPacket(_incompletePacket[4], _incompletePacketData.ToArray()); lock (_responses) { _responses.Add(packet); } Console.Write("Response: Type: " + packet.PacketType + " Data: "); foreach (byte b in packet.Data) { Console.Write(b.ToString("X2") + " "); } Console.WriteLine(); //Now reset everything _incompletePacket.Clear(); _incompletePacketData.Clear(); } } } #endregion #region Overrides public override string ToString() { string ret = "Unknown Device"; if (_reg.Pid == 0xE008) { ret = "TI-84 Plus Silver Edition"; } else if (_reg.Pid == 0xE003) { ret = "TI-84 Plus"; } return ret; } #endregion } }