1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System.Collections.Generic; 8 using System.Linq; 9 using System.Text; 10 using Antmicro.Renode.Debugging; 11 using Antmicro.Renode.Utilities; 12 13 namespace Antmicro.Renode.Network.ExternalControl 14 { 15 public class Response 16 { CommandFailed(Command command, string reason)17 public static Response CommandFailed(Command command, string reason) 18 { 19 return new Response(ReturnCode.CommandFailed, command, text: reason); 20 } 21 FatalError(string reason)22 public static Response FatalError(string reason) 23 { 24 return new Response(ReturnCode.FatalError, text: reason); 25 } 26 InvalidCommand(Command command)27 public static Response InvalidCommand(Command command) 28 { 29 return new Response(ReturnCode.InvalidCommand, command); 30 } 31 Success(Command command)32 public static Response Success(Command command) 33 { 34 return new Response(ReturnCode.SuccessWithoutData, command); 35 } 36 Success(Command command, IEnumerable<byte> data)37 public static Response Success(Command command, IEnumerable<byte> data) 38 { 39 return new Response(ReturnCode.SuccessWithData, command, data); 40 } 41 Success(Command command, string text)42 public static Response Success(Command command, string text) 43 { 44 return new Response(ReturnCode.SuccessWithData, command, text); 45 } 46 SuccessfulHandshake()47 public static Response SuccessfulHandshake() 48 { 49 return new Response(ReturnCode.SuccessfulHandshake); 50 } 51 Event(Command command, int eventDescriptor, IEnumerable<byte> data)52 public static Response Event(Command command, int eventDescriptor, IEnumerable<byte> data) 53 { 54 return new Response(ReturnCode.AsyncEvent, command, eventDescriptor, data.ToArray()); 55 } 56 Response(ReturnCode returnCode)57 private Response(ReturnCode returnCode) 58 : this(returnCode, null, null, null) 59 { 60 } 61 Response(ReturnCode returnCode, string text)62 private Response(ReturnCode returnCode, string text) 63 : this(returnCode, null, text) 64 { 65 } 66 Response(ReturnCode returnCode, Command? command, string text)67 private Response(ReturnCode returnCode, Command? command, string text) 68 : this(returnCode, command, null, Encoding.ASCII.GetBytes(text)) 69 { 70 dataIsText = true; 71 } 72 Response(ReturnCode returnCode, Command? command, IEnumerable<byte> data)73 private Response(ReturnCode returnCode, Command? command, IEnumerable<byte> data) 74 : this(returnCode, command, null, data.ToArray()) 75 { 76 } 77 Response(ReturnCode returnCode, Command? command, int? eventDescriptor = null, byte[] data = null)78 private Response(ReturnCode returnCode, Command? command, int? eventDescriptor = null, byte[] data = null) 79 { 80 // Command can be null only for FatalError and SuccessfulHandshake 81 DebugHelper.Assert(command != null || returnCode == ReturnCode.FatalError || returnCode == ReturnCode.SuccessfulHandshake); 82 // eventDescriptor can be null for all return codes but AsyncEvent 83 DebugHelper.Assert(eventDescriptor != null || returnCode != ReturnCode.AsyncEvent); 84 85 this.returnCode = returnCode; 86 this.command = command; 87 this.eventDescriptor = eventDescriptor; 88 this.data = data; 89 dataIsText = false; 90 } 91 GetBytes()92 public IEnumerable<byte> GetBytes() 93 { 94 var response = new List<byte> { (byte)returnCode }; 95 96 if(command.HasValue) 97 { 98 response.Add((byte)command); 99 } 100 101 if(eventDescriptor.HasValue) 102 { 103 response.AddRange(((int)eventDescriptor).AsRawBytes()); 104 } 105 106 if(data != null && data.Any()) 107 { 108 response.AddRange(checked((uint)data.Length).AsRawBytes()); 109 response.AddRange(data); 110 } 111 112 return response; 113 } 114 ToString()115 public override string ToString() 116 { 117 var result = new StringBuilder("Response(") 118 .Append(returnCode); 119 120 if(command.HasValue) 121 { 122 result 123 .Append(", command: ") 124 .Append(command); 125 } 126 127 if(eventDescriptor != null) 128 { 129 result 130 .Append(", eventDescriptor: ") 131 .Append(eventDescriptor); 132 } 133 134 if(data != null) 135 { 136 result 137 .Append(", data: ") 138 .Append(dataIsText ? Encoding.ASCII.GetString(data) : Misc.PrettyPrintCollectionHex(data)); 139 } 140 141 return result 142 .Append(')') 143 .ToString(); 144 } 145 146 private readonly bool dataIsText; 147 private readonly Command? command; 148 private readonly int? eventDescriptor; 149 private readonly byte[] data; 150 private readonly ReturnCode returnCode; 151 152 // matches the return_code_t enum in tools/external_control_client/lib/renode_api.c 153 private enum ReturnCode : byte 154 { 155 CommandFailed, 156 FatalError, 157 InvalidCommand, 158 SuccessWithData, 159 SuccessWithoutData, 160 SuccessfulHandshake, 161 AsyncEvent, 162 } 163 } 164 } 165