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