' verizon.sb
'
' This runs through the Over-The-Air Service Provisioning procedure
' for the Verizon network on a Sierra Wireless MC5725 CDMA module.
' Usage:
'
' bas verizon.sb
'
' The Sarian should be rebooted when the operation is completed.
' The script automatically works out which ASY ports from the modemcc 0 asy_add and info_asy_add settings
' It also deactivates the PPP instance in case it's in use.
' Progress and final result is reported in the eventlog
' Progress is also logged to a RAM file called vstat.txt
' Web files vstat.asp and vstat1.asp are also generated to allow progress
' to be observed at the web interface
' the following line makes all string comparisons case insensitive
'
OPTION COMPARE sbCaseInsensitive
' set up the error handler
'
on error goto ErrorLabel
const nl = "\r\n"
' Enter an error message in the event log
' Also prints message to console output
' And appends the message to the web log file
'
function eventmsg(msg)
print #aspout, msg, nl
print msg, nl
junk = system("setevent \"" & msg & "\"" & " 0")
end function
' Generate the log files for monitoring progress at the web interface
'
function init_log_files
local fh
fh = 0
open "vstat.asp" for output as fh
print #fh, "\r\n"
print #fh, "
Verizon provisioning started. See below for progress
\r\n"
print #fh, "\r\n"
print #fh, "\r\n"
print #fh, "\r\n"
close fh
aspout=0
open "vstat.txt" for output as aspout
end function
' Generate a final status message which can be displayed at the web interface
' This re-writes the top vstat1 frame page
function log_status(log_msg)
local fh
fh = 0
open "vstat1.asp" for output as fh
print #fh, "\r\n"
print #fh, "\r\n"
print #fh, "Verizon Provisioning Status\r\n"
print #fh, "\r\n"
print #fh, "\r\n"
print #fh, "\r\n"
print #fh, "\r\n"
print #fh, "\r\n"
print #fh, "
"
print #fh, "\r\n"
print #fh, "\r\n"
print #fh, "\r\n"
close fh
end function
' Enter an error message in the event log and exit
' Also prints message to console output
' And updates the web status page
'
function errexit(msg)
log_status(msg)
print #aspout, msg, nl
print msg, nl
print "Please reboot the router", nl
junk = system("setevent \"" & msg & "\"" & " 6")
stop
end function
' Return the value of Sarian parameter in rsp
' strCmd needs to be of form " ?"
' e.g. "ppp 0 ans ?"
function getParam(strCmd,rsp)
local res, tmp, tmpPos, x, pos2
getParam = false
res = execute(strCmd, 0, tmp)
if res = true then
tmpPos = InStr(tmp, nl & "OK" & nl)
if tmpPos > 0 then
For x = tmpPos To 1 Step -1
pos2 = InStr(tmp, nl, x - 3)
If pos2 > 0 and pos2 <> tmpPos then
x = 1
endif
next x
if pos2 > 0 and pos2 <> tmpPos then
rsp = mid(tmp, pos2 + 2, tmpPos - pos2 - 2)
getParam = true
endif
endif
endif
end function
' find the first PPP instance configured for GPRS
' returns TRUE if we found one, and put the instance number into num
' otherwise returns FALSE
'
function get_gprs_ppp_instance(num)
local mdmstr, i
get_gprs_ppp_instance = false
for i = 0 to 4
if getParam("ppp " & i & " use_modem ?", mdmstr) = TRUE then
' print "PPP ", i, " use_modem = ", mdmstr, nl
if mdmstr = "1" or mdmstr = "4" or mdmstr = "5" then
get_gprs_ppp_instance = true
num = i
exit function
endif
else
print "Failed to get PPP ", i, " use_modem", nl
endif
next i
end function
' init_mc75io - opens a serial port and initialises a receive buffer for it
' Sets the following GLOBAL vars:
' mc75in - the input file handle
' mc75out - the output file handle
' mc75rxbuf - the receive buffer
' Parameter required is a GLOBAL variable mc75asyport, eg "ASY5"
'
function init_mc75io
' open the serial port used for the MC75
' two handles, one for input and one for output
mc75in = 0
open mc75asyport for input as mc75in
' if error() <> 0 then
' print "error opening asy port", nl
' endif
mc75out = 0
open mc75asyport for output as mc75out
mc75rxbuf = ""
junk = setevent(0)
junk = setevent(mc75in)
end function
' clear the receive buffer
'
function clear_mc75io
local junk, infobuf, tbuf
mx75rxbuf = ""
if waitevents(1000) = mc75in then
junk = sockinfo(mc75in, infobuf)
if infobuf[2] <> 0 then
tbuf = input(infobuf[2], mc75in)
endif
endif
end function
' gets a character from mc75in
' timeout is fixed at 10 secs for now
' Return TRUE if successful, otherwise FALSE
' If TRUE, then character is retuned in the rsp parameter
'
function getchar(rsp)
local tstart, junk, sinfo
getchar = FALSE
tstart = ticks
while ticks-tstart < 1000
if len(mc75rxbuf) <> 0 then
rsp = left(mc75rxbuf, 1)
mc75rxbuf = mid(mc75rxbuf, 2)
getchar = TRUE
exit function
endif
if waitevents(1000) = mc75in then
junk = sockinfo(mc75in, sinfo)
' print "Bytes available = ", sinfo[2], nl
if sinfo[2] <> 0 then
mc75rxbuf = input(sinfo[2], mc75in)
endif
endif
wend
end function
' Get a line from mc75in
' Return TRUE if successful, otherwise FALSE
' If TRUE, then line is retuned in the rsp parameter
'
function getline(rsp)
local junk, sinfo, tbuf, tchr
getline = FALSE
tbuf = ""
while TRUE
if getchar(tchr) <> TRUE then exit function
' see if we received a LF (our end of line)
if asc(tchr) = 0x0a then
rsp = tbuf
getline = TRUE
exit function
endif
' ignore CR, append all other chars
if asc(tchr) <> 0x0d then
tbuf = tbuf & tchr
endif
wend
end function
function getOKline
local i, myline
getOKline = FALSE
for i = 1 to 10
if getline(myline) <> TRUE then exit function
if myline = "OK" then
getOKline = TRUE
exit function
endif
next i
end function
'
' Collect a number of lines into an array
' Stop at OK, ERROR or timeout
' return the number of lines received.
'
function getResplines(rsplines)
local nlines, i, myline
nlines = 0
getResplines = 0
for i = 1 to 10
if getline(myline) <> TRUE then exit function
rsplines[nlines] = myline
nlines += 1
getResplines = nlines
if (instr(myline, "OK") <> undef) or (instr(myline, "ERROR") <> undef) then
exit function
endif
next i
end function
' extract a line from an array obtained from getResplines
'
function extract_result(rsp_lines, nlines, search_str)
local i
extract_result = ""
for i = 0 to nlines-1
if instr(rsp_lines[i], search_str) <> undef then
extract_result = mid(rsp_lines[i], len(search_str)+1)
exit function
endif
next i
end function
' hex and ascii dump of a string
'
function dump_packet(pkt)
local i, j, n, ll, chval, astr
n = len(pkt)
print "Received ", n, " bytes\r\n"
i = 1
while n > 0
if n >= 16 then
ll = 16
else
ll = n
endif
for j = i to i+ll-1
print format("%02X", asc(mid(pkt,j,1))), " "
next j
astr = string((16-ll)*3+4, " ")
for j = i to i+ll-1
chval = asc(mid(pkt,j,1))
if chval >= 0x20 and chval <= 0x7e then
astr = astr & mid(pkt,j,1)
else
astr = astr & "."
endif
next j
print astr, "\r\n"
n -= 16
i += 16
wend
end function
' Send a CnS enable notification message for the given object id
'
function send_cns_enable_notification(objid)
local lobyte, hibyte
lobyte = objid AND 255
hibyte = objid\256
' HIP header: payload len hi, payload len lo, message id (host to modem), parameter
buf[1] = 0
buf[2] = 10
buf[3] = 0x2b
buf[4] = 0
' CnS message
buf[5] = hibyte
buf[6] = lobyte
buf[7] = 5
buf[8] = 0
buf[9] = 0
buf[10] = 0
buf[11] = 0
buf[12] = 0
buf[13] = 0
buf[14] = 0
msglen = 14
' add framing and escape characters
ostr = chr(0x7e)
for i = 1 to msglen
if buf[i] = 0x7e or buf[i] = 0x7d then
ostr = ostr & chr(0x7d)
ostr = ostr & chr(buf[i] XOR 0x20)
else
ostr = ostr & chr(buf[i])
endif
next i
ostr = ostr & chr(0x7e)
print #cnsout, ostr
end function
' remove escape character sequences from a received packet
'
function decode_escapes(string_data_buf)
local ostr, n, i, c
ostr = ""
n = len(string_data_buf)
for i = 1 to n-1
c = asc(mid(string_data_buf,i,1))
if c = 0x7D then
i += 1
c = asc(mid(string_data_buf,i,1))
c = c XOR 0x20
endif
ostr = ostr & chr(c)
next i
' add the last character
ostr = ostr & right(string_data_buf, 1)
decode_escapes = ostr
end function
' get a response from the cns port, or timeout
' after the specified number of seconds
' Returns the recieved packet as a string variable,
' or an empty string in the case of a timeout
' We also decode the PPP escape sequences
'
function get_cns_response(timeout)
local i, pktbuf, complete, junk, infostat, cnsinbuf, unescstr
get_cns_response = ""
pktbuf = ""
i = timeout
complete = FALSE
while i > 0 AND complete = FALSE
junk = sockinfo(cnsin, infostat)
print "CnS bytes received = ", infostat[2], "\r\n"
if infostat[2] <> 0 then
cnsinbuf = input(infostat[2], cnsin)
pktbuf = pktbuf & cnsinbuf
if asc(right(cnsinbuf, 1)) = 0x7e then
complete = TRUE
unescstr = decode_escapes(pktbuf)
dump_packet(unescstr)
get_cns_response = unescstr
exit function
endif
endif
i -= 1
sleep(1)
wend
end function
' gets a character from cnsin
' Return TRUE if successful, otherwise FALSE
' If TRUE, then character is retuned in the rsp parameter
' Data is buffered in the global string variable cnsrxbuf
' timeout is specified in units of secs
'
function getCnSchar(rsp, timeout)
local tstart, junk, sinfo, ttimeout
getCnSchar = FALSE
tstart = ticks
ttimeout = timeout * 100
while ticks-tstart < ttimeout
if len(cnsrxbuf) <> 0 then
rsp = left(cnsrxbuf, 1)
cnsrxbuf = mid(cnsrxbuf, 2)
getCnSchar = TRUE
' print "getCnsChar returning ", asc(rsp), "\r\n"
exit function
endif
junk = sockinfo(cnsin, sinfo)
if sinfo[2] <> 0 then
cnsrxbuf = input(sinfo[2], cnsin)
endif
sleep(1)
wend
end function
' experimental version that splits up multiple packets
'
function get_cns_response1(timeout)
local pktbuf, tchr, unescstr
get_cns_response1 = ""
pktbuf = ""
while TRUE
if getCnSchar(tchr, timeout) <> TRUE then exit function
' see if we received 0x7e (our start/end of packet)
'
if asc(tchr) = 0x7e then
pktbuf = pktbuf & tchr
' print "Added 7E, length is now ", len(pktbuf), "\r\n"
' we got 7e - see if it's the end of a packet
'
if len(pktbuf) > 1 then
unescstr = decode_escapes(pktbuf)
dump_packet(unescstr)
get_cns_response1 = unescstr
exit function
endif
else
'
' not 7e - append to buffer if it's not the first byte
'
' print "Got other char, length is ", len(pktbuf), "\r\n"
if len(pktbuf) >= 1 then
pktbuf = pktbuf & tchr
endif
endif
wend
end function
' check the HIP header and CnS operation type for a valid, non-error response
'
function is_valid_cns_packet(rsppkt)
local plenhi, plenlo, plen, msgid, pobjidhi, pobjidlo, pobjid, payloadlen
is_valid_cns_packet = FALSE
' check the HIP header
plenhi = asc(mid(rsppkt, 2, 1))
plenlo = asc(mid(rsppkt, 3, 1))
plen = plenhi*256 + plenlo
msgid = asc(mid(rsppkt, 4, 1))
print "HIP header: payload len=", plen, ", msgid=0x", format("%02X", msgid), "\r\n"
if plen <> len(rsppkt)-6 then
print "Bad HIP payload length\r\n"
exit function
endif
if msgid <> 0x6B then
print "Unknown Message ID\r\n"
exit function
endif
' check the CnS packet
pobjidhi = asc(mid(rsppkt, 6, 1))
pobjidlo = asc(mid(rsppkt, 7, 1))
pobjid = pobjidhi*256 + pobjidlo
optype = asc(mid(rsppkt, 8, 1))
payloadlen = asc(mid(rsppkt, 15, 1))
print "CnS header: Objid=0x", format("%04X", pobjid), ", Optype=0x", format("%02X", msgid), ", Payloadlen=", payloadlen, "\r\n"
if (optype and 0x80) <> 0 then
print "Got CnS Error response: ", mid(rsppkt, 16, payloadlen), "\r\n"
exit function
endif
is_valid_cns_packet = TRUE
end function
' checks for a valid notification enable response packet
'
function check_enable_response(rsppkt, objid)
local pobjidhi, pobjidlo, pobjid, optype
check_enable_response = FALSE
if is_valid_cns_packet(rsppkt) = FALSE then
exit function
endif
' check the CnS packet
pobjidhi = asc(mid(rsppkt, 6, 1))
pobjidlo = asc(mid(rsppkt, 7, 1))
pobjid = pobjidhi*256 + pobjidlo
optype = asc(mid(rsppkt, 8, 1))
' make sure it is an enable response operation type
if optype <> 0x6 then
print "Expected Enable Reponse (6), got ", optype, "\r\n"
exit function
endif
' make sure the object id matches
if pobjid <> objid then
print "Expected objid ", objid, ", got ", pobjid, "\r\n"
exit function
endif
check_enable_response = TRUE
end function
' checks for a valid OTASP notification
' if so, we check to see if we got an OTASP notification indicating successful NVRAM commit
'
function check_otasp_done(rsppkt, objid)
local inthi, intlo, pobjid, optype, cnslen, otaspstate, otaspsuccess
check_otasp_done = FALSE
if is_valid_cns_packet(rsppkt) = FALSE then
exit function
endif
' check the CnS packet
inthi = asc(mid(rsppkt, 6, 1))
intlo = asc(mid(rsppkt, 7, 1))
pobjid = inthi*256 + intlo
optype = asc(mid(rsppkt, 8, 1))
cnslen = asc(mid(rsppkt, 15, 1))
' make sure it is a notification operation type (7)
if optype <> 0x7 then
print "Expected Notification (7), got ", optype, "\r\n"
exit function
endif
' make sure the object id matches
if pobjid <> objid then
print "Expected objid ", objid, ", got ", pobjid, "\r\n"
exit function
endif
if cnslen < 4 then
print "Expected OTASP length 4, got ", cnslen, "\r\n"
endif
inthi = asc(mid(rsppkt, 16, 1))
intlo = asc(mid(rsppkt, 17, 1))
otaspstate = inthi*256 + intlo
inthi = asc(mid(rsppkt, 18, 1))
intlo = asc(mid(rsppkt, 19, 1))
otaspsuccess = inthi*256 + intlo
if otaspstate = 8 and otaspsuccess = 1 then
check_otasp_done = TRUE
endif
otaspstatestr = "unknown"
if otaspstate = 1 then
otaspstatestr = "SPL unlock"
elseif otaspstate = 2 then
otaspstatestr = "AKey exchange"
elseif otaspstate = 3 then
otaspstatestr = "SSD update"
elseif otaspstate = 4 then
otaspstatestr = "NAM download"
elseif otaspstate = 5 then
otaspstatestr = "MDM download"
elseif otaspstate = 6 then
otaspstatestr = "IMSI download"
elseif otaspstate = 7 then
otaspstatestr = "PRL download"
elseif otaspstate = 8 then
otaspstatestr = "NVRAM commit"
endif
eventmsg "Got OTASP state=" & otaspstate & "(" & otaspstatestr & "), success=" & otaspsuccess
end function
function restorecfg
local res, resstr
close(mc75out)
close(mc75in)
close(cnsout)
close(cnsin)
sleep(1)
res = execute("modemcc 0 asy_add " & asystr, 0, resstr)
res = execute("modemcc 0 info_asy_add " & infoasystr, 0, resstr)
res = execute("ppp " & gprs_ppp_instance & " use_modem 1", 0, resstr)
res = execute("ppp " & gprs_ppp_instance & " aodion on", 0, resstr)
end function
' check that the modem is configured with the right carrierid
'
function check_carrierid
local i, nlines, rsplines
eventmsg "Sent AT+CARRIERID?"
print #mc75out, "AT+CARRIERID?\r"
nlines = getResplines(rsplines)
if nlines = 0 then
eventmsg "No response to AT+CARRIERID?"
exit function
endif
if rsplines[nlines-1] <> "OK" then
eventmsg "AT+CARRIERID? did not return OK, got " & rsplines[nlines-1]
exit function
endif
' for i = 0 to nlines-1
' eventmsg "Got line: " & rsplines[i]
' next i
for i = 0 to nlines-1
if rsplines[i] = "5" then
eventmsg "Carrier ID is Verizon"
exit function
endif
next i
eventmsg "Did not get Verizon carrier ID"
end function
' ******** MAIN PROGRAM STARTS HERE ********
errcnt = 0
init_log_files()
eventmsg "Verizon OTASP provisioning script started"
' Find out what ASY port is used by modemcc for GPRS
'
if getParam("modemcc 0 asy_add ?", asystr) = TRUE then
print "modemcc 0 asy_add is: ", asystr, nl
else
errexit "OTASP failed: unable to get modemcc 0 asy_add"
endif
if asystr = "255" or asystr = "0" or asystr = "" then
errexit "OTASP failed: invalid modemcc asy_add (try muxon)"
endif
' Find out what INFO ASY port is used by modemcc for GPRS
'
if getParam("modemcc 0 info_asy_add ?", infoasystr) = TRUE then
print "modemcc 0 info_asy_add is: ", infoasystr, nl
else
errexit "OTASP failed: unable to get modemcc 0 info_asy_add"
endif
if infoasystr = "0" or infoasystr = "" then
errexit "OTASP failed: invalid modemcc info_asy_add (try muxon)"
endif
' Find out which PPP instance is configured for GPRS
'
got_gprs_ppp = get_gprs_ppp_instance(gprs_ppp_instance)
if got_gprs_ppp then
print "PPP ", gprs_ppp_instance, " is configured for GPRS", nl
res = execute("ppp " & gprs_ppp_instance & " deact_rq", 0, resstr)
sleep(1)
res = execute("ppp " & gprs_ppp_instance & " use_modem !", 0, resstr)
res = execute("ppp " & gprs_ppp_instance & " aodion off", 0, resstr)
else
print "No PPP instances configured for GPRS", nl
endif
res = execute("modemoff", 0, resstr)
eventmsg "Resetting modem..."
sleep(5)
res = execute("modemcc 0 asy_add 255", 0, resstr)
res = execute("modemcc 0 info_asy_add 0", 0, resstr)
sleep(15)
' initialise the AT serial port
AsyPortName = "ASY" & asystr
mc75asyport = AsyPortName
print "AsyPortName = ", AsyPortName, nl
init_mc75io()
' now open the CnS port
errcnt = 0
CnsPortName = "ASY" & infoasystr
cnsout = 0
cnsin = 0
open CnsPortName for output as cnsout
open CnsPortName for input as cnsin
cnsrxbuf = ""
' First make sure we can talk to the device by ATing it
' until we get OK back
'
i = 0
GotOK = false
while i < 10 and GotOK = false
i += 1
print "Sending AT...", nl
print #mc75out, "AT\r"
if getOKline() = true then GotOK = true
wend
if GotOK = false then
restorecfg()
errexit "OTASP failed: cannot AT device on " & AsyPortName
endif
eventmsg "OTASP: connected to modem on " & AsyPortName
' reset the io
'
' close(mc75out)
' close(mc75in)
' sleep(1)
' init_mc75io()
check_carrierid()
' go to skipotasp
' enable OTASP notification messages (objid 0x3014)
'
send_cns_enable_notification(0x3014)
rsp = get_cns_response1(5)
if rsp = "" then
restorecfg()
errexit "OTASP failed: no response to CnS request"
endif
if check_enable_response(rsp, 0x3014) = FALSE then
restorecfg()
errexit "OTASP failed: bad response to CnS request"
endif
' Now initiate the provisioning call (we don't expect a response from this command)
'
eventmsg "Sent AT+CDV*22899"
print #mc75out, "AT+CDV*22899\r"
' set the end time. ticks are units of 10ms
' 5 mins normally
ticks_end = ticks + 5*60*100
' 15 secs for debugging
' ticks_end = ticks + 15*100
' now sit and wait for OTASP notifications
'
otasp_done = FALSE
while ticks <= ticks_end and otasp_done = FALSE
rsp = get_cns_response1(5)
if rsp <> "" then
if check_otasp_done(rsp, 0x3014) = TRUE then
otasp_done = TRUE
endif
endif
wend
if otasp_done = FALSE then
restorecfg()
errexit "OTASP failed: timeout awaiting NVRAM commit"
endif
skipotasp:
' Looks like provisioning worked. Let's try reading back the MDN
'
' First AT the modem again
clear_mc75io()
i = 0
GotOK = false
while i < 10 and GotOK = false
i += 1
print "Sending AT...", nl
print #mc75out, "AT\r"
if getOKline() = true then GotOK = true
wend
' now query the MIN/MDN
' We should get something like this:
'
' at~namval?0
' MDN: 2023292944
' MIN: 2023292944
' SID: 18
' NID: 65535
print #mc75out, "AT~NAMVAL?0\r"
mdn = ""
rslt = getResplines(rsplines)
if rslt <> 0 then
mdn = extract_result(rsplines, rslt, "MDN: ")
endif
' send a reset command. Don't worry about the reply as we'll be rebooting anyway
'
' eventmsg "Sent AT!RESET"
' print #mc75out, "AT!RESET\r"
' Restore the modem connection
'
restorecfg()
errexit "OTASP SUCCESSFUL (MDN=" & mdn & ")"
stop
ErrorLabel:
'print "Got error code: ", error(), "(", error$, ")", nl
if error() = 22 and errcnt < 20 then
print "Retrying connection ...", nl
sleep(1)
errcnt = errcnt + 1
on error goto ErrorLabel
resume
endif
restorecfg()
errexit(error$)