Advertisement
Guest User

Arduino based DDR4 SPD reader/writer (Updated 24.05.2020)

a guest
May 24th, 2020
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 25.30 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO.Ports;
  4. using System.Threading;
  5. using System.Diagnostics;
  6. using System.IO;
  7.  
  8. namespace SpdReaderWriter {
  9.     /// <summary>
  10.     /// Defines Device class, properties, and methods to handle the communication with the device
  11.     /// </summary>
  12.     internal class Device {
  13.         /// <summary>
  14.         /// Defines device's state
  15.         /// </summary>
  16.         public bool IsConnected {
  17.             get => this._isConnected;
  18.             set => this._isConnected = value;
  19.         }
  20.  
  21.         private bool _isConnected = false;
  22.  
  23.         public string PortName;
  24.         public int EepromAddress;
  25.         public int SpdSize;
  26.  
  27.         public object PortLock = _PortLock;
  28.         private static readonly object _PortLock = new object();
  29.  
  30.         public int BytesToRead {
  31.             get => (int)this.Sp.BytesToRead;
  32.         }
  33.  
  34.         /// <summary>
  35.         /// Serial port default connection settings
  36.         /// </summary>
  37.         private readonly SerialPort Sp = new SerialPort {
  38.             BaudRate = 115200,
  39.             NewLine = "\r",
  40.             ReadTimeout = 1000,
  41.             WriteTimeout = 1000,
  42.             ReadBufferSize = 32,
  43.             WriteBufferSize = 32,
  44.             //Parity = Parity.None,
  45.         };
  46.  
  47.         /// <summary>
  48.         /// Initializes the SPD reader/writer device
  49.         /// </summary>
  50.         /// <param name="PortName">Serial port name</param>
  51.         public Device(string PortName) {
  52.             this.PortName = PortName;
  53.             this.Connect();
  54.         }
  55.  
  56.         /// <summary>
  57.         /// Initializes the SPD reader/writer device
  58.         /// </summary>
  59.         /// <param name = "PortName" >Serial port name</param>
  60.         /// <param name="eepromAddress">EEPROM address on the i2c bus</param>
  61.         public Device(string PortName, int eepromAddress = 0x50) {
  62.             this.PortName = PortName;
  63.             this.EepromAddress = eepromAddress;
  64.             this.Connect();
  65.         }
  66.  
  67.         /// <summary>
  68.         /// Initializes the SPD reader/writer device
  69.         /// </summary>
  70.         /// <param name="PortName">Serial port name</param>
  71.         /// <param name="eepromAddress">EEPROM address on the device's i2c bus</param>
  72.         /// <param name="SpdSize">Total EEPROM size</param>
  73.         public Device(string PortName, int eepromAddress = 0x50, int SpdSize = Eeprom.DDR4_SPD_SIZE) {
  74.             this.PortName = PortName;
  75.             this.EepromAddress = eepromAddress;
  76.             this.SpdSize = SpdSize;
  77.             this.Connect();
  78.         }
  79.  
  80.         /// <summary>
  81.         /// Attempts to establish a connection with the SPD reader/writer device
  82.         /// </summary>
  83.         /// <returns><see langword="true" /> if the connection is established</returns>
  84.         public bool Connect() {
  85.             return Connect(this);
  86.         }
  87.  
  88.         /// <summary>
  89.         /// Disconnects the SPD reader/writer device
  90.         /// </summary>
  91.         /// <returns></returns>
  92.         public bool Disconnect() {
  93.             return Disconnect(this);
  94.         }
  95.  
  96.         /// <summary>
  97.         /// Tests if the device responds to a test command
  98.         /// </summary>
  99.         /// <returns><see langword="true" /> if the device responds properly</returns>
  100.         public bool Test() {
  101.             return Test(this);
  102.         }
  103.  
  104.         /// <summary>
  105.         /// Reads a byte from the device
  106.         /// </summary>
  107.         /// <returns>A single byte value received from the device</returns>
  108.         public int ReadByte() {
  109.             return this.Sp.ReadByte();
  110.         }
  111.  
  112.         /// <summary>
  113.         /// Scans the device for I2C bus devices
  114.         /// </summary>
  115.         /// <param name="startAddress">First address</param>
  116.         /// <param name="endAddress">Last address</param>
  117.         /// <returns>An array of addresses on the device's I2C bus</returns>
  118.         public int[] Scan(int startAddress = 0x50, int endAddress = 0x57) {
  119.             return Scan(this, startAddress, endAddress);
  120.         }
  121.  
  122.         /// <summary>
  123.         /// Probes specified EEPROM address
  124.         /// </summary>
  125.         /// <returns><see langword="true" /> if EEPROM is detected at the specified address</returns>
  126.         public bool Probe() {
  127.             if (this.EepromAddress != 0) {
  128.                 return Probe(this, this.EepromAddress);
  129.             }
  130.             return false;
  131.         }
  132.  
  133.         /// <summary>
  134.         /// Probes specified EEPROM address
  135.         /// </summary>
  136.         /// <param name="address">EEPROM address</param>
  137.         /// <returns><see langword="true" /> if EEPROM is detected at the specified address</returns>
  138.         public bool Probe(int address) {
  139.             return Probe(this, address);
  140.         }
  141.  
  142.         /// <summary>
  143.         /// Clears serial port buffers from unneeded data to prevent unwanted behavior and delays
  144.         /// </summary>
  145.         /// <returns><see langword="true" /> when the buffer is empty</returns>
  146.         public bool ClearBuffer() {
  147.             return ClearBuffer(this);
  148.         }
  149.  
  150.         /// <summary>
  151.         /// Executes commands on the device.
  152.         /// </summary>
  153.         /// <param name="Command">Space separated commands to be executed on the device</param>
  154.  
  155.         public void ExecuteCommand(string Command) {
  156.             this.ClearBuffer();
  157.  
  158.             foreach (string cmd in Command.Split(' ')) {
  159.                 this.Sp.WriteLine(cmd);
  160.             }
  161.         }
  162.  
  163.         /// <summary>
  164.         /// Gets a response from the device
  165.         /// </summary>
  166.         /// <returns>A byte array the device has sent</returns>
  167.         public byte[] GetResponse() {
  168.  
  169.             int retryCount = 0;
  170.             int retryLimit = 1000;
  171.  
  172.             Queue<byte> _response = new Queue<byte>();
  173.  
  174.             while (this.BytesToRead < 1) {
  175.                 retryCount++;
  176.                 if (retryCount == retryLimit) {
  177.                     break;
  178.                 }
  179.                 Wait();
  180.             }
  181.  
  182.             while (this.BytesToRead != 0) {
  183.                 _response.Enqueue((byte)this.ReadByte());
  184.             }
  185.  
  186.             this.ClearBuffer();
  187.  
  188.             return _response.ToArray();
  189.         }
  190.  
  191.         /// <summary>
  192.         /// Gets a single byte from the device's response
  193.         /// </summary>
  194.         /// <param name="offset">A byte offset to get from the device's response</param>
  195.         /// <returns></returns>
  196.         public byte GetResponse(int offset) {
  197.             return GetResponse()[offset];
  198.         }
  199.  
  200.         /// <summary>
  201.         /// Finds devices connected to computer by sending a test command to every serial port device detected
  202.         /// </summary>
  203.         /// <returns>An array of serial port names the device is connected to</returns>
  204.         public static string[] Find() {
  205.             Stack<string> _result = new Stack<string>();
  206.  
  207.             foreach (string _portName in SerialPort.GetPortNames()) {
  208.                 Device _device = new Device(_portName);
  209.                 if (_device.Test()) {
  210.                     _result.Push(_portName);
  211.                     _device.Disconnect();
  212.                 }
  213.             }
  214.             return _result.ToArray();
  215.         }
  216.  
  217.         /// <summary>
  218.         /// Attempts to establish a connection with the device
  219.         /// </summary>
  220.         /// <param name="device">Device</param>
  221.         /// <returns><see langword="true" /> if the connection is established</returns>
  222.         private static bool Connect(Device device) {
  223.             if (!device.IsConnected) {
  224.                 device.Sp.PortName = device.PortName;
  225.                 try {
  226.                     device.Sp.Open();
  227.                     device.IsConnected = true;
  228.  
  229.                     if (device.Test()) {
  230.                         return device.IsConnected;
  231.                     }
  232.                 }
  233.                 catch (Exception) {
  234.                     return false;
  235.                 }
  236.             }
  237.             return device.IsConnected;
  238.         }
  239.  
  240.         /// <summary>
  241.         /// Disconnect from the device
  242.         /// </summary>
  243.         /// <param name="device">Device instance</param>
  244.         /// <returns><see langword="true" /> once the device is disconnected</returns>
  245.         private static bool Disconnect(Device device) {
  246.             if (device.Sp.IsOpen) {
  247.                 device.Sp.Close();
  248.                 device.IsConnected = false;
  249.             }
  250.  
  251.             if (!device.IsConnected) {
  252.                 return true;
  253.             }
  254.  
  255.             return false;
  256.         }
  257.  
  258.         /// <summary>
  259.         /// Tests if the device is able to communicate
  260.         /// </summary>
  261.         /// <param name="device">Device</param>
  262.         /// <returns><see langword="true" /> if the device responds to a test command</returns>
  263.         private static bool Test(Device device) {
  264.             lock (device.PortLock) {
  265.                 if (device.IsConnected) {
  266.  
  267.                     char welcomeString = '!';
  268.                     int retryCount = 0;
  269.                     int retryLimit = 1000; // If the device didn't respond after this many tries, it's probably dead
  270.  
  271.                     while (device.BytesToRead == 0) {
  272.                         device.ExecuteCommand("t"); // This is executed inside the loop, because it takes a few tries to get the device to respond when the device has just been plugged in
  273.                         if (retryCount > retryLimit) {
  274.                             return false;
  275.                         }
  276.                         Wait(10);
  277.                         retryCount++;
  278.                     }
  279.  
  280.                     try {
  281.                         if (device.GetResponse(0) == welcomeString) {
  282.                             return true;
  283.                         }
  284.                     }
  285.                     catch {
  286.                         return false;
  287.                     }
  288.                 }
  289.             }
  290.             return false;
  291.         }
  292.  
  293.         /// <summary>
  294.         /// Scans for EEPROM addresses on the device's I2C bus
  295.         /// </summary>
  296.         /// <param name="device">Device</param>
  297.         /// <param name="startAddress">First address (cannot be lower than 0x50)</param>
  298.         /// <param name="endAddress">Last address (cannot be higher than 0x57)</param>
  299.         /// <returns>An array of EEPROM addresses present on the device's I2C bus</returns>
  300.         private static int[] Scan(Device device, int startAddress = 0x50, int endAddress = 0x57) {
  301.  
  302.             Queue<int> addresses = new Queue<int>();
  303.  
  304.             lock (device.PortLock) {
  305.                 if (device.IsConnected) {
  306.                     device.ExecuteCommand($"s {startAddress} {endAddress}");
  307.  
  308.                     byte[] response = device.GetResponse();
  309.  
  310.                     foreach (int location in response) {
  311.                         if (location != 0) {
  312.                             // An accessible EEPROM address was found
  313.                             addresses.Enqueue(location);
  314.                         }
  315.                     }
  316.                 }
  317.             }
  318.  
  319.             return addresses.ToArray();
  320.         }
  321.  
  322.         /// <summary>
  323.         /// Tests if the EEPROM is present on the device's I2C bus
  324.         /// </summary>
  325.         /// <param name="device">Device instance</param>
  326.         /// <param name="address">EEPROM address</param>
  327.         /// <returns><see langword="true" /> if the address is accessible</returns>
  328.         private static bool Probe(Device device, int address) {
  329.             lock (device.PortLock) {
  330.                 if (device.IsConnected) {
  331.                     device.ExecuteCommand($"p {address}");
  332.                     if (device.GetResponse(0) == address) {
  333.                         // Valid address confirmed
  334.                         return true;
  335.                     }
  336.                 }
  337.             }
  338.             return false;
  339.         }
  340.  
  341.         /// <summary>
  342.         /// Clears serial port buffers from unneeded data to prevent unwanted behavior and delays
  343.         /// </summary>
  344.         /// <param name="device">Device instance</param>
  345.         /// <returns><see langword="true" /> when the buffer is empty</returns>
  346.         private static bool ClearBuffer(Device device) {
  347.             while (device.Sp.BytesToRead > 0 || device.Sp.BytesToWrite > 0) {
  348.                 device.Sp.DiscardInBuffer();
  349.                 device.Sp.DiscardOutBuffer();
  350.                 Wait();
  351.             }
  352.             return true;
  353.         }
  354.  
  355.         /// <summary>
  356.         /// Delays execution
  357.         /// </summary>
  358.         private static void Wait(int timeout = 1) {
  359.             Thread.Sleep(timeout);
  360.         }
  361.     }
  362.  
  363.     /// <summary>
  364.     /// Defines EEPROM class, properties, and methods to handle all EEPROM operations
  365.     /// </summary>
  366.     internal class Eeprom {
  367.  
  368.         /// <summary>
  369.         /// Defines SPD sizes
  370.         /// </summary>
  371.         public const int DDR3_SPD_SIZE = 0x100; // 256 bytes
  372.         public const int DDR4_SPD_SIZE = 0x200; // 512 bytes
  373.  
  374.         /// <summary>
  375.         /// Reads a single byte from the EEPROM
  376.         /// </summary>
  377.         /// <param name="device">SPD reader/writer device instance</param>
  378.         /// <param name="offset">Byte offset</param>
  379.         /// <returns>Byte value at <paramref name="offset"/> </returns>
  380.         public static byte ReadByte(Device device, int offset) {
  381.  
  382.             lock (device.PortLock) {
  383.                 device.ExecuteCommand($"r {device.EepromAddress} {offset}");
  384.             }
  385.  
  386.             return device.GetResponse(0);
  387.         }
  388.  
  389.         /// <summary>
  390.         /// Reads bytes from the EEPROM
  391.         /// </summary>
  392.         /// <param name="device">SPD reader/writer device instance</param>
  393.         /// <param name="offset">Byte position to start reading from</param>
  394.         /// <param name="count">Total number of bytes to read from <paramref name="offset" /> </param>
  395.         /// <returns>A byte array containing byte values</returns>
  396.         public static byte[] ReadByte(Device device, int offset, int count = 1) {
  397.  
  398.             byte[] output = new byte[count];
  399.  
  400.             for (int i = 0; i < count; i++) {
  401.                 output[i] = ReadByte(device, i + offset);
  402.             }
  403.  
  404.             return output;
  405.         }
  406.  
  407.         /// <summary>
  408.         /// Write a byte to the EEPROM
  409.         /// </summary>
  410.         /// <param name="device">SPD reader/writer device instance</param>
  411.         /// <param name="offset">Byte position</param>
  412.         /// <param name="value">Byte value</param>
  413.         /// <returns><see langword="true" /> if <paramref name="value"/> is written at <paramref name="offset"/> </returns>
  414.         public static bool WriteByte(Device device, int offset, byte value) {
  415.  
  416.             lock (device.PortLock) {
  417.                 device.ExecuteCommand($"w {device.EepromAddress} {offset} {value}");
  418.  
  419.                 if (device.GetResponse(0) == 0) { // The device responds with 0 upon successful write
  420.                     return true;
  421.                 }
  422.  
  423.                 return false;
  424.             }
  425.         }
  426.  
  427.         /// <summary>
  428.         /// Write a byte to the EEPROM. The value is written only if differs from the one already saved at the same address.
  429.         /// </summary>
  430.         /// <param name="device">SPD reader/writer device instance</param>
  431.         /// <param name="offset">Byte position</param>
  432.         /// <param name="value">Byte value</param>
  433.         /// <returns><see langword="true" /> if byte read at <paramref name="offset"/> matches <paramref name="value"/> value</returns>
  434.         public static bool UpdateByte(Device device, int offset, byte value) {
  435.  
  436.             return VerifyByte(device, offset, value) || WriteByte(device, offset, value);
  437.         }
  438.  
  439.         /// <summary>
  440.         /// Verifies if the offset content matches the input specified
  441.         /// </summary>
  442.         /// <param name="device">SPD reader/writer device instance</param>
  443.         /// <param name="offset">Byte position</param>
  444.         /// <param name="value">Byte value</param>
  445.         /// <returns><see langword="true" /> if byte at <paramref name="offset"/> matches <paramref name="value"/> value</returns>
  446.         public static bool VerifyByte(Device device, int offset, byte value) {
  447.  
  448.             return ReadByte(device, offset) == value;
  449.         }
  450.  
  451.         /// <summary>
  452.         /// Enables software write protection on the specified EEPROM block
  453.         /// </summary>
  454.         /// <param name="device">SPD reader/writer device instance</param>
  455.         /// <param name="block">Block number to be write protected</param>
  456.         /// <returns><see langword="true" /> when the write protection has been enabled</returns>
  457.         public static bool SetWriteProtection(Device device, int block) {
  458.  
  459.             lock (device.PortLock) {
  460.  
  461.                 //block = (block > 3) ? 0 : block; // Let the device handle incorrect block numbers
  462.  
  463.                 device.ExecuteCommand($"e {block}"); // WP commands don't use address, all devices on the bus will respond simultaneously
  464.                 if (device.GetResponse(0) == 0) {
  465.                     return true;
  466.                 }
  467.             }
  468.  
  469.             return false;
  470.         }
  471.  
  472.         /// <summary>
  473.         /// Enables software write protection on all 4 EEPROM blocks
  474.         /// </summary>
  475.         /// <param name="device">Device instance</param>
  476.         /// <returns><see langword="true" /> when the write protection has been enabled on all blocks</returns>
  477.         public static bool SetWriteProtection(Device device) {
  478.  
  479.             for (int i = 0; i <= 3; i++) {
  480.                 if (!SetWriteProtection(device, i)) {
  481.                     return false;
  482.                 }
  483.             }
  484.  
  485.             return true;
  486.         }
  487.  
  488.         /// <summary>
  489.         /// Clears EEPROM write protection
  490.         /// </summary>
  491.         /// <param name="device">Device instance</param>
  492.         /// <returns><see langword="true" /> if the write protection has been disabled</returns>
  493.         public static bool ClearWriteProtection(Device device) {
  494.  
  495.             lock (device.PortLock) {
  496.  
  497.                 device.ExecuteCommand("c"); // WP commands don't use address, all devices on the bus will respond simultaneously
  498.                 if (device.GetResponse(0) == 0) {
  499.  
  500.                     return true;
  501.                 }
  502.             }
  503.  
  504.             return false;
  505.         }
  506.  
  507.         /// <summary>
  508.         /// Prints bytes in a grid pattern
  509.         /// </summary>
  510.         /// <param name="pos">Byte offset</param>
  511.         /// <param name="b">Byte value</param>
  512.         /// <param name="bpr">Bytes per row</param>
  513.         /// <param name="showOffset">Show or hide offset at the beginning of new line</param>
  514.         public static void DisplayByte(int pos, byte b, int bpr = 16, bool showOffset = true, bool color = true) {
  515.  
  516.             ConsoleColor _defaultForeColor = Console.ForegroundColor;   // Text color
  517.             ConsoleColor _defaultBackgroundColor = Console.BackgroundColor;
  518.  
  519.             byte[] hexRow = new byte[bpr];
  520.             string[] strRow = new string[bpr];
  521.  
  522.             // Top row (offsets)
  523.             if (pos == 0 && showOffset) {
  524.                 Console.Write("     "); // Indentation
  525.                 for (int i = 0; i < bpr; i++) {
  526.                     Console.Write($"{i:x2} ");
  527.                 }
  528.             }
  529.  
  530.             // Contents
  531.             if (pos % bpr == 0) {
  532.                 Console.Write(Environment.NewLine);
  533.                 if (showOffset) {
  534.                     Console.Write("{0:x3}: ", pos); // Row offsets
  535.                 }
  536.             }
  537.  
  538.             if (color) {
  539.                 Console.BackgroundColor = ConsoleColor.Black;
  540.                 Console.ForegroundColor = (ConsoleColor)((b >> 4) == 0 ? (int)ConsoleColor.DarkGray : (b >> 4)); // Set color (bitshift right by 4 bits to use first 8 darker shade colors, black is replaced with dark grey)
  541.             }
  542.             Console.Write($"{b:X2}"); // Bytes
  543.             if (pos % bpr != bpr - 1) {
  544.                 Console.Write(" ");
  545.             }
  546.             Console.ForegroundColor = _defaultForeColor; // Reset foreground (text) color
  547.             Console.BackgroundColor = _defaultBackgroundColor; // Reset background color
  548.         }
  549.  
  550.         /// <summary>
  551.         /// Calculates CRC16/XMODEM checksum
  552.         /// </summary>
  553.         /// <param name="input">A byte array to be checked</param>
  554.         /// <returns>A calculated checksum</returns>
  555.         public static ushort Crc16(byte[] input) {
  556.  
  557.             ushort[] table = new ushort[256];
  558.             ushort initialValue = 0;
  559.             ushort crc = initialValue;
  560.             for (int i = 0; i < table.Length; ++i) {
  561.                 ushort temp = 0;
  562.                 ushort a = (ushort)(i << 8);
  563.                 for (int j = 0; j < 8; ++j) {
  564.                     if (((temp ^ a) & 0x8000) != 0) {
  565.                         temp = (ushort)((temp << 1) ^ 0x1021);
  566.                     }
  567.                     else {
  568.                         temp <<= 1;
  569.                     }
  570.                     a <<= 1;
  571.                 }
  572.                 table[i] = temp;
  573.             }
  574.             for (int i = 0; i < input.Length; ++i) {
  575.                 crc = (ushort)((crc << 8) ^ table[((crc >> 8) ^ (0xff & input[i]))]);
  576.             }
  577.  
  578.             return crc;
  579.         }
  580.     }
  581.  
  582.  
  583.  
  584.     class Program {
  585.         static void Main(string[] args) {
  586.  
  587.             Welcome();
  588.  
  589.             if (args.Length > 0) {
  590. #if DEBUG
  591.                 // Display command line arguments in console title
  592.                 if (Debugger.IsAttached) {
  593.                     Console.Title = ($"{AppDomain.CurrentDomain.FriendlyName} ");
  594.                     foreach (string cmd in args) {
  595.                         Console.Title += ($"{cmd} ");
  596.                     }
  597.                 }
  598. #endif
  599.                 ParseCommand(args);
  600.             }
  601.             else {
  602.                 ShowHelp();
  603.             }
  604.  
  605. #if DEBUG
  606.             // Wait for input to prevent application from closing automatically when debugging
  607.             if (Debugger.IsAttached) {
  608.                 Console.WriteLine("\nPress [enter] to quit.\n");
  609.                 Console.ReadLine();
  610.             }
  611. #endif
  612.         }
  613.  
  614.  
  615.         static void Welcome() {
  616.             string[] header = {
  617.                 "Welcome to DDR4 SPD reader/writer!",
  618.                 "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",
  619.                 "Commercial use is strictly prohibited!",
  620.                 ""
  621.             };
  622.             foreach (string line in header) {
  623.                 Console.WriteLine(line);
  624.             }
  625.         }
  626.         static void ShowHelp() {
  627.             string[] help = {
  628.                 "Command line parameters:",
  629.                 "",
  630.                 "{0} /help",
  631.                 "{0} /find",
  632.                 "{0} /scan <PORT>",
  633.                 "{0} /read <PORT> <ADDRESS#> <filepath> /silent",
  634.                 "{0} /write <PORT> <ADDRESS#> <FILEPATH> /silent",
  635.                 "{0} /writeforce <PORT> <ADDRESS#> <FILEPATH> /silent",
  636.                 "{0} /enablewriteprotection <PORT>",
  637.                 "{0} /enablewriteprotection <PORT> <block#>",
  638.                 "{0} /disablewriteprotection <PORT>",
  639.                 "",
  640.                 "Parameters in CAPS are mandatory!",
  641.                 "Parameter <filepath> is optional when /read switch is used, output will be printed to console only.",
  642.                 "Switch /silent is optional, progress won't be shown with this switch.",
  643.                 "For additional help, visit: https://forums.evga.com/m3053544.aspx"
  644.             };
  645.  
  646.             foreach (string line in help) {
  647.                 Console.WriteLine(line, AppDomain.CurrentDomain.FriendlyName);
  648.             }
  649.         }
  650.  
  651.         static void ParseCommand(string[] args) {
  652.  
  653.             string mode = args[0];
  654.  
  655.             if (mode == "/help") {
  656.                 ShowHelp();
  657.                 return;
  658.             }
  659.  
  660.             try {
  661.                 // Find
  662.                 if (mode == "/find") {
  663.                     // Find
  664.                     string[] devices = Device.Find();
  665.                     if (devices.Length > 0) {
  666.                         foreach (string portName in devices) {
  667.                             Console.WriteLine($"Found Device on Serial Port: {portName}\n");
  668.                         }
  669.                     }
  670.                     else {
  671.                         throw new Exception("Nothing found");
  672.                     }
  673.                     return;
  674.                 }
  675.  
  676.                 // Other functions that require additional parameters
  677.                 if (mode != "/find" && args.Length >= 2) {
  678.  
  679.                     // Init
  680.                     string portName = args[1];
  681.  
  682.                     if (!portName.StartsWith("COM")) {
  683.                         throw new Exception("Port name should start with \"COM\" followed by a number.");
  684.                     }
  685.  
  686.                     Device reader = new Device(portName);
  687.  
  688.                     if (!reader.Connect()) {
  689.                         throw new Exception($"Could not connect to the device on port {portName}.");
  690.                     }
  691.  
  692.                     if (!reader.Test()) {
  693.                         throw new Exception($"The device on port {portName} does not respond.");
  694.                     }
  695.  
  696.                     // Scan
  697.                     if (mode == "/scan" && args.Length == 2) {
  698.                         int[] addresses = reader.Scan();
  699.  
  700.                         if (addresses.Length == 0) {
  701.                             throw new Exception("No EEPROM devices found.");
  702.                         }
  703.  
  704.                         foreach (int location in addresses) {
  705.                             Console.WriteLine($"Found EEPROM at address: {location}");
  706.                         }
  707.  
  708.                         reader.Disconnect();
  709.                         return;
  710.                     }
  711.  
  712.                     // Turn on write protection
  713.                     if (mode == "/enablewriteprotection") {
  714.  
  715.                         Stack<int> block = new Stack<int>();
  716.  
  717.                         if (args.Length == 3) { // Block # was specified
  718.                             try {
  719.                                 block.Push(Int32.Parse(args[2]));
  720.                             }
  721.                             catch {
  722.                                 throw new Exception("Block number should be specified in decimal notation.");
  723.                             }
  724.                         }
  725.                         else { // No block number specified, protect all
  726.                             for (int i = 3; i != -1; i--) { // Push from 3 to 0, so that the stack pops in correct numeric order
  727.                                 block.Push(i);
  728.                             }
  729.                         }
  730.  
  731.                         while (block.Count > 0) {
  732.                             int blocknumber = block.Pop();
  733.                             if (Eeprom.SetWriteProtection(reader, blocknumber)) {
  734.                                 Console.WriteLine($"Block {blocknumber} is now read-only");
  735.                             }
  736.                             else {
  737.                                 throw new Exception($"Unable to set write protection for block {blocknumber}. Either SA0 is not connected to HV, or the block is already read-only.");
  738.                             }
  739.                         }
  740.  
  741.                         return;
  742.                     }
  743.  
  744.                     // Disable write protection
  745.                     if (mode == "/disablewriteprotection") {
  746.  
  747.                         if (Eeprom.ClearWriteProtection(reader)) {
  748.                             Console.WriteLine("Write protection successfully disabled.");
  749.                         }
  750.                         else {
  751.                             throw new Exception("Unable to clear write protection");
  752.                         }
  753.  
  754.                         return;
  755.                     }
  756.  
  757.                     int address;
  758.  
  759.                     try {
  760.                         address = Int32.Parse(args[2]);
  761.                     }
  762.                     catch {
  763.                         throw new Exception("EEPROM address should be specified in decimal notation.");
  764.                     }
  765.  
  766.                     reader.EepromAddress = address;
  767.                     reader.SpdSize = Eeprom.DDR4_SPD_SIZE;
  768.  
  769.                     if (!reader.Probe()) {
  770.                         throw new Exception($"EEPROM is not present at address {reader.EepromAddress}.");
  771.                     }
  772.  
  773.                     string filePath = (args.Length >= 4) ? args[3] : "";
  774.                     bool silent = (args.Length >= 5 && args[4] == "/silent") ? true : false;
  775.  
  776.                     // Read SPD
  777.                     if (mode == "/read") {
  778.  
  779.                         Console.Write($"Reading EEPROM at address {reader.EepromAddress}");
  780.  
  781.                         if (filePath != "") {
  782.                             Console.WriteLine($" to {filePath}");
  783.                         }
  784.                         Console.WriteLine("\n");
  785.  
  786.                         int startTick = Environment.TickCount;
  787.  
  788.                         byte[] spdDump = Eeprom.ReadByte(reader, 0, reader.SpdSize);
  789.  
  790.                         for (int i = 0; i < spdDump.Length; i++) {
  791.                             if (!silent) {
  792.                                 Eeprom.DisplayByte(i, spdDump[i], 16);
  793.                             }
  794.                         }
  795.  
  796.                         Console.Write("\n\nRead {0} {1} from EEPROM at address {2} on port {3} in {4} ms",
  797.                             spdDump.Length,
  798.                             (spdDump.Length > 1) ? "bytes" : "byte",
  799.                             reader.EepromAddress,
  800.                             reader.PortName,
  801.                             Environment.TickCount - startTick
  802.                             );
  803.  
  804.                         if (filePath != "") {
  805.                             try {
  806.                                 File.WriteAllBytes(filePath, spdDump);
  807.                             }
  808.                             catch {
  809.                                 throw new Exception($"Unable to write to {filePath}");
  810.                             }
  811.                             Console.Write($" to file \"{filePath}\"");
  812.                         }
  813.  
  814.                         reader.Disconnect();
  815.                         return;
  816.                     }
  817.  
  818.                     // Write SPD to EEPROM
  819.                     if (mode.StartsWith("/write")) {
  820.  
  821.                         if (filePath.Length < 1) {
  822.                             throw new Exception("File path is mandatory for write mode.");
  823.                         }
  824.  
  825.                         if (!File.Exists(filePath)) {
  826.                             throw new Exception($"File \"{filePath}\" not found.");
  827.                         }
  828.  
  829.                         byte[] inputFile;
  830.                         try {
  831.                             inputFile = File.ReadAllBytes(filePath);
  832.                         }
  833.                         catch {
  834.                             throw new Exception($"Unable to read {filePath}");
  835.                         }
  836.  
  837.                         Console.WriteLine(
  838.                             "Writing \"{0}\" ({1} {2}) to EEPROM at address {3}\n",
  839.                             filePath,
  840.                             inputFile.Length,
  841.                             (inputFile.Length > 1) ? "bytes" : "byte",
  842.                             reader.EepromAddress);
  843.  
  844.                         if (inputFile.Length > reader.SpdSize) {
  845.                             throw new Exception($"File \"{filePath}\" is larger than {reader.SpdSize} bytes.");
  846.                         }
  847.  
  848.                         int bytesWritten = 0;
  849.                         int startTick = Environment.TickCount;
  850.                         byte b;
  851.  
  852.                         for (int i = 0; i != inputFile.Length; i++) {
  853.                             b = inputFile[i];
  854.                             bool writeResult = mode.EndsWith("force")
  855.                                 ? Eeprom.WriteByte(reader, i, inputFile[i])
  856.                                 : Eeprom.UpdateByte(reader, i, inputFile[i]);
  857.  
  858.                             if (!writeResult) {
  859.                                 throw new Exception($"Could not write byte {i} to EEPROM at address {reader.EepromAddress} on port {reader.PortName}.");
  860.                             }
  861.  
  862.                             bytesWritten++;
  863.  
  864.                             if (!silent) {
  865.                                 Eeprom.DisplayByte(i, b);
  866.                             }
  867.                         }
  868.                         reader.Disconnect();
  869.  
  870.                         Console.WriteLine(
  871.                             "\n\nWritten {0} {1} to EEPROM at address {2} on port {3} in {4} ms",
  872.                             bytesWritten,
  873.                             (bytesWritten > 1) ? "bytes" : "byte",
  874.                             reader.EepromAddress,
  875.                             reader.PortName,
  876.                             Environment.TickCount - startTick);
  877.                         return;
  878.                     }
  879.                 }
  880.             }
  881.             catch (Exception e) {
  882.                 //Console.ForegroundColor = ConsoleColor.Red;
  883.                 Console.WriteLine(e.Message);
  884.                 //Console.ForegroundColor = ConsoleColor.Gray;
  885.                 return;
  886.             }
  887.  
  888.             Console.WriteLine("Unknown command line parameters.\n");
  889.             ShowHelp();
  890.         }
  891.     }
  892. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement