1
// *************************************************************************
6
// * Purpose: Utility classes for AT command sequence handling
8
// * Author: Peter Hofmann (software@pxh.de)
10
// * Created: 3.5.1999
11
// *************************************************************************
14
#include <gsm_config.h>
16
#include <gsmlib/gsm_at.h>
17
#include <gsmlib/gsm_nls.h>
18
#include <gsmlib/gsm_util.h>
19
#include <gsmlib/gsm_error.h>
20
#include <gsmlib/gsm_event.h>
21
#include <gsmlib/gsm_me_ta.h>
26
using namespace gsmlib;
30
bool GsmAt::matchResponse(string answer, string responseToMatch)
32
if (answer.substr(0, responseToMatch.length()) == responseToMatch)
35
// some TAs omit the ':' at the end of the response
36
if (_meTa.getCapabilities()._omitsColon &&
37
responseToMatch[responseToMatch.length() - 1] == ':' &&
38
answer.substr(0, responseToMatch.length() - 1) ==
39
responseToMatch.substr(0, responseToMatch.length() - 1))
44
string GsmAt::cutResponse(string answer, string responseToMatch)
46
if (answer.substr(0, responseToMatch.length()) == responseToMatch)
47
return normalize(answer.substr(responseToMatch.length(),
49
responseToMatch.length()));
51
// some TAs omit the ':' at the end of the response
52
if (_meTa.getCapabilities()._omitsColon &&
53
responseToMatch[responseToMatch.length() - 1] == ':' &&
54
answer.substr(0, responseToMatch.length() - 1) ==
55
responseToMatch.substr(0, responseToMatch.length() - 1))
56
return normalize(answer.substr(responseToMatch.length() - 1,
58
responseToMatch.length() + 1));
63
void GsmAt::throwCmeException(string s) throw(GsmException)
65
if (matchResponse(s, "ERROR"))
66
throw GsmException(_("unspecified ME/TA error"), ChatError);
68
bool meError = matchResponse(s, "+CME ERROR:");
70
s = cutResponse(s, "+CME ERROR:");
72
s = cutResponse(s, "+CMS ERROR:");
73
istrstream is(s.c_str());
76
throw GsmException(_("ME/TA error '") +
77
(meError ? getMEErrorText(error) :
78
getSMSErrorText(error)) +
80
stringPrintf(_("(code %s)"), s.c_str()),
84
GsmAt::GsmAt(MeTa &meTa) :
85
_meTa(meTa), _port(meTa.getPort()), _eventHandler(NULL)
89
string GsmAt::chat(string atCommand, string response,
90
bool ignoreErrors, bool acceptEmptyResponse)
94
return chat(atCommand, response, dummy, ignoreErrors, false,
98
string GsmAt::chat(string atCommand, string response, string &pdu,
99
bool ignoreErrors, bool expectPdu,
100
bool acceptEmptyResponse) throw(GsmException)
103
bool gotOk = false; // special handling for empty SMS entries
106
putLine("AT" + atCommand);
107
// and gobble up CR/LF (and possibly echoed characters if echo can't be
111
s = normalize(getLine());
113
while (s.length() == 0 || s == "AT" + atCommand);
116
if (matchResponse(s, "+CME ERROR:") || matchResponse(s, "+CMS ERROR:"))
120
throwCmeException(s);
121
if (matchResponse(s, "ERROR"))
125
throw GsmException(_("ME/TA error '<unspecified>' (code not known)"),
128
// return if response is "OK" and caller says this is OK
129
if (acceptEmptyResponse && s == "OK")
132
// handle PDU if one is expected
138
ps = normalize(getLine());
140
while (ps.length() == 0 && ps != "OK");
146
// remove trailing zero added by some devices (e.g. Falcom A2-1)
147
if (pdu.length() > 0 && pdu[pdu.length() - 1] == 0)
148
pdu.erase(pdu.length() - 1);
152
// handle expected response
153
if (response.length() == 0) // no response expected
155
if (s == "OK") return "";
156
// else fall through to error
161
// some TA/TEs don't prefix their response with the response string
162
// as proscribed by the standard: just handle either case
163
if (matchResponse(s, response))
164
result = cutResponse(s, response);
172
// get the final "OK"
175
s = normalize(getLine());
177
while (s.length() == 0);
179
if (s == "OK") return result;
180
// else fall through to error
184
stringPrintf(_("unexpected response '%s' when sending 'AT%s'"),
185
s.c_str(), atCommand.c_str()),
189
vector<string> GsmAt::chatv(string atCommand, string response,
190
bool ignoreErrors) throw(GsmException)
193
vector<string> result;
196
putLine("AT" + atCommand);
197
// and gobble up CR/LF (and possibly echoed characters if echo can't be
201
s = normalize(getLine());
203
while (s.length() == 0 || s == "AT" + atCommand);
206
if (matchResponse(s, "+CME ERROR:") || matchResponse(s, "+CMS ERROR:"))
210
throwCmeException(s);
211
if (matchResponse(s, "ERROR"))
215
throw GsmException(_("ME/TA error '<unspecified>' (code not known)"),
218
// push all lines that are not empty
219
// cut response prefix if it is there
220
// stop when an OK line is read
225
// some TA/TEs don't prefix their response with the response string
226
// as proscribed by the standard: just handle either case
227
if (response.length() != 0 && matchResponse(s, response))
228
result.push_back(cutResponse(s, response));
234
s = normalize(getLine());
236
while (s.length() == 0);
245
string GsmAt::normalize(string s)
247
size_t start = 0, end = s.length();
250
while (start < end && changed)
253
if (isspace(s[start]))
259
if (isspace(s[end - 1]))
265
return s.substr(start, end - start);
268
string GsmAt::sendPdu(string atCommand, string response,
269
string pdu) throw(GsmException)
272
bool errorCondition = false;
278
putLine("AT" + atCommand);
279
// read first of two bytes "> "
282
// there have been reports that some phones give spurious CRs
286
if (c == '+' || c == 'E') // error or unsolicited result code
289
s = normalize(getLine());
290
errorCondition = (s != "");
292
retry = ! errorCondition;
293
// The following code is for the unlikely case that the TA wants
294
// to resume PDU sending after an unsolicited result code.
295
// For the time being I have decided that it is better to retry
297
// if (! errorCondition)
299
// // readByte() times out after TIMEOUT_SECS (gsm_port.h) seconds
303
// retry = c != '>'; // TA still expects PDU if c == '>'
305
// _port->putBack(c);
307
// catch (GsmException &e)
309
// retry = true; // TA does not expect PDU anymore, retry
316
if (! errorCondition)
319
if (c != '>' || readByte() != ' ')
320
throw GsmException(_("unexpected character in PDU handshake"),
323
putLine(pdu + "\032", false); // write pdu followed by CTRL-Z
326
s = normalize(getLine());
328
while (s.length() == 0 || s == pdu);
332
if (matchResponse(s, "+CME ERROR:") || matchResponse(s, "+CMS ERROR:"))
333
throwCmeException(s);
334
if (matchResponse(s, "ERROR"))
335
throw GsmException(_("ME/TA error '<unspecified>' (code not known)"),
338
if (matchResponse(s, response))
340
string result = cutResponse(s, response);
341
// get the final "OK"
344
s = normalize(getLine());
346
while (s.length() == 0);
348
if (s == "OK") return result;
349
// else fall through to error
352
stringPrintf(_("unexpected response '%s' when sending 'AT%s'"),
353
s.c_str(), atCommand.c_str()),
357
string GsmAt::getLine() throw(GsmException)
359
if (_eventHandler == (GsmEvent*)NULL)
360
return _port->getLine();
367
eventOccurred = false;
368
result = _port->getLine();
369
string s = normalize(result);
370
if (matchResponse(s, "+CMT:") ||
371
matchResponse(s, "+CBM:") ||
372
matchResponse(s, "+CDS:") ||
373
matchResponse(s, "+CMTI:") ||
374
matchResponse(s, "+CBMI:") ||
375
matchResponse(s, "+CDSI:") ||
376
matchResponse(s, "RING") ||
377
// hack: the +CLIP? sequence returns +CLIP: n,m
378
// which is NOT an unsolicited result code
379
(matchResponse(s, "+CLIP:") && s.length() > 10))
381
_eventHandler->dispatch(s, *this);
382
eventOccurred = true;
385
while (eventOccurred);
390
void GsmAt::putLine(string line,
391
bool carriageReturn) throw(GsmException)
393
_port->putLine(line, carriageReturn);
394
// remove empty echo line
399
bool GsmAt::wait(GsmTime timeout) throw(GsmException)
401
return _port->wait(timeout);
404
int GsmAt::readByte() throw(GsmException)
406
return _port->readByte();
409
GsmEvent *GsmAt::setEventHandler(GsmEvent *newHandler)
411
GsmEvent *result = _eventHandler;
412
_eventHandler = newHandler;