' 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, "\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, "\r\n" print #fh, "\r\n" print #fh, "\r\n" print #fh, "\r\n" close 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, "

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, "

" & log_msg & "

\r\n" print #fh, "

Please reboot the router.

" 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$)