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; 8 using System.Linq; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Utilities; 13 14 namespace Antmicro.Renode.Peripherals.Network 15 { 16 public class Quectel_BC66 : QuectelModem 17 { Quectel_BC66(IMachine machine, string imeiNumber = DefaultImeiNumber, string softwareVersionNumber = DefaultSoftwareVersionNumber, string serialNumber = DefaultSerialNumber)18 public Quectel_BC66(IMachine machine, string imeiNumber = DefaultImeiNumber, 19 string softwareVersionNumber = DefaultSoftwareVersionNumber, 20 string serialNumber = DefaultSerialNumber) : base(machine, imeiNumber, softwareVersionNumber, serialNumber) 21 { 22 nameModemConfigDecoder = new Dictionary<string, ModemConfigBC66>(StringComparer.OrdinalIgnoreCase) 23 { 24 {"epco", ModemConfigBC66.ExtendedProtocolConfigurationOptions}, 25 {"combinedattach", ModemConfigBC66.CombinedAttach}, 26 {"up", ModemConfigBC66.UserPlane}, 27 {"upopt", ModemConfigBC66.UserPlaneOptimization}, 28 {"multidrb", ModemConfigBC66.MultiDRB}, 29 {"autopdn", ModemConfigBC66.AutoActivationPDN}, 30 {"ripin", ModemConfigBC66.DefaultOutputLevelForRIPin}, 31 {"initlocktime", ModemConfigBC66.InitialSleepLockDuration}, 32 {"dsevent", ModemConfigBC66.DeepSleepEvent}, 33 {"atlocktime", ModemConfigBC66.SleepLockDurationByATCommand}, 34 {"urc/ri/mask", ModemConfigBC66.TriggerRIPinOnURC}, 35 {"vbattimes", ModemConfigBC66.VoltageDetectionCycleForAtQVBATT}, 36 {"activetimer", ModemConfigBC66.ActiveTimer} 37 }; 38 39 modemBasicConfig = new Dictionary<ModemConfigBC66, int>() 40 { 41 {ModemConfigBC66.ExtendedProtocolConfigurationOptions, 0}, 42 {ModemConfigBC66.CombinedAttach, 0}, 43 {ModemConfigBC66.UserPlane, 0}, 44 {ModemConfigBC66.UserPlaneOptimization, 0}, 45 {ModemConfigBC66.MultiDRB, 0}, 46 {ModemConfigBC66.AutoActivationPDN, 0}, 47 {ModemConfigBC66.DefaultOutputLevelForRIPin, 0}, 48 {ModemConfigBC66.InitialSleepLockDuration, 0}, 49 {ModemConfigBC66.DeepSleepEvent, 0}, 50 {ModemConfigBC66.SleepLockDurationByATCommand, 0}, 51 // RI is an output ringing prompt and this setting can take form of 0,<URC> or 1,<URC> 52 // to configure its behavior per URC basis. For now let's assume it's enabled for all URCs - value 2. 53 {ModemConfigBC66.TriggerRIPinOnURC, 2}, 54 {ModemConfigBC66.VoltageDetectionCycleForAtQVBATT, 0}, 55 {ModemConfigBC66.ActiveTimer, 0} 56 }; 57 } 58 59 // QBANDSL - Set the List of Preferred Bands to Be Searched 60 [AtCommand("AT+QBANDSL", CommandType.Write)] QbandslWrite(int mode, int bandCount = 0, params int[] bands)61 protected virtual Response QbandslWrite(int mode, int bandCount = 0, params int[] bands) 62 { 63 if(mode < 0 || mode > 1 || bandCount < 0 || bandCount > 4 || bands.Length != bandCount) 64 { 65 return Error; 66 } 67 68 return Ok; // stub 69 } 70 71 // AT+QLEDMODE - Configure Network-status-indication Light 72 [AtCommand("AT+QLEDMODE", CommandType.Write)] QledmodeWrite(int ledMode)73 protected virtual Response QledmodeWrite(int ledMode) 74 { 75 return SetNetLightMode(ledMode); 76 } 77 78 [AtCommand("AT+QLEDMODE", CommandType.Read)] QledmodeRead()79 protected virtual Response QledmodeRead() => Ok.WithParameters($"+QLEDMODE: {netLightMode}"); 80 81 // QCCLK - Set and Get Current Date and UTC 82 [AtCommand("AT+QCCLK", CommandType.Read)] QcclkRead()83 protected virtual Response QcclkRead() 84 { 85 return Ok.WithParameters(machine.RealTimeClockDateTime.ToString("yy/MM/dd,HH:mm:sszz")); 86 } 87 88 // QCCLK - Set and Get Current Date and UTC 89 [AtCommand("AT+QCCLK", CommandType.Write)] QcclkWrite(string dateTime)90 protected virtual Response QcclkWrite(string dateTime) 91 { 92 this.Log(LogLevel.Warning, "Ignoring attempt to set date/time to '{0}'", dateTime); 93 return Ok; // stub 94 } 95 96 // CCLK - Set and Get Current Date and Time 97 [AtCommand("AT+CCLK", CommandType.Read)] CclkRead()98 protected virtual Response CclkRead() 99 { 100 return Ok.WithParameters("+CCLK: " + machine.RealTimeClockDateTime.ToString("yyyy/MM/dd,HH:mm:ss'GMT'zz")); 101 } 102 103 // CREG - Network Registration 104 [AtCommand("AT+CREG", CommandType.Write)] CregWrite(NetworkRegistrationUrcType type)105 protected override Response CregWrite(NetworkRegistrationUrcType type) 106 { 107 this.Log(LogLevel.Warning, "Command not available for this modem: 'AT+CREG'"); 108 return Error; 109 } 110 111 [AtCommand("AT+CREG", CommandType.Read)] CregRead()112 protected override Response CregRead() 113 { 114 this.Log(LogLevel.Warning, "Command not available for this modem: 'AT+CREG'"); 115 return Error; 116 } 117 118 // QCFG - System Configuration 119 [AtCommand("AT+QCFG", CommandType.Write)] Qcfg(string function, params int[] args)120 protected override Response Qcfg(string function, params int[] args) 121 { 122 if(!nameModemConfigDecoder.TryGetValue(function, out var modemFunction)) 123 { 124 return base.Qcfg(function, args); // unrecognized function 125 } 126 127 if(modemBasicConfig.TryGetValue(modemFunction, out int value)) 128 { 129 if(args.Length == 0) 130 { 131 // If the optional parameter is omitted, query the current configuration. 132 var parameters = string.Format("+QCFG: \"{0}\",{1}", function, value); 133 return Ok.WithParameters(parameters); 134 } 135 else if(args.Length == 1) 136 { 137 modemBasicConfig[modemFunction] = args[0]; 138 // Handle functions with side effects 139 switch(modemFunction) 140 { 141 case ModemConfigBC66.DeepSleepEvent: 142 deepSleepEventEnabled = args[0] != 0; 143 break; 144 } 145 } 146 else 147 { 148 return base.Qcfg(function, args); 149 } 150 151 return Ok; 152 } 153 return base.Qcfg(function, args); 154 } 155 156 [AtCommand("AT+QCFG", CommandType.Read)] QcfgRead()157 protected virtual Response QcfgRead() 158 { 159 var quotedFunctions = nameModemConfigDecoder.Keys.Select(key => key.SurroundWith("\"")); 160 var currentConfigValues = nameModemConfigDecoder.Values.Select(key => modemBasicConfig[key]); 161 var parameters = $"+QCFG: ({string.Join(",", quotedFunctions)}),({string.Join(",", currentConfigValues)})"; 162 return Ok.WithParameters(parameters); 163 } 164 165 // QCGDEFCONT - Set Default PSD Connection Settings 166 [AtCommand("AT+QCGDEFCONT", CommandType.Write)] Qcgdefcont(PdpType pdpType, string apn = R, string username = R, string password = R)167 protected virtual Response Qcgdefcont(PdpType pdpType, string apn = "", string username = "", string password = "") 168 { 169 pdpContextApn = apn; 170 return Ok; // stub 171 } 172 173 // QEMMTIMER - Enable/Disable URC Reporting for EMM Timer 174 [AtCommand("AT+QEMMTIMER", CommandType.Write)] Qemmtimer(int enable = 0)175 protected virtual Response Qemmtimer(int enable = 0) 176 { 177 return Ok; // stub 178 } 179 180 // QICFG - Configure Optional TCP/IP Parameters 181 [AtCommand("AT+QICFG", CommandType.Write)] Qicfg(string parameter, params int[] args)182 protected override Response Qicfg(string parameter, params int[] args) 183 { 184 if(args.Length < 1) 185 { 186 return Error; 187 } 188 189 switch(parameter) 190 { 191 case "echomode": 192 echoInDataMode = args[0] != 0; 193 break; 194 case "dataformat": 195 if(args.Length < 2) 196 { 197 return Error; 198 } 199 sendDataFormat = args[0] != 0 ? DataFormat.Hex : DataFormat.Text; 200 receiveDataFormat = args[1] != 0 ? DataFormat.Hex : DataFormat.Text; 201 break; 202 case "showlength": 203 showLength = args[0] != 0; 204 break; 205 case "viewmode": 206 dataOutputSeparator = args[0] != 0 ? "," : CrLf; 207 break; 208 default: 209 return base.Qicfg(parameter, args); 210 } 211 return Ok; 212 } 213 214 // QRELLOCK - Release Sleep Lock of AT Commands 215 [AtCommand("AT+QRELLOCK")] Qrellock()216 protected virtual Response Qrellock() 217 { 218 // This command is meant to release the 10-second sleep lock timer after each 219 // AT command, not necessarily make the modem enter sleep mode immediately. 220 // We use it as a signal that the software talking to the modem expects it to 221 // enter sleep mode, so we enter sleep mode if this was requested with the 222 // DeepsleepOnRellock property. 223 if(DeepsleepOnRellock && !deepsleepTimer.Enabled) 224 { 225 // The signaling connection goes inactive before entering sleep mode. 226 SendSignalingConnectionStatus(false); 227 ExecuteWithDelay(EnterDeepsleep, 50); 228 } 229 return Ok; 230 } 231 IsValidContextId(int id)232 protected override bool IsValidContextId(int id) 233 { 234 return id == 1; 235 } 236 237 protected override string Vendor => "Quectel_Ltd"; 238 protected override string ModelName => "Quectel_BC66"; 239 protected override string Revision => "Revision: MTK_2625"; 240 protected override string ManufacturerRevision => "BC66NBR01A01"; 241 protected override string SoftwareRevision => "01.002.01.002"; 242 243 private readonly Dictionary<string, ModemConfigBC66> nameModemConfigDecoder; 244 private readonly Dictionary<ModemConfigBC66, int> modemBasicConfig; 245 246 private const string DefaultImeiNumber = "866818039921444"; 247 private const string DefaultSoftwareVersionNumber = "31"; 248 private const string DefaultSerialNumber = "<serial number>"; 249 250 private enum ModemConfigBC66 251 { 252 ExtendedProtocolConfigurationOptions, 253 CombinedAttach, 254 UserPlane, 255 UserPlaneOptimization, 256 MultiDRB, 257 AutoActivationPDN, 258 DefaultOutputLevelForRIPin, 259 InitialSleepLockDuration, 260 DeepSleepEvent, 261 SleepLockDurationByATCommand, 262 TriggerRIPinOnURC, 263 VoltageDetectionCycleForAtQVBATT, 264 ActiveTimer, 265 } 266 } 267 } 268