diff options
449 files changed, 75319 insertions, 25 deletions
diff --git a/doc/calypso-block.svg b/doc/calypso-block.svg new file mode 100644 index 00000000..6f872676 --- /dev/null +++ b/doc/calypso-block.svg @@ -0,0 +1,707 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="297mm" + height="210mm" + id="svg2383" + sodipodi:version="0.32" + inkscape:version="0.46" + sodipodi:docname="calypso-block.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:export-filename="/home/laforge/calypso-block.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <defs + id="defs2385"> + <marker + inkscape:stockid="CurveIn" + orient="auto" + refY="0.0" + refX="0.0" + id="CurveIn" + style="overflow:visible"> + <path + id="path3349" + d="M 4.6254930,-5.0456926 C 1.8654930,-5.0456926 -0.37450702,-2.8056926 -0.37450702,-0.045692580 C -0.37450702,2.7143074 1.8654930,4.9543074 4.6254930,4.9543074" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;marker-end:none;fill:none" + transform="scale(0.6)" /> + </marker> + <marker + inkscape:stockid="DotM" + orient="auto" + refY="0.0" + refX="0.0" + id="DotM" + style="overflow:visible"> + <path + id="path3229" + d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;marker-end:none" + transform="scale(0.4) translate(7.4, 1)" /> + </marker> + <marker + inkscape:stockid="Arrow2Mstart" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow2Mstart" + style="overflow:visible"> + <path + id="path7719" + style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round" + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(0.6) translate(0,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Mend" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow2Mend" + style="overflow:visible;"> + <path + id="path3191" + style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;" + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(0.6) rotate(180) translate(0,0)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow1Send" + style="overflow:visible;"> + <path + id="path3179" + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" + transform="scale(0.2) rotate(180) translate(6,0)" /> + </marker> + <marker + inkscape:stockid="Arrow1Mend" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow1Mend" + style="overflow:visible;"> + <path + id="path3173" + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" + transform="scale(0.4) rotate(180) translate(10,0)" /> + </marker> + <marker + inkscape:stockid="TriangleOutL" + orient="auto" + refY="0.0" + refX="0.0" + id="TriangleOutL" + style="overflow:visible"> + <path + id="path3307" + d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" + transform="scale(0.8)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow2Lend" + style="overflow:visible;"> + <path + id="path3185" + style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;" + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(1.1) rotate(180) translate(1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow1Lend" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow1Lend" + style="overflow:visible;"> + <path + id="path3188" + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" + transform="scale(0.8) rotate(180) translate(12.5,0)" /> + </marker> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 372.04724 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="1052.3622 : 372.04724 : 1" + inkscape:persp3d-origin="526.18109 : 248.03149 : 1" + id="perspective2392" /> + </defs> + <sodipodi:namedview + inkscape:document-units="mm" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.96166971" + inkscape:cx="576.3753" + inkscape:cy="495.79724" + inkscape:current-layer="layer1" + id="namedview2387" + showgrid="true" + inkscape:snap-global="true" + inkscape:window-width="1022" + inkscape:window-height="731" + inkscape:window-x="1024" + inkscape:window-y="0" + inkscape:snap-guide="true" + inkscape:object-paths="false" + inkscape:object-nodes="false" + objecttolerance="3" + gridtolerance="10000"> + <inkscape:grid + type="xygrid" + id="grid2400" + visible="true" + enabled="true" + units="mm" + spacingx="5mm" + spacingy="5mm" + empspacing="2" /> + </sodipodi:namedview> + <metadata + id="metadata2389"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#CurveIn);stroke-miterlimit:4;stroke-dasharray:none" + d="M 992.12598,230.31495 L 1045.2756,230.31495 L 1045.2756,159.44881 L 1045.2756,159.44881" + id="path22123" /> + <g + id="g12239"> + <rect + y="35.433064" + x="70.866142" + height="265.74802" + width="177.16536" + id="rect7161" + style="fill:#ffffff;stroke:#000000;stroke-width:1.77165353000000003;stroke-miterlimit:4;stroke-dasharray:none" /> + <text + sodipodi:linespacing="100%" + id="text7163" + y="53.149601" + x="72.866142" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="53.149601" + x="72.866142" + id="tspan7165" + sodipodi:role="line">CALYPSO</tspan></text> + </g> + <g + id="g20347"> + <rect + y="70.77356" + x="389.63159" + height="230.24446" + width="106.56361" + id="rect2406" + style="fill:#ffffff;stroke:#000000;stroke-width:1.50725651;stroke-miterlimit:4;stroke-dasharray:none" /> + <text + transform="scale(1.0221827,0.9782987)" + sodipodi:linespacing="100%" + id="text7157" + y="92.63372" + x="381.30542" + style="font-size:21.45465279px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="92.63372" + x="381.30542" + id="tspan7159" + sodipodi:role="line">TWL3025</tspan></text> + <text + sodipodi:linespacing="100%" + id="text12244" + y="216.1601" + x="394.66666" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="216.1601" + x="394.66666" + id="tspan12246" + sodipodi:role="line">BSP</tspan></text> + <text + sodipodi:linespacing="100%" + id="text12248" + y="252.99342" + x="395.16666" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="252.99342" + x="395.16666" + id="tspan12250" + sodipodi:role="line">USP</tspan></text> + <text + sodipodi:linespacing="100%" + id="text12252" + y="286.42783" + x="396.16666" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="286.42783" + x="396.16666" + id="tspan12254" + sodipodi:role="line">TSP</tspan></text> + <text + sodipodi:linespacing="100%" + id="text20330" + y="163.44881" + x="464.62991" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="163.44881" + x="464.62991" + id="tspan20332" + sodipodi:role="line">BUL</tspan></text> + <text + sodipodi:linespacing="100%" + id="text20334" + y="146.96971" + x="463.70053" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="146.96971" + x="463.70053" + id="tspan20336" + sodipodi:role="line">BDL</tspan></text> + </g> + <g + id="g13450"> + <rect + y="177.16687" + x="885.82831" + height="88.888702" + width="106.29617" + id="rect12310" + style="fill:#ffffff;stroke:#000000;stroke-width:1.77468574;stroke-miterlimit:4;stroke-dasharray:none" /> + <text + sodipodi:linespacing="100%" + id="text13440" + y="230.31496" + x="903.54333" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="230.31496" + x="903.54333" + id="tspan13442" + sodipodi:role="line">Antenna</tspan><tspan + id="tspan13444" + y="242.31496" + x="903.54333" + sodipodi:role="line">Switch</tspan></text> + <text + sodipodi:linespacing="100%" + id="text13446" + y="194.88188" + x="887.82678" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="194.88188" + x="887.82678" + id="tspan13448" + sodipodi:role="line">ASM4532</tspan></text> + </g> + <rect + style="fill:#ffffff;stroke:#000000;stroke-width:1.61511528;stroke-miterlimit:4;stroke-dasharray:none" + id="rect11596" + width="142.16623" + height="88.074844" + x="885.74847" + y="35.354794" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#550000;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow2Mend)" + d="M 496.06299,283.46456 L 850.3937,283.46456 L 850.3937,106.29921 L 885.82677,106.29921" + id="path21554" /> + <path + style="fill:#ff0000;fill-rule:evenodd;stroke:#ff0000;stroke-width:3.54330707;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 248.0315,212.59842 L 389.76378,212.59842 L 389.76378,212.59842" + id="path7167" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:3.54330708999999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow2Mend)" + d="M 248.0315,283.46456 L 389.76378,283.46456" + id="path7171" /> + <path + style="fill:#ff0000;fill-rule:evenodd;stroke:#ff0000;stroke-width:3.54330707;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 248.03149,248.03149 L 389.76378,248.03149 L 389.76378,248.03149" + id="path9925" /> + <g + id="g12217"> + <rect + y="35.433064" + x="637.79529" + height="212.59842" + width="141.73228" + id="rect2408" + style="fill:#ffffff;stroke:#000000;stroke-width:1.77165354;stroke-miterlimit:4;stroke-dasharray:none" /> + <text + sodipodi:linespacing="125%" + id="text7153" + y="53.149601" + x="637.79529" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + id="tspan11602" + y="78.149597" + x="637.79529" + sodipodi:role="line">TRF6151</tspan></text> + <text + sodipodi:linespacing="100%" + id="text12175" + y="88.582672" + x="655.51184" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="88.582672" + x="655.51184" + id="tspan12177" + sodipodi:role="line">Transceiver</tspan><tspan + id="tspan12179" + y="104.58267" + x="655.51184" + sodipodi:role="line">Mixers</tspan><tspan + id="tspan12183" + y="120.58267" + x="655.51184" + sodipodi:role="line">VCO</tspan><tspan + id="tspan12181" + y="136.58267" + x="655.51184" + sodipodi:role="line">PLL</tspan></text> + </g> + <text + xml:space="preserve" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="885.82678" + y="53.149601" + id="text11598" + sodipodi:linespacing="100%"><tspan + sodipodi:role="line" + id="tspan11600" + x="885.82678" + y="53.149601">RF3166</tspan></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="903.54333" + y="88.582672" + id="text12185" + sodipodi:linespacing="100%"><tspan + sodipodi:role="line" + x="903.54333" + y="88.582672" + id="tspan12193">RF PA</tspan></text> + <text + xml:space="preserve" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="218.59842" + y="287.46457" + id="text12256" + sodipodi:linespacing="100%"><tspan + sodipodi:role="line" + id="tspan12258" + x="218.59842" + y="287.46457">TSP</tspan></text> + <g + id="g12268"> + <path + id="path11606" + d="M 779.52756,53.149601 L 885.82677,53.149601 L 885.82677,53.149601" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none" /> + <text + sodipodi:linespacing="100%" + id="text12260" + y="51.149601" + x="814.96063" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="51.149601" + x="814.96063" + id="tspan12262" + sodipodi:role="line">GSM</tspan></text> + </g> + <g + id="g12273"> + <path + id="path12195" + d="M 779.52756,70.866136 L 885.82677,70.866136 L 885.82677,70.866136" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text12264" + y="68.866135" + x="814.96063" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="68.866135" + x="814.96063" + id="tspan12266" + sodipodi:role="line">DCS/PCS</tspan></text> + </g> + <g + id="g12290"> + <path + id="path12197" + d="M 885.82677,194.88188 L 779.52756,194.88188 L 779.52756,194.88188" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text12278" + y="192.88188" + x="814.96063" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="192.88188" + x="814.96063" + id="tspan12280" + sodipodi:role="line">GSM</tspan></text> + </g> + <g + id="g12295"> + <path + id="path12165" + d="M 885.82677,212.59841 L 779.52756,212.59841 L 779.52756,212.59841" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text12282" + y="210.59842" + x="814.96063" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="210.59842" + x="814.96063" + id="tspan12284" + sodipodi:role="line">DCS</tspan></text> + </g> + <g + id="g12300"> + <path + id="path12199" + d="M 885.82677,230.31495 L 779.52756,230.31495 L 779.52756,230.31495" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text12286" + y="228.31496" + x="814.96063" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="228.31496" + x="814.96063" + id="tspan12288" + sodipodi:role="line">PCS</tspan></text> + </g> + <g + id="g18005"> + <text + sodipodi:linespacing="100%" + id="text14594" + y="49.149601" + x="425.21259" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + id="tspan14598" + y="49.149601" + x="425.21259" + sodipodi:role="line">RFCLK</tspan></text> + <path + id="path14605" + d="M 637.79528,53.149601 L 248.0315,53.149601 L 248.0315,53.149601" + style="fill:none;fill-rule:evenodd;stroke:#ffff00;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow2Mend)" /> + </g> + <g + id="g18583"> + <path + inkscape:label="#path2410" + id="path241011111" + d="M 496.06299,141.73228 L 637.79528,141.73228 L 637.79528,141.73228 L 637.79528,141.73228" + style="fill:#00ff00;fill-rule:evenodd;stroke:#00ff00;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text12167" + y="137.73228" + x="539.21259" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="137.73228" + x="539.21259" + id="tspan12169" + sodipodi:role="line">I/Q Analog</tspan></text> + <path + id="path15170" + d="M 531.49606,141.73228 L 531.49606,159.44881 L 496.06299,159.44881 L 496.06299,159.44881" + style="fill:none;fill-rule:evenodd;stroke:#00ff00;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Mstart);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + <g + id="g18000"> + <path + id="path17431" + d="M 248.0315,88.582671 L 389.76378,88.582671" + style="fill:none;fill-rule:evenodd;stroke:#ffff00;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow2Mend)" /> + <text + sodipodi:linespacing="100%" + id="text17996" + y="84.582672" + x="295.18109" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="84.582672" + x="295.18109" + id="tspan17998" + sodipodi:role="line">CLK13M</tspan></text> + </g> + <g + id="g18593"> + <path + id="path18010" + d="M 496.06299,194.88188 L 637.79528,194.88188" + style="fill:none;fill-rule:evenodd;stroke:#550000;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text18589" + y="188.88188" + x="537.49603" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="188.88188" + x="537.49603" + id="tspan18591" + sodipodi:role="line">AFC Analog</tspan></text> + </g> + <path + style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow2Mend)" + d="M 212.59843,301.1811 L 212.59843,372.04724 L 921.25984,372.04724 L 921.25984,265.74803" + id="path19726" /> + <text + xml:space="preserve" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="354.33072" + y="368.04724" + id="text20291" + sodipodi:linespacing="100%"><tspan + sodipodi:role="line" + id="tspan20293" + x="354.33072" + y="368.04724">TSP Parallel</tspan></text> + <text + xml:space="preserve" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="356.33072" + y="330.89764" + id="text20295" + sodipodi:linespacing="100%"><tspan + sodipodi:role="line" + id="tspan20297" + x="356.33072" + y="330.89764">TSP Serial</tspan></text> + <g + id="g20314" + transform="translate(-4.7244095e-7,-17.716533)"> + <path + id="path20301" + d="M 248.0315,124.01574 L 389.76378,124.01574" + style="fill:none;fill-rule:evenodd;stroke:#ffff00;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text20303" + y="120.01574" + x="295.18109" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + id="tspan20310" + y="120.01574" + x="295.18109" + sodipodi:role="line">CLK32K</tspan></text> + </g> + <g + id="g20421"> + <path + id="path12871" + d="M 956.69291,124.01574 L 956.69291,177.16535" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + transform="matrix(0,-1,1,0,0,0)" + sodipodi:linespacing="100%" + id="text20402" + y="952.69293" + x="-159.44881" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="952.69293" + x="-159.44881" + id="tspan20404" + sodipodi:role="line">GSM</tspan></text> + </g> + <g + id="g20415"> + <g + id="g20410"> + <path + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none" + d="M 921.25984,124.01574 L 921.25984,177.16535" + id="path12312" /> + <text + xml:space="preserve" + style="font-size:11.36807537px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="-184.69075" + y="867.06183" + id="text20406" + sodipodi:linespacing="100%" + transform="matrix(0,-0.9473396,1.0555877,0,0,0)"><tspan + sodipodi:role="line" + id="tspan20408" + x="-184.69075" + y="867.06183">DCS/PCS</tspan></text> + </g> + </g> + <path + style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-start:url(#DotM);marker-end:url(#Arrow2Mend)" + d="M 921.25984,372.04724 L 1009.8425,372.04724 L 1009.8425,124.01574 L 1009.8425,124.01574" + id="path20426" /> + <text + xml:space="preserve" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="539.21259" + y="279.46457" + id="text22119" + sodipodi:linespacing="100%"><tspan + sodipodi:role="line" + id="tspan22121" + x="539.21259" + y="279.46457">APC Analog</tspan></text> + <path + style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#DotM);marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none" + d="M 318.89764,283.46456 L 318.89764,336.61417 L 673.22835,336.61417 L 673.22835,248.03149" + id="path18598" /> + </g> +</svg> diff --git a/doc/calypso-gsm-notes.txt b/doc/calypso-gsm-notes.txt new file mode 100644 index 00000000..4377ceb4 --- /dev/null +++ b/doc/calypso-gsm-notes.txt @@ -0,0 +1,2 @@ +GSM burst duration: 577uS +RITA synthesizer retuning max: 170uS diff --git a/doc/calypso-signals.txt b/doc/calypso-signals.txt new file mode 100644 index 00000000..be49cf03 --- /dev/null +++ b/doc/calypso-signals.txt @@ -0,0 +1,184 @@ +CALYPSO PAD AND NAME I/O CONNECTS TO TO BFIN? +======================================================================================================== +C12 CLK32K_OUT O TWL3025:CLK32K (32kHz clock input) M +F12 CLK13M_OUT_START_BIT O TWL3025:CLK13M (master clock input) M +D12 nRESPWON I TWL3025:RESPWONz (dbb power-on reset @ batt insert) O +P1 EXT-FIQ I TWL3025:INT1 O +M3 EXT-IRQ I TWL3025:INT2 M +A12 TCXOEN O TRF6151:XSEL,XEN (enable 26MHz oscillator) M +F10 ON_OFF I TWL3025:ON_nOFF (dbb reset @ each switch on) O +B14 IT_WAKEUP O TWL3025:ITWAKEUP (real-time wake-up irq) O +B11 NEMU0 NC +E10 NEMU1 NC +D11 NBSCAN NC +D10 TDI I +C10 TDO O +B10 TCK I +E9 TMS I +L11 BFSR I TWL3025:BFSX (bb-serial tx frame sync) M +K10 BDR I TWL3025:BDX (bb-serial tx data) M +P12 BFSX O TWL3025:BFSR (bb-serial rx frame sync) M +M11 BDX O TWL3025:BDR (bb-serial rx data) M +N11 BCLKX-IO6 NC +P11 BCLKR-ARMCLK GND +P14 VDX O TWL3025:VDR (vb-serial rx data) +N13 VDR I TWL3025:VDX (vb-serial tx data) +M13 VFSRX I TWL3025:VFS (vb-serial frame sync) +N12 VCLKRX I TWL3025:VCK (vb-serial clock) +N7 MCUDI I TWL3025:UDX (spi output) M +M7 MCUDO O TWL3025:UDR (spi input) M +M8 MCUEN0 O TWL3025:UEN (spi enable) M +P8 MCUEN1-IO9 NC +L8 MCUEN2-IO13 NC +G13 SIM_IO IO TWL3025:DBBSIO (sim card shifter data) L +F13 SIM_CLK O TWL3025:DBBSCK (sim card shifter clock) L +G10 SIM_RST O TWL3025:DBBSRST (sim card shifter reset inp) L +G11 SIM_CD-MAS0 V_IO +F14 SIM_PWRCTRL-IO5 O TWL3025:DBBSIO (via 10k) L +B13 OSC32K_OUT O XTAL +C13 OSC32K_IN I XTAL +A14 VSSO GND +E13 CLKTCXO I TRF6151C:RFCLK (TCXO clock output) M +M2 IDDQ GND +N1 NI BOOT GND (via 100k) +N2 nRESET_OUT O NC +M4 IO3-SIM_RNW NC +L4 IO2-IRQ4 NC +P3 IO1-TPU_IDLE O S3C2410 +N3 IO0-TPU_WAIT NC +L7 LT-PWL NC +K7 BU-PWT NC +L6 KBR4-XDIO7 NC +N6 KBR3-XDIO6 NC +P6 KBR2-XDIO5 NC +M6 KBR1-XDIO4 NC +K6 KBR0-XDIO3 NC +M5 KBC4-XDIO2 NC +P5 KBC3-XDIO1 NC +L5 KBC2-XDIO0 NC +K5 KBC1-nIRQ NC +N4 KBC0-nFIQ NC +K9 MCSI_FSYNCH-IO12 NC +N10 MCSI_CLI-IO11 NC +M10 MCSI_RXD-IO10 NC +L10 MCSI_TXD-IO9 NC +C9 CTS_MODEM-X_F S3C24xx +D9 DSR_MODEM-LPG S3c24xx +E8 RTS_MODEM-TOUT S3c24xx +A9 RX_MODEM I S3c24xx +B9 TX_MODEM O S3c24xx +B8 SD_IRDA-CLKOUT_DSP NC +A8 RXIR_IRDA-X_A1 NC +C7 TXIR_IRDA-X_A4 NC +D8 RX_IRDA I socket +C8 TX_IRDA O socket +N9 NSCS(1)-X_A32 NC +L9 NSCS0-SCL NC +M9 SDI-SDA I V-IO +K8 SDO-nINT10 I NC +P9 SCLK-nINT1 I INT0 +I14 TSPCLKX O TRF6151:CLK (serial clock) M +H11 TSPDO O TWL3025:TDR,TRF6151:DATA (serial data) M +H10 DSPDI-IO4 I NC +H13 TSPEN0 O TWL3025:TEN (TSP enable) M +H12 TSPEN1 NC +H14 TSPEN2 TRF6151:STROBE (serial strobe) M +G12 TSPEN3-nSCS2 NC +M12 TSPACT0 O TRF6151:RESETz (serial interface reset) M +M14 TSPACT1 O ASM4532:VC2 M +L12 TSPACT2 O ASM4532:VC1 M +L13 TSPACT3 O RF3166:BAND_SELECT M +I10 TSPACT4-nRDYMEM O ASM4532:VC3 M +K11 TSPACT5-DPLLCLK NC +K13 TSPACT6-nCS6 NC +K12 TSPACT7-CLKX_SPI NC +K14 TSPACT8-nMREQ NC +I11 TSPACT9-MAS1 RF3166:TX_ENABLE (PA TX) M +I12 TSPACT10-nWAIT NC +I13 TSPACT11-MCLK NC +B7 DATA0 RAM/FLASH +D7 DATA1 RAM/FLASH +E7 DATA2 RAM/FLASH +D6 DATA3 RAM/FLASH +A6 DATA4 RAM/FLASH +C6 DATA5 RAM/FLASH +E6 DATA6 RAM/FLASH +C6 DATA7 RAM/FLASH +B5 DATA8 RAM/FLASH +D5 DATA9 RAM/FLASH +E5 DATA10 RAM/FLASH +B4 DATA11 RAM/FLASH +C4 DATA12 RAM/FLASH +D4 DATA13 RAM/FLASH +A3 DATA14 RAM/FLASH +B3 DATA15 RAM/FLASH +F3 ADD0 RAM/FLASH +F2 ADD1 RAM/FLASH +G5 ADD2 RAM/FLASH +G4 ADD3 RAM/FLASH +G2 ADD4 RAM/FLASH +G3 ADD5 RAM/FLASH +H1 ADD6 RAM/FLASH +H3 ADD7 RAM/FLASH +H2 ADD8 RAM/FLASH +H4 ADD9 RAM/FLASH +H5 ADD10 RAM/FLASH +I1 ADD11 RAM/FLASH +I2 ADD12 RAM/FLASH +I3 ADD13 RAM/FLASH +K3 ADD15 RAM/FLASH +K2 ADD16 RAM/FLASH +K4 ADD17 RAM/FLASH +I5 ADD18 RAM/FLASH +L1 ADD19 RAM/FLASH +L2 ADD20 RAM/FLASH +L3 ADD21-CLK16X_IRDA RAM/FLASH +B2 RNW RAM/FLASH +E3 nFEW-X_A0 NC +E2 nFOE-X_A3 RAM/FLASH +F4 FDP-nIACK RAM/FLASH +E4 nBLE-IO15 RAM/FLASH +F5 nBHE-IO14 RAM/FLASH +C2 nCS0 +C3 nCS1 RAM/FLASH +C1 nCS2 NC +D3 nCS3-nINT4 NC +D2 CS4-ADD22 RAM/FLASH +C11 nCS4-CO35 RAM/FLASH +A4 VDDS-MF1 V-FLASH +B6 VDDS-MF2 V-FLASH +G1 VDDS-MF3 V-FLASH +D1 VDDS-MF4 V-FLASH +A11 VDDS_2 V-IO +L14 VDDS_1 V-IO +N5 VDDS_1 V-IO +A5 VDD1 V-DBB +B12 VDD2 V-DBB +N14 VDD3 V-DBB +P7 VDD4 V-DBB +M1 VDD5 V-DBB +E1 VDD6 V-DBB +F1 VSS1 GND +N8 VSS2 GND +K1 VSS3 GND +P2 VSS4 GND +P4 VSS5 GND +P10 VSS6 GND +P13 VSS7 GND +G14 BSS8 GND +A10 VSS9 GND +A7 VSS10 GND +A2 VSS11 GND +B1 VSS12 GND +D13 VDDS_RTC V-RTC +D14 VDD_RTC V-RTC +C14 VSS_RTC GND +E11 VDD_ANG V-IO +E12 VSS_ANG GND +F11 VDD_PLL V-DBB +E14 VSS_PLL GND + +Other signals + +MODEM_ON +MODEM_RST diff --git a/doc/gsmdevboard-block.svg b/doc/gsmdevboard-block.svg new file mode 100644 index 00000000..cb19bc0a --- /dev/null +++ b/doc/gsmdevboard-block.svg @@ -0,0 +1,746 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="297mm" + height="210mm" + id="svg2383" + sodipodi:version="0.32" + inkscape:version="0.46" + sodipodi:docname="gsmdevboard-block.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:export-filename="/sunbeam/home/laforge/gsmdevboard-block.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <defs + id="defs2385"> + <marker + inkscape:stockid="CurveIn" + orient="auto" + refY="0.0" + refX="0.0" + id="CurveIn" + style="overflow:visible"> + <path + id="path3349" + d="M 4.6254930,-5.0456926 C 1.8654930,-5.0456926 -0.37450702,-2.8056926 -0.37450702,-0.045692580 C -0.37450702,2.7143074 1.8654930,4.9543074 4.6254930,4.9543074" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;marker-end:none;fill:none" + transform="scale(0.6)" /> + </marker> + <marker + inkscape:stockid="DotM" + orient="auto" + refY="0.0" + refX="0.0" + id="DotM" + style="overflow:visible"> + <path + id="path3229" + d="M -2.5,-1.0 C -2.5,1.7600000 -4.7400000,4.0 -7.5,4.0 C -10.260000,4.0 -12.5,1.7600000 -12.5,-1.0 C -12.5,-3.7600000 -10.260000,-6.0 -7.5,-6.0 C -4.7400000,-6.0 -2.5,-3.7600000 -2.5,-1.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;marker-end:none" + transform="scale(0.4) translate(7.4, 1)" /> + </marker> + <marker + inkscape:stockid="Arrow2Mstart" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow2Mstart" + style="overflow:visible"> + <path + id="path7719" + style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round" + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(0.6) translate(0,0)" /> + </marker> + <marker + inkscape:stockid="Arrow2Mend" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow2Mend" + style="overflow:visible;"> + <path + id="path3191" + style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;" + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(0.6) rotate(180) translate(0,0)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow1Send" + style="overflow:visible;"> + <path + id="path3179" + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" + transform="scale(0.2) rotate(180) translate(6,0)" /> + </marker> + <marker + inkscape:stockid="Arrow1Mend" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow1Mend" + style="overflow:visible;"> + <path + id="path3173" + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" + transform="scale(0.4) rotate(180) translate(10,0)" /> + </marker> + <marker + inkscape:stockid="TriangleOutL" + orient="auto" + refY="0.0" + refX="0.0" + id="TriangleOutL" + style="overflow:visible"> + <path + id="path3307" + d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" + transform="scale(0.8)" /> + </marker> + <marker + inkscape:stockid="Arrow2Lend" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow2Lend" + style="overflow:visible;"> + <path + id="path3185" + style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;" + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " + transform="scale(1.1) rotate(180) translate(1,0)" /> + </marker> + <marker + inkscape:stockid="Arrow1Lend" + orient="auto" + refY="0.0" + refX="0.0" + id="Arrow1Lend" + style="overflow:visible;"> + <path + id="path3188" + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;" + transform="scale(0.8) rotate(180) translate(12.5,0)" /> + </marker> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 372.04724 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="1052.3622 : 372.04724 : 1" + inkscape:persp3d-origin="526.18109 : 248.03149 : 1" + id="perspective2392" /> + </defs> + <sodipodi:namedview + inkscape:document-units="mm" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.96166971" + inkscape:cx="386.08127" + inkscape:cy="495.79724" + inkscape:current-layer="layer1" + id="namedview2387" + showgrid="true" + inkscape:snap-global="true" + inkscape:window-width="1598" + inkscape:window-height="1163" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:snap-guide="true" + inkscape:object-paths="false" + inkscape:object-nodes="false" + objecttolerance="3" + gridtolerance="10000"> + <inkscape:grid + type="xygrid" + id="grid2400" + visible="true" + enabled="true" + units="mm" + spacingx="5mm" + spacingy="5mm" + empspacing="2" /> + </sodipodi:namedview> + <metadata + id="metadata2389"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#CurveIn);stroke-miterlimit:4;stroke-dasharray:none" + d="M 992.12598,230.31495 L 1045.2756,230.31495 L 1045.2756,159.44881 L 1045.2756,159.44881" + id="path22123" /> + <g + id="g20347"> + <rect + y="70.77356" + x="389.63159" + height="230.24446" + width="106.56361" + id="rect2406" + style="fill:#ffffff;stroke:#000000;stroke-width:1.50725651;stroke-miterlimit:4;stroke-dasharray:none" /> + <text + transform="scale(1.0221827,0.9782987)" + sodipodi:linespacing="100%" + id="text7157" + y="92.63372" + x="381.30542" + style="font-size:21.45465279px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="92.63372" + x="381.30542" + id="tspan7159" + sodipodi:role="line">TWL3025</tspan></text> + <text + sodipodi:linespacing="100%" + id="text12244" + y="216.1601" + x="394.66666" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="216.1601" + x="394.66666" + id="tspan12246" + sodipodi:role="line">BSP</tspan></text> + <text + sodipodi:linespacing="100%" + id="text12248" + y="252.99342" + x="395.16666" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="252.99342" + x="395.16666" + id="tspan12250" + sodipodi:role="line">USP</tspan></text> + <text + sodipodi:linespacing="100%" + id="text12252" + y="286.42783" + x="396.16666" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="286.42783" + x="396.16666" + id="tspan12254" + sodipodi:role="line">TSP</tspan></text> + <text + sodipodi:linespacing="100%" + id="text20330" + y="163.44881" + x="464.62991" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="163.44881" + x="464.62991" + id="tspan20332" + sodipodi:role="line">BUL</tspan></text> + <text + sodipodi:linespacing="100%" + id="text20334" + y="146.96971" + x="463.70053" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="146.96971" + x="463.70053" + id="tspan20336" + sodipodi:role="line">BDL</tspan></text> + </g> + <g + id="g13450"> + <rect + y="177.16687" + x="885.82831" + height="88.888702" + width="106.29617" + id="rect12310" + style="fill:#ffffff;stroke:#000000;stroke-width:1.77468574;stroke-miterlimit:4;stroke-dasharray:none" /> + <text + sodipodi:linespacing="100%" + id="text13440" + y="230.31496" + x="903.54333" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="230.31496" + x="903.54333" + id="tspan13442" + sodipodi:role="line">Antenna</tspan><tspan + id="tspan13444" + y="242.31496" + x="903.54333" + sodipodi:role="line">Switch</tspan></text> + <text + sodipodi:linespacing="100%" + id="text13446" + y="194.88188" + x="887.82678" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="194.88188" + x="887.82678" + id="tspan13448" + sodipodi:role="line">ASM4532</tspan></text> + </g> + <rect + style="fill:#ffffff;stroke:#000000;stroke-width:1.61511528;stroke-miterlimit:4;stroke-dasharray:none" + id="rect11596" + width="142.16623" + height="88.074844" + x="885.74847" + y="35.354794" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#550000;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow2Mend)" + d="M 496.06299,283.46456 L 850.3937,283.46456 L 850.3937,106.29921 L 885.82677,106.29921" + id="path21554" /> + <path + style="fill:#ff0000;fill-rule:evenodd;stroke:#ff0000;stroke-width:3.54330707;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 248.0315,212.59842 L 389.76378,212.59842 L 389.76378,212.59842" + id="path7167" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:3.54330708999999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow2Mend)" + d="M 248.0315,283.46456 L 389.76378,283.46456" + id="path7171" /> + <path + style="fill:#ff0000;fill-rule:evenodd;stroke:#ff0000;stroke-width:3.54330707;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 248.03149,248.03149 L 389.76378,248.03149 L 389.76378,248.03149" + id="path9925" /> + <g + id="g12217"> + <rect + y="35.433064" + x="637.79529" + height="212.59842" + width="141.73228" + id="rect2408" + style="fill:#ffffff;stroke:#000000;stroke-width:1.77165354;stroke-miterlimit:4;stroke-dasharray:none" /> + <text + sodipodi:linespacing="125%" + id="text7153" + y="53.149601" + x="637.79529" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + id="tspan11602" + y="78.149597" + x="637.79529" + sodipodi:role="line">TRF6151</tspan></text> + <text + sodipodi:linespacing="100%" + id="text12175" + y="88.582672" + x="655.51184" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="88.582672" + x="655.51184" + id="tspan12177" + sodipodi:role="line">Transceiver</tspan><tspan + id="tspan12179" + y="104.58267" + x="655.51184" + sodipodi:role="line">Mixers</tspan><tspan + id="tspan12183" + y="120.58267" + x="655.51184" + sodipodi:role="line">VCO</tspan><tspan + id="tspan12181" + y="136.58267" + x="655.51184" + sodipodi:role="line">PLL</tspan></text> + </g> + <text + xml:space="preserve" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="885.82678" + y="53.149601" + id="text11598" + sodipodi:linespacing="100%"><tspan + sodipodi:role="line" + id="tspan11600" + x="885.82678" + y="53.149601">RF3166</tspan></text> + <text + xml:space="preserve" + style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="903.54333" + y="88.582672" + id="text12185" + sodipodi:linespacing="100%"><tspan + sodipodi:role="line" + x="903.54333" + y="88.582672" + id="tspan12193">RF PA</tspan></text> + <g + id="g12268"> + <path + id="path11606" + d="M 779.52756,53.149601 L 885.82677,53.149601 L 885.82677,53.149601" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none" /> + <text + sodipodi:linespacing="100%" + id="text12260" + y="51.149601" + x="814.96063" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="51.149601" + x="814.96063" + id="tspan12262" + sodipodi:role="line">GSM</tspan></text> + </g> + <g + id="g12273"> + <path + id="path12195" + d="M 779.52756,70.866136 L 885.82677,70.866136 L 885.82677,70.866136" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text12264" + y="68.866135" + x="814.96063" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="68.866135" + x="814.96063" + id="tspan12266" + sodipodi:role="line">DCS/PCS</tspan></text> + </g> + <g + id="g12290"> + <path + id="path12197" + d="M 885.82677,194.88188 L 779.52756,194.88188 L 779.52756,194.88188" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text12278" + y="192.88188" + x="814.96063" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="192.88188" + x="814.96063" + id="tspan12280" + sodipodi:role="line">GSM</tspan></text> + </g> + <g + id="g12295"> + <path + id="path12165" + d="M 885.82677,212.59841 L 779.52756,212.59841 L 779.52756,212.59841" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text12282" + y="210.59842" + x="814.96063" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="210.59842" + x="814.96063" + id="tspan12284" + sodipodi:role="line">DCS</tspan></text> + </g> + <g + id="g12300"> + <path + id="path12199" + d="M 885.82677,230.31495 L 779.52756,230.31495 L 779.52756,230.31495" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text12286" + y="228.31496" + x="814.96063" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="228.31496" + x="814.96063" + id="tspan12288" + sodipodi:role="line">PCS</tspan></text> + </g> + <g + id="g18005"> + <text + sodipodi:linespacing="100%" + id="text14594" + y="49.149601" + x="425.21259" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + id="tspan14598" + y="49.149601" + x="425.21259" + sodipodi:role="line">RFCLK</tspan></text> + <path + id="path14605" + d="M 637.79528,53.149601 L 248.0315,53.149601 L 248.0315,53.149601" + style="fill:none;fill-rule:evenodd;stroke:#ffff00;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow2Mend)" /> + </g> + <g + id="g18583"> + <path + inkscape:label="#path2410" + id="path241011111" + d="M 496.06299,141.73228 L 637.79528,141.73228 L 637.79528,141.73228 L 637.79528,141.73228" + style="fill:#00ff00;fill-rule:evenodd;stroke:#00ff00;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Mstart);marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text12167" + y="137.73228" + x="539.21259" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="137.73228" + x="539.21259" + id="tspan12169" + sodipodi:role="line">I/Q Analog</tspan></text> + <path + id="path15170" + d="M 531.49606,141.73228 L 531.49606,159.44881 L 496.06299,159.44881 L 496.06299,159.44881" + style="fill:none;fill-rule:evenodd;stroke:#00ff00;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Mstart);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + <g + id="g18000"> + <path + id="path17431" + d="M 248.0315,88.582671 L 389.76378,88.582671" + style="fill:none;fill-rule:evenodd;stroke:#ffff00;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow2Mend)" /> + <text + sodipodi:linespacing="100%" + id="text17996" + y="84.582672" + x="295.18109" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="84.582672" + x="295.18109" + id="tspan17998" + sodipodi:role="line">CLK13M</tspan></text> + </g> + <g + id="g18593"> + <path + id="path18010" + d="M 496.06299,194.88188 L 637.79528,194.88188" + style="fill:none;fill-rule:evenodd;stroke:#550000;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text18589" + y="188.88188" + x="537.49603" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="188.88188" + x="537.49603" + id="tspan18591" + sodipodi:role="line">AFC Analog</tspan></text> + </g> + <path + style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow2Mend)" + d="M 212.59843,301.1811 L 212.59843,372.04724 L 921.25984,372.04724 L 921.25984,265.74803" + id="path19726" /> + <text + xml:space="preserve" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="354.33072" + y="368.04724" + id="text20291" + sodipodi:linespacing="100%"><tspan + sodipodi:role="line" + id="tspan20293" + x="354.33072" + y="368.04724">TSP Parallel</tspan></text> + <text + xml:space="preserve" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="356.33072" + y="330.89764" + id="text20295" + sodipodi:linespacing="100%"><tspan + sodipodi:role="line" + id="tspan20297" + x="356.33072" + y="330.89764">TSP Serial</tspan></text> + <g + id="g20314" + transform="translate(-4.7244095e-7,-17.716533)"> + <path + id="path20301" + d="M 248.0315,124.01574 L 389.76378,124.01574" + style="fill:none;fill-rule:evenodd;stroke:#ffff00;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text20303" + y="120.01574" + x="295.18109" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + id="tspan20310" + y="120.01574" + x="295.18109" + sodipodi:role="line">CLK32K</tspan></text> + </g> + <g + id="g20421"> + <path + id="path12871" + d="M 956.69291,124.01574 L 956.69291,177.16535" + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464575;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + transform="matrix(0,-1,1,0,0,0)" + sodipodi:linespacing="100%" + id="text20402" + y="952.69293" + x="-159.44881" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="952.69293" + x="-159.44881" + id="tspan20404" + sodipodi:role="line">GSM</tspan></text> + </g> + <g + id="g20415"> + <g + id="g20410"> + <path + style="fill:none;fill-rule:evenodd;stroke:#ff00ff;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none" + d="M 921.25984,124.01574 L 921.25984,177.16535" + id="path12312" /> + <text + xml:space="preserve" + style="font-size:11.36807537px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="-184.69075" + y="867.06183" + id="text20406" + sodipodi:linespacing="100%" + transform="matrix(0,-0.9473396,1.0555877,0,0,0)"><tspan + sodipodi:role="line" + id="tspan20408" + x="-184.69075" + y="867.06183">DCS/PCS</tspan></text> + </g> + </g> + <path + style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-start:url(#DotM);marker-end:url(#Arrow2Mend)" + d="M 921.25984,372.04724 L 1009.8425,372.04724 L 1009.8425,124.01574 L 1009.8425,124.01574" + id="path20426" /> + <text + xml:space="preserve" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + x="539.21259" + y="279.46457" + id="text22119" + sodipodi:linespacing="100%"><tspan + sodipodi:role="line" + id="tspan22121" + x="539.21259" + y="279.46457">APC Analog</tspan></text> + <path + style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:2.83464567;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#DotM);marker-end:url(#Arrow2Mend);stroke-miterlimit:4;stroke-dasharray:none" + d="M 318.89764,283.46456 L 318.89764,336.61417 L 673.22835,336.61417 L 673.22835,248.03149" + id="path18598" /> + <g + id="g3366"> + <rect + y="35.433064" + x="70.866142" + height="265.74802" + width="177.16536" + id="rect7161" + style="fill:#ffffff;stroke:#000000;stroke-width:1.77165353;stroke-miterlimit:4;stroke-dasharray:none" /> + <text + sodipodi:linespacing="100%" + id="text7163" + y="53.149601" + x="72.866142" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + id="tspan3352" + y="53.149601" + x="72.866142" + sodipodi:role="line">BF537</tspan></text> + <text + sodipodi:linespacing="100%" + id="text12256" + y="287.46457" + x="218.59842" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="287.46457" + x="218.59842" + id="tspan12258" + sodipodi:role="line">TSP</tspan></text> + <path + d="M 158.52718,76.505608 A 0.46875,0.46875 0 1 1 157.58968,76.505608 A 0.46875,0.46875 0 1 1 158.52718,76.505608 z" + sodipodi:ry="0.46875" + sodipodi:rx="0.46875" + sodipodi:cy="76.505608" + sodipodi:cx="158.05843" + style="fill:#000000;stroke:none" + id="path2520" + sodipodi:type="arc" /> + <path + id="path2522" + d="M 159.44882,35.327311 L 159.44882,301.07535 L 159.44882,301.07535 L 159.44882,301.07535 L 159.44882,301.07535" + style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1.47677553;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + sodipodi:linespacing="100%" + id="text3348" + y="53.149601" + x="159.44882" + style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + id="tspan3358" + y="53.149601" + x="159.44882" + sodipodi:role="line">Spartan</tspan><tspan + id="tspan3356" + y="73.149597" + x="159.44882" + sodipodi:role="line">3E</tspan></text> + <text + sodipodi:linespacing="100%" + id="text3362" + y="290.92511" + x="89.994156" + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + xml:space="preserve"><tspan + y="290.92511" + x="89.994156" + id="tspan3364" + sodipodi:role="line">Ethernet</tspan></text> + </g> + </g> +</svg> diff --git a/include/l1ctl_proto.h b/include/l1ctl_proto.h new file mode 100644 index 00000000..9adadfd1 --- /dev/null +++ b/include/l1ctl_proto.h @@ -0,0 +1,258 @@ +/* Messages to be sent between the different layers */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __L1CTL_PROTO_H__ +#define __L1CTL_PROTO_H__ + +enum { + _L1CTL_NONE = 0, + L1CTL_FBSB_REQ, + L1CTL_FBSB_CONF, + L1CTL_DATA_IND, + L1CTL_RACH_REQ, + L1CTL_DM_EST_REQ, + L1CTL_DATA_REQ, + L1CTL_RESET_IND, + L1CTL_PM_REQ, /* power measurement */ + L1CTL_PM_CONF, /* power measurement */ + L1CTL_ECHO_REQ, + L1CTL_ECHO_CONF, + L1CTL_RACH_CONF, + L1CTL_RESET_REQ, + L1CTL_RESET_CONF, + L1CTL_DATA_CONF, + L1CTL_CCCH_MODE_REQ, + L1CTL_CCCH_MODE_CONF, + L1CTL_DM_REL_REQ, + L1CTL_PARAM_REQ, + L1CTL_DM_FREQ_REQ, + L1CTL_CRYPTO_REQ, + L1CTL_SIM_REQ, + L1CTL_SIM_CONF, + L1CTL_TCH_MODE_REQ, + L1CTL_TCH_MODE_CONF, +}; + +enum ccch_mode { + CCCH_MODE_NONE = 0, + CCCH_MODE_NON_COMBINED, + CCCH_MODE_COMBINED, +}; + +/* + * NOTE: struct size. We do add manual padding out of the believe + * that it will avoid some unaligned access. + */ + +/* there are no more messages in a sequence */ +#define L1CTL_F_DONE 0x01 + +struct l1ctl_hdr { + uint8_t msg_type; + uint8_t flags; + uint8_t padding[2]; + uint8_t data[0]; +} __attribute__((packed)); + +/* + * downlink info ... down from the BTS.. + */ +struct l1ctl_info_dl { + /* GSM 08.58 channel number (9.3.1) */ + uint8_t chan_nr; + /* GSM 08.58 link identifier (9.3.2) */ + uint8_t link_id; + /* the ARFCN and the band. FIXME: what about MAIO? */ + uint16_t band_arfcn; + + uint32_t frame_nr; + + uint8_t rx_level; /* 0 .. 63 in typical GSM notation (dBm+110) */ + uint8_t snr; /* Signal/Noise Ration (dB) */ + uint8_t num_biterr; + uint8_t fire_crc; + + uint8_t payload[0]; +} __attribute__((packed)); + +/* new CCCH was found. This is following the header */ +struct l1ctl_fbsb_conf { + int16_t initial_freq_err; + uint8_t result; + uint8_t bsic; + /* FIXME: contents of cell_info ? */ +} __attribute__((packed)); + +/* CCCH mode was changed */ +struct l1ctl_ccch_mode_conf { + uint8_t ccch_mode; /* enum ccch_mode */ + uint8_t padding[3]; +} __attribute__((packed)); + +/* TCH mode was changed */ +struct l1ctl_tch_mode_conf { + uint8_t tch_mode; /* enum tch_mode */ + uint8_t padding[3]; +} __attribute__((packed)); + +/* data on the CCCH was found. This is following the header */ +struct l1ctl_data_ind { + uint8_t data[23]; +} __attribute__((packed)); + +/* + * uplink info + */ +struct l1ctl_info_ul { + /* GSM 08.58 channel number (9.3.1) */ + uint8_t chan_nr; + /* GSM 08.58 link identifier (9.3.2) */ + uint8_t link_id; + uint8_t padding[2]; + + uint8_t payload[0]; +} __attribute__((packed)); + +/* + * msg for FBSB_REQ + * the l1_info_ul header is in front + */ +struct l1ctl_fbsb_req { + uint16_t band_arfcn; + uint16_t timeout; /* in TDMA frames */ + + uint16_t freq_err_thresh1; + uint16_t freq_err_thresh2; + + uint8_t num_freqerr_avg; + uint8_t flags; /* L1CTL_FBSB_F_* */ + uint8_t sync_info_idx; + uint8_t ccch_mode; /* enum ccch_mode */ +} __attribute__((packed)); + +#define L1CTL_FBSB_F_FB0 (1 << 0) +#define L1CTL_FBSB_F_FB1 (1 << 1) +#define L1CTL_FBSB_F_SB (1 << 2) +#define L1CTL_FBSB_F_FB01SB (L1CTL_FBSB_F_FB0|L1CTL_FBSB_F_FB1|L1CTL_FBSB_F_SB) + +/* + * msg for CCCH_MODE_REQ + * the l1_info_ul header is in front + */ +struct l1ctl_ccch_mode_req { + uint8_t ccch_mode; /* enum ccch_mode */ + uint8_t padding[3]; +} __attribute__((packed)); + +/* + * msg for TCH_MODE_REQ + * the l1_info_ul header is in front + */ +struct l1ctl_tch_mode_req { + uint8_t tch_mode; /* enum gsm48_chan_mode */ + uint8_t padding[3]; +} __attribute__((packed)); + +/* the l1_info_ul header is in front */ +struct l1ctl_rach_req { + uint8_t ra; + uint8_t combined; + uint16_t offset; +} __attribute__((packed)); + +/* the l1_info_ul header is in front */ +struct l1ctl_par_req { + int8_t ta; + uint8_t tx_power; + uint8_t padding[2]; +} __attribute__((packed)); + +struct l1ctl_h0 { + uint16_t band_arfcn; +} __attribute__((packed)); + +struct l1ctl_h1 { + uint8_t hsn; + uint8_t maio; + uint8_t n; + uint8_t _padding[1]; + uint16_t ma[64]; +} __attribute__((packed)); + +struct l1ctl_dm_est_req { + uint8_t tsc; + uint8_t h; + union { + struct l1ctl_h0 h0; + struct l1ctl_h1 h1; + }; + uint8_t tch_mode; + uint8_t _padding[1]; +} __attribute__((packed)); + +struct l1ctl_dm_freq_req { + uint16_t fn; + uint8_t tsc; + uint8_t h; + union { + struct l1ctl_h0 h0; + struct l1ctl_h1 h1; + }; +} __attribute__((packed)); + +struct l1ctl_crypto_req { + uint8_t algo; + uint8_t key[0]; +} __attribute__((packed)); + +struct l1ctl_pm_req { + uint8_t type; + uint8_t padding[3]; + + union { + struct { + uint16_t band_arfcn_from; + uint16_t band_arfcn_to; + } range; + }; +} __attribute__((packed)); + +/* a single L1CTL_PM response */ +struct l1ctl_pm_conf { + uint16_t band_arfcn; + uint8_t pm[2]; +} __attribute__((packed)); + +enum l1ctl_reset_type { + L1CTL_RES_T_BOOT, /* only _IND */ + L1CTL_RES_T_FULL, + L1CTL_RES_T_SCHED, +}; + +/* argument to L1CTL_RESET_REQ and L1CTL_RESET_IND */ +struct l1ctl_reset { + uint8_t type; + uint8_t pad[3]; +} __attribute__((packed)); + +#endif /* __L1CTL_PROTO_H__ */ diff --git a/include/osmocore/rsl.h b/include/osmocore/rsl.h index 54d67032..3a2b04d4 100644 --- a/include/osmocore/rsl.h +++ b/include/osmocore/rsl.h @@ -7,35 +7,18 @@ void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type); -void rsl_init_cchan_hdr(struct abis_rsl_cchan_hdr *ch, uint8_t msg_type); - extern const struct tlv_definition rsl_att_tlvdef; #define rsl_tlv_parse(dec, buf, len) \ tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0) /* encode channel number as per Section 9.3.1 */ uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot); -/* decode channel number as per Section 9.3.1 */ -int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot); -/* Turns channel number into a string */ -const char *rsl_chan_nr_str(uint8_t chan_nr); +const struct value_string rsl_rlm_cause_strs[]; const char *rsl_err_name(uint8_t err); -const char *rsl_rlm_cause_name(uint8_t err); /* Section 3.3.2.3 TS 05.02. I think this looks like a table */ int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf); -/* Push a RSL RLL header */ -void rsl_rll_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, - uint8_t link_id, int transparent); - -/* Push a RSL RLL header with L3_INFO IE */ -void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, - uint8_t link_id, int transparent); - -/* Allocate msgb and fill with simple RSL RLL header */ -struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr, - uint8_t link_id, int transparent); #endif /* _OSMOCORE_RSL_H */ diff --git a/include/osmocore/utils.h b/include/osmocore/utils.h index 3574f7f8..cf3b4607 100644 --- a/include/osmocore/utils.h +++ b/include/osmocore/utils.h @@ -13,12 +13,5 @@ struct value_string { const char *get_value_string(const struct value_string *vs, uint32_t val); int get_string_value(const struct value_string *vs, const char *str); -char bcd2char(uint8_t bcd); -/* only works for numbers in ascci */ -uint8_t char2bcd(char c); - -int hexparse(const char *str, uint8_t *b, int max_len); -char *hexdump(const unsigned char *buf, int len); -char *hexdump_nospc(const unsigned char *buf, int len); #endif diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 00000000..a0dea5db --- /dev/null +++ b/src/Makefile @@ -0,0 +1,104 @@ + +# this is not really used as we don't do 'make install'. You can still specify +# it in case you _want_ to manually 'make install' the target libosmocore. +CROSS_INST_PREFIX=/usr/local/stow/osmocom-bb/arm-elf + +# this is the prefix of your cross-toolchain programs +CROSS_TOOL_PREFIX=arm-elf- + +TOPDIR=$(shell pwd) +OSMOCORE_CONFIGURE_ENV= LIBOSMOCORE_LIBS=$(TOPDIR)/shared/libosmocore/build-host/src/.libs/libosmocore.a \ + LIBOSMOVTY_LIBS=$(TOPDIR)/shared/libosmocore/build-host/src/vty/.libs/libosmovty.a \ + LIBOSMOCORE_CFLAGS=-I$(TOPDIR)/shared/libosmocore/include \ + LIBOSMOVTY_CFLAGS=-I$(TOPDIR)/shared/libosmocore/include + +all: libosmocore-target nofirmware firmware +nofirmware: libosmocore-host layer23 osmocon gsmmap + +libosmocore-host: shared/libosmocore/build-host/src/.libs/libosmocore.la + +shared/libosmocore/build-host: + mkdir $@ + +shared/libosmocore/configure: shared/libosmocore/configure.in + cd shared/libosmocore && autoreconf -i + +shared/libosmocore/build-host/Makefile: shared/libosmocore/configure shared/libosmocore/build-host + cd shared/libosmocore/build-host && ../configure $(HOST_CONFARGS) + +shared/libosmocore/build-host/src/.libs/libosmocore.la: shared/libosmocore/build-host/Makefile + cd shared/libosmocore/build-host && make + + +libosmocore-target: shared/libosmocore/build-target/src/.libs/libosmocore.a + +shared/libosmocore/build-target: + mkdir $@ + +shared/libosmocore/build-target/Makefile: shared/libosmocore/configure shared/libosmocore/build-target + cd shared/libosmocore/build-target && ../configure \ + --host=arm-elf-linux --disable-vty --enable-panic-infloop \ + --disable-shared --disable-talloc --disable-tests \ + CC="$(CROSS_TOOL_PREFIX)gcc" CFLAGS="-Os -ffunction-sections -I$(TOPDIR)/target/firmware/include" + +shared/libosmocore/build-target/src/.libs/libosmocore.a: shared/libosmocore/build-target/Makefile + cd shared/libosmocore/build-target && make + + +.PHONY: osmocon +osmocon: host/osmocon/osmocon + +host/osmocon/configure: host/osmocon/configure.ac + cd host/osmocon && autoreconf -i + +host/osmocon/Makefile: host/osmocon/configure + cd host/osmocon && $(OSMOCORE_CONFIGURE_ENV) ./configure $(HOST_CONFARGS) + +host/osmocon/osmocon: host/osmocon/Makefile libosmocore-host + make -C host/osmocon + + +.PHONY: gsmmap +gsmmap: host/gsmmap/gsmmap + +host/gsmmap/configure: host/gsmmap/configure.ac + cd host/gsmmap && autoreconf -i + +host/gsmmap/Makefile: host/gsmmap/configure + cd host/gsmmap && $(OSMOCORE_CONFIGURE_ENV) ./configure $(HOST_CONFARGS) + +host/gsmmap/gsmmap: host/gsmmap/Makefile libosmocore-host + make -C host/gsmmap + + +.PHONY: layer23 +layer23: host/layer23/layer23 + +host/layer23/configure: host/layer23/configure.ac + cd host/layer23 && autoreconf -i + +host/layer23/Makefile: host/layer23/configure + cd host/layer23 && $(OSMOCORE_CONFIGURE_ENV) ./configure $(HOST_CONFARGS) + +host/layer23/layer23: host/layer23/Makefile libosmocore-host + make -C host/layer23 + + +.PHONY: firmware +firmware: libosmocore-target + make -C target/firmware CROSS_COMPILE=$(CROSS_TOOL_PREFIX) + + +clean: + make -C shared/libosmocore/build-host $@ + make -C shared/libosmocore/build-target $@ + make -C host/layer23 $@ + make -C host/osmocon $@ + make -C target/firmware $@ + +distclean: + rm -rf shared/libosmocore/build-host + rm -rf shared/libosmocore/build-target + make -C host/layer23 $@ + make -C host/osmocon $@ + make -C target/firmware $@ diff --git a/src/README.building b/src/README.building new file mode 100644 index 00000000..a871c7f0 --- /dev/null +++ b/src/README.building @@ -0,0 +1,30 @@ +== How to build OsmocomBB? == + +=== Prerequisites === + +We assume you are building on a GNU/Linux host system such as Debian +GNU/Linux. Builds have been reported successfully using MacOS X +and the Cygwin environment for MS Windows, but we do not officially support +this. + + # Get a GNU toolchain (gcc/binutils) for ARM (e.g. from http://gnuarm.com/) + # Set your path to include the arm-elf-* executables of your toolchain + # call 'make' in this (the src) subdirectory + +=== Details === + +The master Makefile will build + + * libosmocore for the host (x86 or whatever you use) + * libosmocore for the target (ARM) + * osmocon and layer23 executables for the host (linking libosmocore) + * the actual target firmware images (in src/target/firmware/board/*/*.bin) + +== Transmitting == + +For safety reasons, all code that can enable the transmitter on the phone is +disabled in the default builds. Plese check the src/target/firmware/Makefile +for the "#CFLAGS += -DCONFIG_TX_ENABLE" line. + +Please notice that GSM operates in licensed spectrum and in most jurisdictions +you will need a license from a regulatory authority to transmit. diff --git a/src/README.development b/src/README.development new file mode 100644 index 00000000..2e55f312 --- /dev/null +++ b/src/README.development @@ -0,0 +1,72 @@ += Contributing to OsmocomBB development = + +Feel free to help us by extending the code. Always make sure to +send back all your patches to the baseband-devel@lists.osmocom.org +mailing list - Free Software is all about sharing. + +== Coding Style == + +Lie all C language Osmocom projects, we use the Linux Kernel coding +style, you can find it in the Documentation/CodingStyle subdirectory +of any Linux Kernel source. + +== More Information == + +Please consult the http://bb.osmocom.org/ web page / wiki. + +If you have any technical questions regarding the code, don't hesitate +to ask the baseband-devel@lists.osmocom.org mailing list. + +== subdirectories containing libraries and code == + +=== src/shared/libosmocore === + +is a library of various utility routines, including linked lists, +message buffers, bit-vectors, memory allocator, signals, select loop +handling, timers - as well as some more specifically GSM related things +like a TLV parser, a Comp128V1 implementation and utility functions for +RSL (TS 08.58) and CC/MM/RR (TS 04.08). + +libosmocore is maintained in git://git.osmocom.org/libosmocore.git, so + + DO NOT DIRECTLY COMMIT TO libosmocore IN THIS REPOSITORY! + +We simply maintain a copy (synchronized by git-subtree) in this +repository for the ease of building and to make sure everyone is using +the proper/compatible version of libosmocore + +Please note, whatever you add to libosmocore will need to build as a +Linux userspace program (using glibc) just as well as on the OsmocomBB +embedded target without OS. So please refrain from using fancy +functions. + + +=== src/target/firmware === + +The firmware is what we build for the actual target (phone). It was +written with some idea of modularity in mind, i.e. we have + + * Ti Calypso specific code in calypso/ + * Analog Baseband code in abb/ + * RF Mixer code in rf/ + * Layer1 code in layer1/ + * NOR flash handling in flash/ + * LCD display handlin in display/ + * minimal C-Library code in lib/ + * communications utility routines in comm/ + * Board (phone model/family) specific code in board/ + * board/compal_e88 is the Motorola C115-C124 family + * board/compal_e99 is the Motorola C155 family + * Applications (each app builds one firmware image) in apps/ + + +=== src/target_dsp/calypso === + +This is where we keep some (assembly) code that we wrote for +the DSP that is part of the Caylypso DBB. + +=== src/host/layer23 === + +The Layer2 (LAPDm / TS 04.06) and Layer3 (CC/MM/RR / TS 04.08) +implementations, as they are growing. + diff --git a/src/host/calypso_pll/pll.pl b/src/host/calypso_pll/pll.pl new file mode 100755 index 00000000..52c91319 --- /dev/null +++ b/src/host/calypso_pll/pll.pl @@ -0,0 +1,10 @@ +#!/usr/bin/perl + +my $f_in = 26*1000*1000; + +for (my $mult = 1; $mult < 31; $mult++) { + for (my $div = 0; $div < 3; $div++) { + my $fout = $f_in * ($mult / ($div+1)); + printf("%03.1f MHz (mult=%2u, div=%1u)\n", $fout/(1000*1000), $mult, $div); + } +} diff --git a/src/host/gsm48-andreas/issues.txt b/src/host/gsm48-andreas/issues.txt new file mode 100644 index 00000000..49a796b6 --- /dev/null +++ b/src/host/gsm48-andreas/issues.txt @@ -0,0 +1,23 @@ +Location updating procedure: + +OpenBSC: +If tx_setup fails during process, the msg must be freed to avoid memory leak. + +OpenBSC: +Must use *_LOC_PRN_S_LU on protocol side. +Or it uses *_LOC_PUN_S_LU to hide private network type (maybe some cfg option) + +OpenBSC: +Wrong channel is assigned when mobile supports TCH only. + +LCR: +Also LCR must use correct location. +For MS support, it must use *_LOC_USER + +mncc.h of openbsc / layer23: +What about putting all (except call structure) to osmocore? + + + + + diff --git a/src/host/gsmmap/.gitignore b/src/host/gsmmap/.gitignore new file mode 100644 index 00000000..661fd133 --- /dev/null +++ b/src/host/gsmmap/.gitignore @@ -0,0 +1,35 @@ +# autoreconf by-products +*.in + +aclocal.m4 +autom4te.cache/ +configure +depcomp +install-sh +missing + +# configure by-products +.deps/ +Makefile + +config.status +version.h + +# build by-products +*.o + +gsmmap + +# various +.version +.tarball-version + +# IDA file +*.id* +*.nam +*.til + +# Other test files +*.dump +*.bin +*.log diff --git a/src/host/gsmmap/Makefile.am b/src/host/gsmmap/Makefile.am new file mode 100644 index 00000000..d866d02e --- /dev/null +++ b/src/host/gsmmap/Makefile.am @@ -0,0 +1,18 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 + +# versioning magic +BUILT_SOURCES = $(top_srcdir)/.version +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) + +sbin_PROGRAMS = gsmmap + +INCLUDES += -I../layer23/include -DHOST_BUILD +gsmmap_SOURCES = gsmmap.c geo.c locate.c log.c ../layer23/src/common/sysinfo.c ../layer23/src/common/networks.c +gsmmap_LDADD = $(LIBOSMOCORE_LIBS) -lm + diff --git a/src/host/gsmmap/configure.ac b/src/host/gsmmap/configure.ac new file mode 100644 index 00000000..45a00e2e --- /dev/null +++ b/src/host/gsmmap/configure.ac @@ -0,0 +1,25 @@ +dnl Process this file with autoconf to produce a configure script +AC_INIT([gsmmap], + m4_esyscmd([./git-version-gen .tarball-version]), + [baseband-devel@lists.osmocom.org]) + +AM_INIT_AUTOMAKE([dist-bzip2]) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL + +dnl checks for libraries +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) + +dnl checks for header files +AC_HEADER_STDC + +dnl Checks for typedefs, structures and compiler characteristics + +AC_OUTPUT( + Makefile) diff --git a/src/host/gsmmap/geo.c b/src/host/gsmmap/geo.c new file mode 100644 index 00000000..65633d2c --- /dev/null +++ b/src/host/gsmmap/geo.c @@ -0,0 +1,47 @@ +#include <math.h> +#include "geo.h" + +void geo2space(double *x, double *y, double *z, double lon, double lat) +{ + *z = sin(lat / 180.0 * PI) * POLE_RADIUS; + *x = sin(lon / 180.0 * PI) * cos(lat / 180.0 * PI) * EQUATOR_RADIUS; + *y = -cos(lon / 180.0 * PI) * cos(lat / 180.0 * PI) * EQUATOR_RADIUS; +} + +void space2geo(double *lon, double *lat, double x, double y, double z) +{ + double r; + + /* bring geoid to 1m radius */ + z = z / POLE_RADIUS; + x = x / EQUATOR_RADIUS; + y = y / EQUATOR_RADIUS; + + /* normalize */ + r = sqrt(x * x + y * y + z * z); + z = z / r; + x = x / r; + y = y / r; + + *lat = asin(z) / PI * 180; + *lon = atan2(x, -y) / PI * 180; +} + +double distinspace(double x1, double y1, double z1, double x2, double y2, + double z2) +{ + double x = x1 - x2; + double y = y1 - y2; + double z = z1 - z2; + + return sqrt(x * x + y * y + z * z); +} + +double distonplane(double x1, double y1, double x2, double y2) +{ + double x = x1 - x2; + double y = y1 - y2; + + return sqrt(x * x + y * y); +} + diff --git a/src/host/gsmmap/geo.h b/src/host/gsmmap/geo.h new file mode 100644 index 00000000..25e26cba --- /dev/null +++ b/src/host/gsmmap/geo.h @@ -0,0 +1,12 @@ +/* WGS 84 */ +#define EQUATOR_RADIUS 6378137.0 +#define POLE_RADIUS 6356752.314 + +#define PI 3.1415926536 + +void geo2space(double *x, double *y, double *z, double lat, double lon); +void space2geo(double *lat, double *lon, double x, double y, double z); +double distinspace(double x1, double y1, double z1, double x2, double y2, + double z2); +double distonplane(double x1, double y1, double x2, double y2); + diff --git a/src/host/gsmmap/git-version-gen b/src/host/gsmmap/git-version-gen new file mode 100755 index 00000000..652fac68 --- /dev/null +++ b/src/host/gsmmap/git-version-gen @@ -0,0 +1,151 @@ +#!/bin/sh +# Print a version string. +scriptversion=2010-01-28.01 + +# Copyright (C) 2007-2010 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# It is probably wise to add these two files to .gitignore, so that you +# don't accidentally commit either generated file. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .tarball-version will +# exist in distribution tarballs. +# +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + +case $# in + 1) ;; + *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; +esac + +tarball_version_file=$1 +nl=' +' + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || exit 1 + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test -z "$v" \ + && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 +fi + +if test -n "$v" +then + : # use $v +elif + v=`git describe --abbrev=4 --match='osmocon_v*' HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && case $v in + osmocon_[0-9]*) ;; + osmocon_v[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + case $v in + *-*-*) : git describe is okay three part flavor ;; + *-*) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + numcommits=`git rev-list "$vtag"..HEAD | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + ;; + esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/;s/^osmocon_//'`; +else + v="UNKNOWN" +fi + +v=`echo "$v" |sed 's/^v//'` + +# Don't declare a version "dirty" merely because a time stamp has changed. +git status > /dev/null 2>&1 + +dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= +case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; +esac + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d '\012' + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/src/host/gsmmap/gsmmap.c b/src/host/gsmmap/gsmmap.c new file mode 100644 index 00000000..01f3670c --- /dev/null +++ b/src/host/gsmmap/gsmmap.c @@ -0,0 +1,646 @@ +/* Conversion of logged cells to KML file */ + +/* (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#warning todo bsic +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <math.h> +#include <time.h> + +#define GSM_TA_M 553.85 +#define PI 3.1415926536 + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/networks.h> + +#include "log.h" +#include "geo.h" +#include "locate.h" + +/* + * structure of power and cell infos + */ + +struct power power; +struct sysinfo sysinfo; +static struct node_power *node_power_first = NULL; +static struct node_power **node_power_last_p = &node_power_first; +struct node_mcc *node_mcc_first = NULL; +int log_lines = 0, log_debug = 0; + + +static void nomem(void) +{ + fprintf(stderr, "No mem!\n"); + exit(-ENOMEM); +} + +static void add_power() +{ + struct node_power *node_power; + +// printf("New Power\n"); + /* append or insert to list */ + node_power = calloc(1, sizeof(struct node_power)); + if (!node_power) + nomem(); + *node_power_last_p = node_power; + node_power_last_p = &node_power->next; + memcpy(&node_power->power, &power, sizeof(power)); +} + +static void add_sysinfo() +{ + struct gsm48_sysinfo s; + struct node_mcc *mcc; + struct node_mnc *mnc; + struct node_lac *lac; + struct node_cell *cell; + struct node_meas *meas; + + memset(&s, 0, sizeof(s)); + + /* decode sysinfo */ + if (sysinfo.si1[2]) + gsm48_decode_sysinfo1(&s, + (struct gsm48_system_information_type_1 *) sysinfo.si1, + 23); + if (sysinfo.si2[2]) + gsm48_decode_sysinfo2(&s, + (struct gsm48_system_information_type_2 *) sysinfo.si2, + 23); + if (sysinfo.si2bis[2]) + gsm48_decode_sysinfo2bis(&s, + (struct gsm48_system_information_type_2bis *) + sysinfo.si2bis, + 23); + if (sysinfo.si2ter[2]) + gsm48_decode_sysinfo2ter(&s, + (struct gsm48_system_information_type_2ter *) + sysinfo.si2ter, + 23); + if (sysinfo.si3[2]) + gsm48_decode_sysinfo3(&s, + (struct gsm48_system_information_type_3 *) sysinfo.si3, + 23); + if (sysinfo.si4[2]) + gsm48_decode_sysinfo4(&s, + (struct gsm48_system_information_type_4 *) sysinfo.si4, + 23); + + mcc = get_node_mcc(s.mcc); + if (!mcc) + nomem(); + mnc = get_node_mnc(mcc, s.mnc); + if (!mnc) + nomem(); + lac = get_node_lac(mnc, s.lac); + if (!lac) + nomem(); + cell = get_node_cell(lac, s.cell_id); + if (!cell) + nomem(); + meas = add_node_meas(cell); + if (!meas) + nomem(); + if (!cell->content) { + cell->content = 1; + memcpy(&cell->sysinfo, &sysinfo, sizeof(sysinfo)); + memcpy(&cell->s, &s, sizeof(s)); + } else { + if (memcmp(&cell->sysinfo.si1, sysinfo.si1, + sizeof(sysinfo.si1))) { +new_sysinfo: + fprintf(stderr, "FIXME: the cell changed sysinfo\n"); + return; + } + if (memcmp(&cell->sysinfo.si2, sysinfo.si2, + sizeof(sysinfo.si2))) + goto new_sysinfo; + if (memcmp(&cell->sysinfo.si2bis, sysinfo.si2bis, + sizeof(sysinfo.si2bis))) + goto new_sysinfo; + if (memcmp(&cell->sysinfo.si2ter, sysinfo.si2ter, + sizeof(sysinfo.si2ter))) + goto new_sysinfo; + if (memcmp(&cell->sysinfo.si3, sysinfo.si3, + sizeof(sysinfo.si3))) + goto new_sysinfo; + if (memcmp(&cell->sysinfo.si4, sysinfo.si4, + sizeof(sysinfo.si4))) + goto new_sysinfo; + } +} + +void kml_header(FILE *outfp, char *name) +{ + /* XML header */ + fprintf(outfp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + + /* KML open tag */ + fprintf(outfp, "<kml xmlns=\"http://www.opengis.net/kml/2.2\" " + "xmlns:gx=\"http://www.google.com/kml/ext/2.2\" " + "xmlns:kml=\"http://www.opengis.net/kml/2.2\" " + "xmlns:atom=\"http://www.w3.org/2005/Atom\">\n"); + + /* document open tag */ + fprintf(outfp, "<Document>\n"); + + /* pushpin */ + fprintf(outfp, "\t<Style id=\"sn_placemark_red_pushpin\">\n"); + fprintf(outfp, "\t\t<IconStyle>\n"); + fprintf(outfp, "\t\t\t<scale>1.1</scale>\n"); + fprintf(outfp, "\t\t\t<Icon>\n"); + fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/" + "pushpin/red-pushpin.png</href>\n"); + fprintf(outfp, "\t\t\t</Icon>\n"); + fprintf(outfp, "\t\t</IconStyle>\n"); + fprintf(outfp, "\t\t<ListStyle>\n"); + fprintf(outfp, "\t\t</ListStyle>\n"); + fprintf(outfp, "\t</Style>\n"); + fprintf(outfp, "\t<Style id=\"sh_placemark_red_pushpin_highlight\">\n"); + fprintf(outfp, "\t\t<IconStyle>\n"); + fprintf(outfp, "\t\t\t<scale>1.3</scale>\n"); + fprintf(outfp, "\t\t\t<Icon>\n"); + fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/" + "pushpin/red-pushpin.png</href>\n"); + fprintf(outfp, "\t\t\t</Icon>\n"); + fprintf(outfp, "\t\t</IconStyle>\n"); + fprintf(outfp, "\t\t<ListStyle>\n"); + fprintf(outfp, "\t\t</ListStyle>\n"); + fprintf(outfp, "\t</Style>\n"); + fprintf(outfp, "\t<StyleMap id=\"msn_placemark_red_pushpin\">\n"); + fprintf(outfp, "\t\t<Pair>\n"); + fprintf(outfp, "\t\t\t<key>normal</key>\n"); + fprintf(outfp, "\t\t\t<styleUrl>#sn_placemark_red_pushpin" + "</styleUrl>\n"); + fprintf(outfp, "\t\t</Pair>\n"); + fprintf(outfp, "\t\t<Pair>\n"); + fprintf(outfp, "\t\t\t<key>highlight</key>\n"); + fprintf(outfp, "\t\t\t<styleUrl>#sh_placemark_red_pushpin_highlight" + "</styleUrl>\n"); + fprintf(outfp, "\t\t</Pair>\n"); + fprintf(outfp, "\t</StyleMap>\n"); + + fprintf(outfp, "\t<Style id=\"sn_placemark_grn_pushpin\">\n"); + fprintf(outfp, "\t\t<IconStyle>\n"); + fprintf(outfp, "\t\t\t<scale>1.1</scale>\n"); + fprintf(outfp, "\t\t\t<Icon>\n"); + fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/" + "pushpin/grn-pushpin.png</href>\n"); + fprintf(outfp, "\t\t\t</Icon>\n"); + fprintf(outfp, "\t\t</IconStyle>\n"); + fprintf(outfp, "\t\t<ListStyle>\n"); + fprintf(outfp, "\t\t</ListStyle>\n"); + fprintf(outfp, "\t</Style>\n"); + fprintf(outfp, "\t<Style id=\"sh_placemark_grn_pushpin_highlight\">\n"); + fprintf(outfp, "\t\t<IconStyle>\n"); + fprintf(outfp, "\t\t\t<scale>1.3</scale>\n"); + fprintf(outfp, "\t\t\t<Icon>\n"); + fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/" + "pushpin/grn-pushpin.png</href>\n"); + fprintf(outfp, "\t\t\t</Icon>\n"); + fprintf(outfp, "\t\t</IconStyle>\n"); + fprintf(outfp, "\t\t<ListStyle>\n"); + fprintf(outfp, "\t\t</ListStyle>\n"); + fprintf(outfp, "\t</Style>\n"); + fprintf(outfp, "\t<StyleMap id=\"msn_placemark_grn_pushpin\">\n"); + fprintf(outfp, "\t\t<Pair>\n"); + fprintf(outfp, "\t\t\t<key>normal</key>\n"); + fprintf(outfp, "\t\t\t<styleUrl>#sn_placemark_grn_pushpin" + "</styleUrl>\n"); + fprintf(outfp, "\t\t</Pair>\n"); + fprintf(outfp, "\t\t<Pair>\n"); + fprintf(outfp, "\t\t\t<key>highlight</key>\n"); + fprintf(outfp, "\t\t\t<styleUrl>#sh_placemark_grn_pushpin_highlight" + "</styleUrl>\n"); + fprintf(outfp, "\t\t</Pair>\n"); + fprintf(outfp, "\t</StyleMap>\n"); + + /* circle */ + fprintf(outfp, "\t<Style id=\"sn_placemark_circle\">\n"); + fprintf(outfp, "\t\t<IconStyle>\n"); + fprintf(outfp, "\t\t\t<scale>1.0</scale>\n"); + fprintf(outfp, "\t\t\t<Icon>\n"); + fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/" + "shapes/placemark_circle.png</href>\n"); + fprintf(outfp, "\t\t\t</Icon>\n"); + fprintf(outfp, "\t\t</IconStyle>\n"); + fprintf(outfp, "\t\t<ListStyle>\n"); + fprintf(outfp, "\t\t</ListStyle>\n"); + fprintf(outfp, "\t</Style>\n"); + fprintf(outfp, "\t<Style id=\"sh_placemark_circle_highlight\">\n"); + fprintf(outfp, "\t\t<IconStyle>\n"); + fprintf(outfp, "\t\t\t<scale>1.2</scale>\n"); + fprintf(outfp, "\t\t\t<Icon>\n"); + fprintf(outfp, "\t\t\t\t<href>http://maps.google.com/mapfiles/kml/" + "shapes/placemark_circle_highlight.png</href>\n"); + fprintf(outfp, "\t\t\t</Icon>\n"); + fprintf(outfp, "\t\t</IconStyle>\n"); + fprintf(outfp, "\t\t<ListStyle>\n"); + fprintf(outfp, "\t\t</ListStyle>\n"); + fprintf(outfp, "\t</Style>\n"); + fprintf(outfp, "\t<StyleMap id=\"msn_placemark_circle\">\n"); + fprintf(outfp, "\t\t<Pair>\n"); + fprintf(outfp, "\t\t\t<key>normal</key>\n"); + fprintf(outfp, "\t\t\t<styleUrl>#sn_placemark_circle</styleUrl>\n"); + fprintf(outfp, "\t\t</Pair>\n"); + fprintf(outfp, "\t\t<Pair>\n"); + fprintf(outfp, "\t\t\t<key>highlight</key>\n"); + fprintf(outfp, "\t\t\t<styleUrl>#sh_placemark_circle_highlight" + "</styleUrl>\n"); + fprintf(outfp, "\t\t</Pair>\n"); + fprintf(outfp, "\t</StyleMap>\n"); +} + +void kml_footer(FILE *outfp) +{ + /* document close tag */ + fprintf(outfp, "</Document>\n"); + + /* KML close tag */ + fprintf(outfp, "</kml>\n"); + +} + +void kml_meas(FILE *outfp, struct node_meas *meas, int n, uint16_t mcc, + uint16_t mnc, uint16_t lac, uint16_t cellid) +{ + struct tm *tm = localtime(&meas->gmt); + + fprintf(outfp, "\t\t\t\t\t<Placemark>\n"); + fprintf(outfp, "\t\t\t\t\t\t<name>%d: %d</name>\n", n, meas->rxlev); + fprintf(outfp, "\t\t\t\t\t\t<description>\n"); + fprintf(outfp, "MCC=%s MNC=%s\nLAC=%04x CELL-ID=%04x\n(%s %s)\n", + gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac, cellid, + gsm_get_mcc(mcc), gsm_get_mnc(mcc, mnc)); + fprintf(outfp, "\n%s", asctime(tm)); + fprintf(outfp, "RX-LEV %d dBm\n", meas->rxlev); + if (meas->ta_valid) + fprintf(outfp, "TA=%d (%d-%d meter)\n", meas->ta, + (int)(GSM_TA_M * meas->ta), + (int)(GSM_TA_M * (meas->ta + 1))); + fprintf(outfp, "\t\t\t\t\t\t</description>\n"); + fprintf(outfp, "\t\t\t\t\t\t<LookAt>\n"); + fprintf(outfp, "\t\t\t\t\t\t\t<longitude>%.8f</longitude>\n", + meas->longitude); + fprintf(outfp, "\t\t\t\t\t\t\t<latitude>%.8f</latitude>\n", + meas->latitude); + fprintf(outfp, "\t\t\t\t\t\t\t<altitude>0</altitude>\n"); + fprintf(outfp, "\t\t\t\t\t\t\t<tilt>0</tilt>\n"); + fprintf(outfp, "\t\t\t\t\t\t\t<altitudeMode>relativeToGround" + "</altitudeMode>\n"); + fprintf(outfp, "\t\t\t\t\t\t\t<gx:altitudeMode>relativeToSeaFloor" + "</gx:altitudeMode>\n"); + fprintf(outfp, "\t\t\t\t\t\t</LookAt>\n"); + fprintf(outfp, "\t\t\t\t\t\t<styleUrl>#msn_placemark_circle" + "</styleUrl>\n"); + fprintf(outfp, "\t\t\t\t\t\t<Point>\n"); + fprintf(outfp, "\t\t\t\t\t\t\t<coordinates>%.8f,%.8f</coordinates>\n", + meas->longitude, meas->latitude); + fprintf(outfp, "\t\t\t\t\t\t</Point>\n"); + fprintf(outfp, "\t\t\t\t\t</Placemark>\n"); +} + +static void print_si(void *priv, const char *fmt, ...) +{ + char buffer[1000]; + FILE *outfp = (FILE *)priv; + va_list args; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + if (buffer[0]) + fprintf(outfp, "%s", buffer); +} + +double debug_long, debug_lat, debug_x_scale; +FILE *debug_fp; + +void kml_cell(FILE *outfp, struct node_cell *cell) +{ + struct node_meas *meas; + double x, y, z, sum_x = 0, sum_y = 0, sum_z = 0, longitude, latitude; + int n, known = 0; + + meas = cell->meas; + n = 0; + while (meas) { + if (meas->gps_valid && meas->ta_valid) { + geo2space(&x, &y, &z, meas->longitude, meas->latitude); + sum_x += x; + sum_y += y; + sum_z += z; + n++; + } + meas = meas->next; + } + if (!n) + return; + if (n < 3) { + x = sum_x / n; + y = sum_y / n; + z = sum_z / n; + space2geo(&longitude, &latitude, x, y, z); + } else { + struct probe *probe_first = NULL, *probe, + **probe_last_p = &probe_first; + double x_scale; + + /* translate to flat surface */ + meas = cell->meas; + x_scale = 1.0 / cos(meas->latitude / 180.0 * PI); + longitude = meas->longitude; + latitude = meas->latitude; + debug_x_scale = x_scale; + debug_long = longitude; + debug_lat = latitude; + debug_fp = outfp; + while (meas) { + if (meas->gps_valid && meas->ta_valid) { + probe = calloc(1, sizeof(struct probe)); + if (!probe) + nomem(); + probe->x = (meas->longitude - longitude) / + x_scale; + if (x < -180) + x += 360; + else if (x > 180) + x -= 360; + probe->y = meas->latitude - latitude; + probe->dist = GSM_TA_M * (0.5 + + (double)meas->ta) / + (EQUATOR_RADIUS * PI / 180.0); + *probe_last_p = probe; + probe_last_p = &probe->next; + } + meas = meas->next; + } + + /* locate */ + locate_cell(probe_first, &x, &y); + + /* translate from flat surface */ + longitude += x * x_scale; + if (longitude < 0) + longitude += 360; + else if (longitude >= 360) + longitude -= 360; + latitude += y; + + /* remove probes */ + while (probe_first) { + probe = probe_first; + probe_first = probe->next; + free(probe); + } + + known = 1; + } + + if (!known) + return; + + fprintf(outfp, "\t\t\t\t\t<Placemark>\n"); + fprintf(outfp, "\t\t\t\t\t\t<name>MCC=%s MNC=%s\nLAC=%04x " + "CELL-ID=%04x\n(%s %s)</name>\n", gsm_print_mcc(cell->s.mcc), + gsm_print_mnc(cell->s.mnc), cell->s.lac, cell->s.cell_id, + gsm_get_mcc(cell->s.mcc), + gsm_get_mnc(cell->s.mcc, cell->s.mnc)); + fprintf(outfp, "\t\t\t\t\t\t<description>\n"); + gsm48_sysinfo_dump(&cell->s, cell->sysinfo.arfcn, print_si, outfp); + fprintf(outfp, "\t\t\t\t\t\t</description>\n"); + fprintf(outfp, "\t\t\t\t\t\t<LookAt>\n"); + fprintf(outfp, "\t\t\t\t\t\t\t<longitude>%.8f</longitude>\n", + longitude); + fprintf(outfp, "\t\t\t\t\t\t\t<latitude>%.8f</latitude>\n", latitude); + fprintf(outfp, "\t\t\t\t\t\t\t<altitude>0</altitude>\n"); + fprintf(outfp, "\t\t\t\t\t\t\t<tilt>0</tilt>\n"); + fprintf(outfp, "\t\t\t\t\t\t\t<altitudeMode>relativeToGround" + "</altitudeMode>\n"); + fprintf(outfp, "\t\t\t\t\t\t\t<gx:altitudeMode>relativeToSeaFloor" + "</gx:altitudeMode>\n"); + fprintf(outfp, "\t\t\t\t\t\t</LookAt>\n"); + if (known) + fprintf(outfp, "\t\t\t\t\t\t<styleUrl>#msn_placemark_grn_" + "pushpin</styleUrl>\n"); + else + fprintf(outfp, "\t\t\t\t\t\t<styleUrl>#msn_placemark_red_" + "pushpin</styleUrl>\n"); + fprintf(outfp, "\t\t\t\t\t\t<Point>\n"); + fprintf(outfp, "\t\t\t\t\t\t\t<coordinates>%.8f,%.8f</coordinates>\n", + longitude, latitude); + fprintf(outfp, "\t\t\t\t\t\t</Point>\n"); + fprintf(outfp, "\t\t\t\t\t</Placemark>\n"); + + if (!log_lines) + return; + + fprintf(outfp, "\t<Folder>\n"); + fprintf(outfp, "\t\t<name>Lines</name>\n"); + fprintf(outfp, "\t\t<open>0</open>\n"); + fprintf(outfp, "\t\t<visibility>0</visibility>\n"); + + geo2space(&x, &y, &z, longitude, latitude); + meas = cell->meas; + n = 0; + while (meas) { + if (meas->gps_valid) { + double mx, my, mz, dist; + + geo2space(&mx, &my, &mz, meas->longitude, + meas->latitude); + dist = distinspace(x, y, z, mx, my, mz); + fprintf(outfp, "\t\t<Placemark>\n"); + fprintf(outfp, "\t\t\t<name>Range</name>\n"); + fprintf(outfp, "\t\t\t<description>\n"); + fprintf(outfp, "Distance: %d\n", (int)dist); + fprintf(outfp, "TA=%d (%d-%d meter)\n", meas->ta, + (int)(GSM_TA_M * meas->ta), + (int)(GSM_TA_M * (meas->ta + 1))); + fprintf(outfp, "\t\t\t</description>\n"); + fprintf(outfp, "\t\t\t<visibility>0</visibility>\n"); + fprintf(outfp, "\t\t\t<LineString>\n"); + fprintf(outfp, "\t\t\t\t<tessellate>1</tessellate>\n"); + fprintf(outfp, "\t\t\t\t<coordinates>\n"); + fprintf(outfp, "%.8f,%.8f\n", longitude, latitude); + fprintf(outfp, "%.8f,%.8f\n", meas->longitude, + meas->latitude); + fprintf(outfp, "\t\t\t\t</coordinates>\n"); + fprintf(outfp, "\t\t\t</LineString>\n"); + fprintf(outfp, "\t\t</Placemark>\n"); + } + meas = meas->next; + } + fprintf(outfp, "\t</Folder>\n"); +} + +int main(int argc, char *argv[]) +{ + FILE *infp, *outfp; + int type, n, i; + char *p; + struct node_mcc *mcc; + struct node_mnc *mnc; + struct node_lac *lac; + struct node_cell *cell; + struct node_meas *meas; + + if (argc <= 2) { +usage: + fprintf(stderr, "Usage: %s <file.log> <file.kml> " + "[lines] [debug]\n", argv[0]); + fprintf(stderr, "lines: Add lines between cell and " + "Measurement point\n"); + fprintf(stderr, "debug: Add debugging of location algorithm.\n" + ); + return 0; + } + + for (i = 3; i < argc; i++) { + if (!strcmp(argv[i], "lines")) + log_lines = 1; + else if (!strcmp(argv[i], "debug")) + log_debug = 1; + else goto usage; + } + + infp = fopen(argv[1], "r"); + if (!infp) { + fprintf(stderr, "Failed to open '%s' for reading\n", argv[1]); + return -EIO; + } + + while ((type = read_log(infp))) { + switch (type) { + case LOG_TYPE_SYSINFO: + add_sysinfo(); + break; + case LOG_TYPE_POWER: + add_power(); + break; + } + } + + fclose(infp); + + if (!strcmp(argv[2], "-")) + outfp = stdout; + else + outfp = fopen(argv[2], "w"); + if (!outfp) { + fprintf(stderr, "Failed to open '%s' for writing\n", argv[2]); + return -EIO; + } + + /* document name */ + p = argv[2]; + while (strchr(p, '/')) + p = strchr(p, '/') + 1; + + kml_header(outfp, p); + mcc = node_mcc_first; + while (mcc) { + printf("MCC: %02x\n", mcc->mcc); + /* folder open */ + fprintf(outfp, "\t<Folder>\n"); + fprintf(outfp, "\t\t<name>MCC %s (%s)</name>\n", + gsm_print_mcc(mcc->mcc), gsm_get_mcc(mcc->mcc)); + fprintf(outfp, "\t\t<open>0</open>\n"); + mnc = mcc->mnc; + while (mnc) { + printf(" MNC: %02x\n", mnc->mnc); + /* folder open */ + fprintf(outfp, "\t\t<Folder>\n"); + fprintf(outfp, "\t\t\t<name>MNC %s (%s)</name>\n", + gsm_print_mnc(mnc->mnc), gsm_get_mnc(mcc->mcc, mnc->mnc)); + fprintf(outfp, "\t\t\t<open>0</open>\n"); + lac = mnc->lac; + while (lac) { + printf(" LAC: %04x\n", lac->lac); + /* folder open */ + fprintf(outfp, "\t\t\t<Folder>\n"); + fprintf(outfp, "\t\t\t\t<name>LAC %04x</name>\n", lac->lac); + fprintf(outfp, "\t\t\t\t<open>0</open>\n"); + cell = lac->cell; + while (cell) { + printf(" CELL: %04x\n", cell->cellid); + fprintf(outfp, "\t\t\t\t<Folder>\n"); + fprintf(outfp, "\t\t\t\t\t<name>CELL-ID %04x</name>\n", cell->cellid); + fprintf(outfp, "\t\t\t\t\t<open>0</open>\n"); + meas = cell->meas; + n = 0; + while (meas) { + if (meas->ta_valid) + printf(" TA: %d\n", meas->ta); + if (meas->gps_valid) + kml_meas(outfp, meas, ++n, mcc->mcc, mnc->mnc, + lac->lac, cell->cellid); + meas = meas->next; + } + kml_cell(outfp, cell); + /* folder close */ + fprintf(outfp, "\t\t\t\t</Folder>\n"); + cell = cell->next; + } + /* folder close */ + fprintf(outfp, "\t\t\t</Folder>\n"); + lac = lac->next; + } + /* folder close */ + fprintf(outfp, "\t\t</Folder>\n"); + mnc = mnc->next; + } + /* folder close */ + fprintf(outfp, "\t</Folder>\n"); + mcc = mcc->next; + } +#if 0 + FIXME: power + /* folder open */ + fprintf(outfp, "\t<Folder>\n"); + fprintf(outfp, "\t\t<name>Power</name>\n"); + fprintf(outfp, "\t\t<open>0</open>\n"); + power = node_power_first; + n = 0; + while (power) { + /* folder open */ + fprintf(outfp, "\t\t<Folder>\n"); + fprintf(outfp, "\t\t\t<name>Power %d</name>\n", ++n); + fprintf(outfp, "\t\t\t<open>0</open>\n"); + /* folder close */ + fprintf(outfp, "\t\t</Folder>\n"); + power = power->next; + } + /* folder close */ + fprintf(outfp, "\t</Folder>\n"); +#endif + kml_footer(outfp); + + fclose(outfp); + + return 0; +} diff --git a/src/host/gsmmap/locate.c b/src/host/gsmmap/locate.c new file mode 100644 index 00000000..ed0ac931 --- /dev/null +++ b/src/host/gsmmap/locate.c @@ -0,0 +1,182 @@ +/* Algorithm to locate a destination by distance measurement: + */ + +#include <stdio.h> +#include <errno.h> +#include <math.h> + +#include "geo.h" +#include "locate.h" + +#define CIRCLE_PROBE 30.0 +#define FINETUNE_RADIUS 5.0 + +extern double debug_long, debug_lat, debug_x_scale; +extern FILE *debug_fp; +extern int log_debug; + +static double finetune_x[6], finetune_y[6], finetune_dist[6]; + +int locate_cell(struct probe *probe_first, double *min_x, double *min_y) +{ + struct probe *probe, *min_probe; + int i, test_steps, optimized; + double min_dist, dist, x, y, rad, temp; + double circle_probe, finetune_radius; + + /* convert meters into degrees */ + circle_probe = CIRCLE_PROBE / (EQUATOR_RADIUS * PI / 180.0); + finetune_radius = FINETUNE_RADIUS / (EQUATOR_RADIUS * PI / 180.0); + + if (log_debug) { + fprintf(debug_fp, "<Folder>\n"); + fprintf(debug_fp, "\t<name>Debug Locator</name>\n"); + fprintf(debug_fp, "\t<open>0</open>\n"); + fprintf(debug_fp, "\t<visibility>0</visibility>\n"); + } + + /* get probe of minimum distance */ + min_probe = NULL; + probe = probe_first; + min_dist = 42; + i = 0; + while (probe) { + if (log_debug) { + fprintf(debug_fp, "\t<Placemark>\n"); + fprintf(debug_fp, "\t\t<name>MEAS</name>\n"); + fprintf(debug_fp, "\t\t<visibility>0</visibility>\n"); + fprintf(debug_fp, "\t\t<LineString>\n"); + fprintf(debug_fp, "\t\t\t<tessellate>1</tessellate>\n"); + fprintf(debug_fp, "\t\t\t<coordinates>\n"); + rad = 2.0 * 3.1415927 / 35; + for (i = 0; i < 35; i++) { + x = probe->x + probe->dist * sin(rad * i); + y = probe->y + probe->dist * cos(rad * i); + fprintf(debug_fp, "%.8f,%.8f\n", debug_long + + x * debug_x_scale, debug_lat + y); + } + fprintf(debug_fp, "\t\t\t</coordinates>\n"); + fprintf(debug_fp, "\t\t</LineString>\n"); + fprintf(debug_fp, "\t</Placemark>\n"); + } + + if (!min_probe || probe->dist < min_dist) { + min_probe = probe; + min_dist = probe->dist; + } + probe = probe->next; + i++; + } + + if (i < 3) { + fprintf(stderr, "Need at least 3 points\n"); + return -EINVAL; + } + + /* calculate the number of steps to search for destination point */ + test_steps = 2.0 * 3.1415927 * min_probe->dist / circle_probe; + rad = 2.0 * 3.1415927 / test_steps; + + if (log_debug) { + fprintf(debug_fp, "\t<Placemark>\n"); + fprintf(debug_fp, "\t\t<name>Smallest MEAS</name>\n"); + fprintf(debug_fp, "\t\t<visibility>0</visibility>\n"); + fprintf(debug_fp, "\t\t<LineString>\n"); + fprintf(debug_fp, "\t\t\t<tessellate>1</tessellate>\n"); + fprintf(debug_fp, "\t\t\t<coordinates>\n"); + } + + /* search on a circle for the location of the lowest distance + * to the radius with the greatest distance */ + min_dist = 42; + *min_x = *min_y = 42; + for (i = 0; i < test_steps; i++) { + x = min_probe->x + min_probe->dist * sin(rad * i); + y = min_probe->y + min_probe->dist * cos(rad * i); + if (log_debug) + fprintf(debug_fp, "%.8f,%.8f\n", debug_long + + x * debug_x_scale, debug_lat + y); + /* look for greatest distance */ + dist = 0; + probe = probe_first; + while (probe) { + if (probe != min_probe) { + /* distance to the radius */ + temp = distonplane(probe->x, probe->y, x, y); + temp -= probe->dist; + if (temp < 0) + temp = -temp; + if (temp > dist) + dist = temp; + } + probe = probe->next; + } + if (i == 0 || dist < min_dist) { + min_dist = dist; + *min_x = x; + *min_y = y; + } + } + + if (log_debug) { + fprintf(debug_fp, "\t\t\t</coordinates>\n"); + fprintf(debug_fp, "\t\t</LineString>\n"); + fprintf(debug_fp, "\t</Placemark>\n"); + + fprintf(debug_fp, "\t<Placemark>\n"); + fprintf(debug_fp, "\t\t<name>Finetune</name>\n"); + fprintf(debug_fp, "\t\t<visibility>0</visibility>\n"); + fprintf(debug_fp, "\t\t<LineString>\n"); + fprintf(debug_fp, "\t\t\t<tessellate>1</tessellate>\n"); + fprintf(debug_fp, "\t\t\t<coordinates>\n"); + } + + min_dist = 9999999999.0; +tune_again: + if (log_debug) + fprintf(debug_fp, "%.8f,%.8f\n", debug_long + + *min_x * debug_x_scale, debug_lat + *min_y); + + /* finetune the point */ + rad = 2.0 * 3.1415927 / 6; + for (i = 0; i < 6; i++) { + x = *min_x + finetune_radius * sin(rad * i); + y = *min_y + finetune_radius * cos(rad * i); + /* search for the point with the lowest sum of distances */ + dist = 0; + probe = probe_first; + while (probe) { + /* distance to the radius */ + temp = distonplane(probe->x, probe->y, x, y); + temp -= probe->dist; + if (temp < 0) + temp = -temp; + dist += temp; + probe = probe->next; + } + finetune_dist[i] = dist; + finetune_x[i] = x; + finetune_y[i] = y; + } + + optimized = 0; + for (i = 0; i < 6; i++) { + if (finetune_dist[i] < min_dist) { + min_dist = finetune_dist[i]; + *min_x = finetune_x[i]; + *min_y = finetune_y[i]; + optimized = 1; + } + } + if (optimized) + goto tune_again; + + if (log_debug) { + fprintf(debug_fp, "\t\t\t</coordinates>\n"); + fprintf(debug_fp, "\t\t</LineString>\n"); + fprintf(debug_fp, "\t</Placemark>\n"); + fprintf(debug_fp, "</Folder>\n"); + } + + return 0; +} diff --git a/src/host/gsmmap/locate.h b/src/host/gsmmap/locate.h new file mode 100644 index 00000000..26133452 --- /dev/null +++ b/src/host/gsmmap/locate.h @@ -0,0 +1,8 @@ + +struct probe { + struct probe *next; + double x, y, dist; +}; + +int locate_cell(struct probe *probe_first, double *min_x, double *min_y); + diff --git a/src/host/gsmmap/log.c b/src/host/gsmmap/log.c new file mode 100644 index 00000000..c2db801e --- /dev/null +++ b/src/host/gsmmap/log.c @@ -0,0 +1,377 @@ +/* Conversion of logged cells to KML file */ + +/* (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <osmocom/bb/common/osmocom_data.h> + +#include "log.h" + +extern struct power power; +extern struct sysinfo sysinfo; +extern struct node_power *node_power_first; +extern struct node_power **node_power_last_p; +extern struct node_mcc *node_mcc_first; + +struct node_mcc *get_node_mcc(uint16_t mcc) +{ + struct node_mcc *node_mcc; + struct node_mcc **node_mcc_p = &node_mcc_first; + +//printf("add mcc %d\n", mcc); + while (*node_mcc_p) { + /* found in list */ + if ((*node_mcc_p)->mcc == mcc) + return *node_mcc_p; + /* insert into list */ + if ((*node_mcc_p)->mcc > mcc) + break; + node_mcc_p = &((*node_mcc_p)->next); + } + +//printf("new mcc %d\n", mcc); + /* append or insert to list */ + node_mcc = calloc(1, sizeof(struct node_mcc)); + if (!node_mcc) + return NULL; + node_mcc->mcc = mcc; + node_mcc->next = *node_mcc_p; + *node_mcc_p = node_mcc; + return node_mcc; +} + +struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc) +{ + struct node_mnc *node_mnc; + struct node_mnc **node_mnc_p = &mcc->mnc; + + while (*node_mnc_p) { + /* found in list */ + if ((*node_mnc_p)->mnc == mnc) + return *node_mnc_p; + /* insert into list */ + if ((*node_mnc_p)->mnc > mnc) + break; + node_mnc_p = &((*node_mnc_p)->next); + } + + /* append or insert to list */ + node_mnc = calloc(1, sizeof(struct node_mnc)); + if (!node_mnc) + return NULL; + node_mnc->mnc = mnc; + node_mnc->next = *node_mnc_p; + *node_mnc_p = node_mnc; + return node_mnc; +} + +struct node_lac *get_node_lac(struct node_mnc *mnc, uint16_t lac) +{ + struct node_lac *node_lac; + struct node_lac **node_lac_p = &mnc->lac; + + while (*node_lac_p) { + /* found in list */ + if ((*node_lac_p)->lac == lac) + return *node_lac_p; + /* insert into list */ + if ((*node_lac_p)->lac > lac) + break; + node_lac_p = &((*node_lac_p)->next); + } + + /* append or insert to list */ + node_lac = calloc(1, sizeof(struct node_lac)); + if (!node_lac) + return NULL; + node_lac->lac = lac; + node_lac->next = *node_lac_p; + *node_lac_p = node_lac; + return node_lac; +} + +struct node_cell *get_node_cell(struct node_lac *lac, uint16_t cellid) +{ + struct node_cell *node_cell; + struct node_cell **node_cell_p = &lac->cell; + + while (*node_cell_p) { + /* found in list */ + if ((*node_cell_p)->cellid == cellid) + return *node_cell_p; + /* insert into list */ + if ((*node_cell_p)->cellid > cellid) + break; + node_cell_p = &((*node_cell_p)->next); + } + + /* append or insert to list */ + node_cell = calloc(1, sizeof(struct node_cell)); + if (!node_cell) + return NULL; + node_cell->meas_last_p = &node_cell->meas; + node_cell->cellid = cellid; + node_cell->next = *node_cell_p; + *node_cell_p = node_cell; + return node_cell; +} + +struct node_meas *add_node_meas(struct node_cell *cell) +{ + struct node_meas *node_meas; + + /* append to list */ + node_meas = calloc(1, sizeof(struct node_meas)); + if (!node_meas) + return NULL; + node_meas->gmt = sysinfo.gmt; + node_meas->rxlev = sysinfo.rxlev; + if (sysinfo.ta_valid) { + node_meas->ta_valid = 1; + node_meas->ta = sysinfo.ta; + } + if (sysinfo.gps_valid) { + node_meas->gps_valid = 1; + node_meas->longitude = sysinfo.longitude; + node_meas->latitude = sysinfo.latitude; + } + *cell->meas_last_p = node_meas; + cell->meas_last_p = &node_meas->next; + return node_meas; +} + +/* read "<ncc>,<bcc>" */ +static void read_log_bsic(char *buffer) +{ + char *p; + uint8_t bsic; + + /* skip first spaces */ + while (*buffer == ' ') + buffer++; + + /* read ncc */ + p = buffer; + while (*p > ' ' && *p != ',') + p++; + if (*p == '\0') + return; /* no value */ + *p++ = '\0'; + bsic = atoi(buffer) << 3; + buffer = p; + + /* read latitude */ + bsic |= atoi(buffer); + + sysinfo.bsic = bsic; +} + +/* read "<longitude> <latitude>" */ +static void read_log_pos(char *buffer, double *longitude, double *latitude, + uint8_t *valid) +{ + char *p; + + /* skip first spaces */ + while (*buffer == ' ') + buffer++; + + /* read longitude */ + p = buffer; + while (*p > ' ') + p++; + if (*p == '\0') + return; /* no value after longitude */ + *p++ = '\0'; + *longitude = atof(buffer); + buffer = p; + + /* skip second spaces */ + while (*buffer == ' ') + buffer++; + + /* read latitude */ + *latitude = atof(buffer); + + *valid = 1; +} + +/* read "<arfcn> <value> <next value> ...." */ +static void read_log_power(char *buffer) +{ + char *p; + int arfcn; + + /* skip first spaces */ + while (*buffer == ' ') + buffer++; + + /* read arfcn */ + p = buffer; + while (*p > ' ') + p++; + if (*p == '\0') + return; /* no value after arfcn */ + *p++ = '\0'; + arfcn = atoi(buffer); + buffer = p; + + while (*buffer) { + /* wrong arfcn */ + if (arfcn < 0 || arfcn > 1023) + break; + /* skip spaces */ + while (*buffer == ' ') + buffer++; + /* get value */ + p = buffer; + while (*p > ' ') + p++; + /* last value */ + if (*p == '\0') { + power.rxlev[arfcn] = atoi(buffer); + break; + } + *p++ = '\0'; + power.rxlev[arfcn] = atoi(buffer); + arfcn++; + buffer = p; + } +} + +/* read "xx xx xx xx xx...." */ +static void read_log_si(char *buffer, uint8_t *data) +{ + uint8_t si[23]; + int i; + +// printf("%s ", buffer); + for (i = 0; i < 23; i++) { + while (*buffer == ' ') + buffer++; + if (*buffer >= '0' && *buffer <= '9') + si[i] = (*buffer - '0') << 4; + else if (*buffer >= 'a' && *buffer <= 'f') + si[i] = (*buffer - 'a' + 10) << 4; + else if (*buffer >= 'A' && *buffer <= 'F') + si[i] = (*buffer - 'A' + 10) << 4; + else + break; + buffer++; + if (*buffer >= '0' && *buffer <= '9') + si[i] += *buffer - '0'; + else if (*buffer >= 'a' && *buffer <= 'f') + si[i] += *buffer - 'a' + 10; + else if (*buffer >= 'A' && *buffer <= 'F') + si[i] += *buffer - 'A' + 10; + else + break; + buffer++; +// printf("%02x ", si[i]); + } +// printf("\n"); + + if (i == 23) + memcpy(data, si, 23); +} + +/* read next record from log file */ +int read_log(FILE *infp) +{ + static int type = LOG_TYPE_NONE, ret; + char buffer[256]; + + memset(&sysinfo, 0, sizeof(sysinfo)); + memset(&power, 0, sizeof(power)); + memset(&power.rxlev, -128, sizeof(power.rxlev)); + + if (feof(infp)) + return LOG_TYPE_NONE; + + while (fgets(buffer, sizeof(buffer), infp)) { + buffer[sizeof(buffer) - 1] = 0; + if (buffer[0]) + buffer[strlen(buffer) - 1] = '\0'; + if (buffer[0] == '[') { + if (!strcmp(buffer, "[sysinfo]")) { + ret = type; + type = LOG_TYPE_SYSINFO; + if (ret != LOG_TYPE_NONE) + return ret; + } else + if (!strcmp(buffer, "[power]")) { + ret = type; + type = LOG_TYPE_POWER; + if (ret != LOG_TYPE_NONE) + return ret; + } else { + type = LOG_TYPE_NONE; + } + continue; + } + switch (type) { + case LOG_TYPE_SYSINFO: + if (!strncmp(buffer, "arfcn ", 6)) + sysinfo.arfcn = atoi(buffer + 6); + else if (!strncmp(buffer, "si1 ", 4)) + read_log_si(buffer + 4, sysinfo.si1); + else if (!strncmp(buffer, "si2 ", 4)) + read_log_si(buffer + 4, sysinfo.si2); + else if (!strncmp(buffer, "si2bis ", 7)) + read_log_si(buffer + 7, sysinfo.si2bis); + else if (!strncmp(buffer, "si2ter ", 7)) + read_log_si(buffer + 7, sysinfo.si2ter); + else if (!strncmp(buffer, "si3 ", 4)) + read_log_si(buffer + 4, sysinfo.si3); + else if (!strncmp(buffer, "si4 ", 4)) + read_log_si(buffer + 4, sysinfo.si4); + else if (!strncmp(buffer, "time ", 5)) + sysinfo.gmt = strtoul(buffer + 5, NULL, 0); + else if (!strncmp(buffer, "position ", 9)) + read_log_pos(buffer + 9, &sysinfo.longitude, + &sysinfo.latitude, &sysinfo.gps_valid); + else if (!strncmp(buffer, "rxlev ", 5)) + sysinfo.rxlev = + strtoul(buffer + 5, NULL, 0); + else if (!strncmp(buffer, "bsic ", 5)) + read_log_bsic(buffer + 5); + else if (!strncmp(buffer, "ta ", 3)) { + sysinfo.ta_valid = 1; + sysinfo.ta = atoi(buffer + 3); + } + break; + case LOG_TYPE_POWER: + if (!strncmp(buffer, "arfcn ", 6)) + read_log_power(buffer + 6); + else if (!strncmp(buffer, "time ", 5)) + power.gmt = strtoul(buffer + 5, NULL, 0); + else if (!strncmp(buffer, "position ", 9)) + read_log_pos(buffer + 9, &power.longitude, + &power.latitude, &sysinfo.gps_valid); + break; + } + } + + return type; +} + diff --git a/src/host/gsmmap/log.h b/src/host/gsmmap/log.h new file mode 100644 index 00000000..d1520101 --- /dev/null +++ b/src/host/gsmmap/log.h @@ -0,0 +1,80 @@ + +enum { + LOG_TYPE_NONE = 0, + LOG_TYPE_SYSINFO, + LOG_TYPE_POWER, +}; + +struct power { + uint8_t gps_valid; + double longitude, latitude; + time_t gmt; + int8_t rxlev[1024]; +}; + +struct node_power { + struct node_power *next; + struct power power; +}; + +struct node_mcc { + struct node_mcc *next; + uint16_t mcc; + struct node_mnc *mnc; +}; + +struct node_mnc { + struct node_mnc *next; + uint16_t mnc; + struct node_lac *lac; +}; + +struct node_lac { + struct node_lac *next; + uint16_t lac; + struct node_cell *cell; +}; + +struct sysinfo { + uint16_t arfcn; + int8_t rxlev; + uint8_t bsic; + uint8_t gps_valid; + double longitude, latitude; + time_t gmt; + uint8_t si1[23]; + uint8_t si2[23]; + uint8_t si2bis[23]; + uint8_t si2ter[23]; + uint8_t si3[23]; + uint8_t si4[23]; + uint8_t ta_valid; + uint8_t ta; +}; + +struct node_cell { + struct node_cell *next; + uint16_t cellid; + uint8_t content; /* indicates, if sysinfo is already applied */ + struct node_meas *meas, **meas_last_p; + struct sysinfo sysinfo; + struct gsm48_sysinfo s; +}; + +struct node_meas { + struct node_meas *next; + time_t gmt; + int8_t rxlev; + uint8_t gps_valid; + double longitude, latitude; + uint8_t ta_valid; + uint8_t ta; +}; + +struct node_mcc *get_node_mcc(uint16_t mcc); +struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc); +struct node_lac *get_node_lac(struct node_mnc *mnc, uint16_t lac); +struct node_cell *get_node_cell(struct node_lac *lac, uint16_t cellid); +struct node_meas *add_node_meas(struct node_cell *cell); +int read_log(FILE *infp); + diff --git a/src/host/layer23/.gitignore b/src/host/layer23/.gitignore new file mode 100644 index 00000000..a817b9ba --- /dev/null +++ b/src/host/layer23/.gitignore @@ -0,0 +1,33 @@ +# autoreconf by-products +*.in + +aclocal.m4 +autom4te.cache/ +config.h.in +configure +depcomp +install-sh +missing + +# configure by-products +.deps +Makefile + +config.h +config.log +config.status + +# build by-products +*.o +*.a + +# various +*.sw? +*.deps + +# final executables +src/misc/bcch_scan +src/misc/cell_log +src/misc/echo_test +src/misc/layer23 +src/mobile/mobile diff --git a/COPYING b/src/host/layer23/COPYING index d511905c..d511905c 100644 --- a/COPYING +++ b/src/host/layer23/COPYING diff --git a/src/host/layer23/Makefile.am b/src/host/layer23/Makefile.am new file mode 100644 index 00000000..bc3910fa --- /dev/null +++ b/src/host/layer23/Makefile.am @@ -0,0 +1,3 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 + +SUBDIRS = include src diff --git a/src/host/layer23/README b/src/host/layer23/README new file mode 100644 index 00000000..dd598234 --- /dev/null +++ b/src/host/layer23/README @@ -0,0 +1,42 @@ += OsmocomBB layer23 architecture = + +layer23 is an (incomplete) MS-side implementation of the L2 and L3 GSM +protocols as described in GSM TS 04.06, 04.08 and others. + +== Interfaces == + +L1 (on the phone) uses the L1CTL protocol to talk with layer23 (on the PC). + +L2 (inside layer23) uses the RSLms protocol to talk with the L3 (inside layer23) + + +=== RSLms === + +RSLms is modeled after the GSM TS 08.58 Radio Subsystem Link protocol. Despite +being designed for the network side, RSL seems a good match for the L2/L3 +interface inside a MS, too. + +At least the RLL (Radio Link Layer) part of RSL is 100% as applicable to the MS +side as it is for the ntwork side. + +==== Lower interface (L2 to RSLms) ==== + +Layer2 calls rslms_sendmsg() with a msgb that has the msgb->l2h pointing to a +RSL header (struct abis_rsl_common_hdr). + +==== Upper interface (L3 to RSLms) ==== + +Layer3 calls rslms_recvmsg() with a msgb that has the msgb->l2h pointing to a +RSL header (struct abis_rsl_common_hdr). + +There are utility functions like rslms_tx_rll_req() and rslms_tx_rsll_req_l3() +for creating msgb's with the apropriate RSL/RLL headers. + + +=== LAPDm === + +LAPDm is the GSM TS 04.06 protocol + +The lower interface (to L1) is using L1CTL + +The upper interface (to L3) is using RSLms diff --git a/src/host/layer23/configure.ac b/src/host/layer23/configure.ac new file mode 100644 index 00000000..1be98eec --- /dev/null +++ b/src/host/layer23/configure.ac @@ -0,0 +1,35 @@ +dnl Process this file with autoconf to produce a configure script +AC_INIT + +AM_INIT_AUTOMAKE(layer23, 0.0.0) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_RANLIB + +dnl checks for libraries +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) +PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty) + +dnl checks for header files +AC_HEADER_STDC + +dnl Checks for typedefs, structures and compiler characteristics + +AC_OUTPUT( + src/Makefile + src/common/Makefile + src/misc/Makefile + src/mobile/Makefile + include/Makefile + include/osmocom/Makefile + include/osmocom/bb/Makefile + include/osmocom/bb/common/Makefile + include/osmocom/bb/misc/Makefile + include/osmocom/bb/mobile/Makefile + Makefile) diff --git a/src/host/layer23/include/Makefile.am b/src/host/layer23/include/Makefile.am new file mode 100644 index 00000000..297ece97 --- /dev/null +++ b/src/host/layer23/include/Makefile.am @@ -0,0 +1,2 @@ +noinst_HEADERS = l1ctl_proto.h +SUBDIRS = osmocom diff --git a/src/host/layer23/include/l1ctl_proto.h b/src/host/layer23/include/l1ctl_proto.h new file mode 120000 index 00000000..f12ba71e --- /dev/null +++ b/src/host/layer23/include/l1ctl_proto.h @@ -0,0 +1 @@ +../../../../include/l1ctl_proto.h
\ No newline at end of file diff --git a/src/host/layer23/include/osmocom/Makefile.am b/src/host/layer23/include/osmocom/Makefile.am new file mode 100644 index 00000000..5adf9df5 --- /dev/null +++ b/src/host/layer23/include/osmocom/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = bb diff --git a/src/host/layer23/include/osmocom/bb/Makefile.am b/src/host/layer23/include/osmocom/bb/Makefile.am new file mode 100644 index 00000000..58a5f7fb --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = common misc mobile diff --git a/src/host/layer23/include/osmocom/bb/common/Makefile.am b/src/host/layer23/include/osmocom/bb/common/Makefile.am new file mode 100644 index 00000000..26e63cfe --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/Makefile.am @@ -0,0 +1,2 @@ +noinst_HEADERS = l1ctl.h l1l2_interface.h l23_app.h lapdm.h logging.h \ + networks.h gps.h sysinfo.h osmocom_data.h diff --git a/src/host/layer23/include/osmocom/bb/common/gps.h b/src/host/layer23/include/osmocom/bb/common/gps.h new file mode 100644 index 00000000..e45cbc5d --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/gps.h @@ -0,0 +1,40 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +struct gps { + /* GPS device */ + uint8_t enable; + char device[32]; + uint32_t baud; + + /* current data */ + uint8_t valid; /* we have a fix */ + time_t gmt; /* GMT time when position was received */ + double latitude, longitude; +}; + +extern struct gps gps; + +int gps_open(void); +void gps_close(void); +void gps_init(void); + + diff --git a/src/host/layer23/include/osmocom/bb/common/l1ctl.h b/src/host/layer23/include/osmocom/bb/common/l1ctl.h new file mode 100644 index 00000000..64abf9c4 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/l1ctl.h @@ -0,0 +1,65 @@ +#ifndef osmocom_l1ctl_h +#define osmocom_l1ctl_h + +#include <osmocore/msgb.h> +#include <osmocom/bb/common/osmocom_data.h> + +struct osmocom_ms; + +/* Receive incoming data from L1 using L1CTL format */ +int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg); + +/* Transmit L1CTL_DATA_REQ */ +int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg, uint8_t chan_nr, + uint8_t link_id); + +/* Transmit L1CTL_PARAM_REQ */ +int l1ctl_tx_param_req(struct osmocom_ms *ms, uint8_t ta, uint8_t tx_power); + +int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t algo, uint8_t *key, + uint8_t len); + +/* Transmit L1CTL_RACH_REQ */ +int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset, + uint8_t combined); + +/* Transmit L1CTL_DM_EST_REQ */ +int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, + uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode); +int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, + uint16_t *ma, uint8_t ma_len, uint8_t chan_nr, uint8_t tsc, + uint8_t tch_mode); + +/* Transmit L1CTL_DM_FREQ_REQ */ +int l1ctl_tx_dm_freq_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, + uint8_t tsc, uint16_t fn); +int l1ctl_tx_dm_freq_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, + uint16_t *ma, uint8_t ma_len, uint8_t tsc, uint16_t fn); + +/* Transmit L1CTL_DM_REL_REQ */ +int l1ctl_tx_dm_rel_req(struct osmocom_ms *ms); + +/* Transmit FBSB_REQ */ +int l1ctl_tx_fbsb_req(struct osmocom_ms *ms, uint16_t arfcn, + uint8_t flags, uint16_t timeout, uint8_t sync_info_idx, + uint8_t ccch_mode); + +/* Transmit CCCH_MODE_REQ */ +int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode); + +/* Transmit TCH_MODE_REQ */ +int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode); + +/* Transmit ECHO_REQ */ +int l1ctl_tx_echo_req(struct osmocom_ms *ms, unsigned int len); + +/* Transmit L1CTL_RESET_REQ */ +int l1ctl_tx_reset_req(struct osmocom_ms *ms, uint8_t type); + +/* Transmit L1CTL_PM_REQ */ +int l1ctl_tx_pm_req_range(struct osmocom_ms *ms, uint16_t arfcn_from, + uint16_t arfcm_to); + +int l1ctl_tx_sim_req(struct osmocom_ms *ms, uint8_t *data, uint16_t length); + +#endif diff --git a/src/host/layer23/include/osmocom/bb/common/l1l2_interface.h b/src/host/layer23/include/osmocom/bb/common/l1l2_interface.h new file mode 100644 index 00000000..41403d87 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/l1l2_interface.h @@ -0,0 +1,8 @@ +#ifndef _L1L2_INTERFACE_H +#define _L1L2_INTERFACE_H + +int layer2_open(struct osmocom_ms *ms, const char *socket_path); +int layer2_close(struct osmocom_ms *ms); +int osmo_send_l1(struct osmocom_ms *ms, struct msgb *msg); + +#endif /* _L1L2_INTERFACE_H */ diff --git a/src/host/layer23/include/osmocom/bb/common/l23_app.h b/src/host/layer23/include/osmocom/bb/common/l23_app.h new file mode 100644 index 00000000..e4c5d55e --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/l23_app.h @@ -0,0 +1,35 @@ +#ifndef _L23_APP_H +#define _L23_APP_H + +struct option; + +/* Options supported by the l23 app */ +enum { + L23_OPT_SAP = 1, + L23_OPT_ARFCN = 2, + L23_OPT_TAP = 4, + L23_OPT_VTY = 8, + L23_OPT_DBG = 16, +}; + +/* initialization, called once when starting the app, before entering + * select loop */ +extern int l23_app_init(struct osmocom_ms *ms); +extern int (*l23_app_work) (struct osmocom_ms *ms); +extern int (*l23_app_exit) (struct osmocom_ms *ms); + +/* configuration options */ +struct l23_app_info { + const char *copyright; + const char *contribution; + + char *getopt_string; + int (*cfg_supported)(); + int (*cfg_print_help)(); + int (*cfg_getopt_opt)(struct option **options); + int (*cfg_handle_opt)(int c,const char *optarg); +}; + +extern struct l23_app_info *l23_app_info(); + +#endif /* _L23_APP_H */ diff --git a/src/host/layer23/include/osmocom/bb/common/lapdm.h b/src/host/layer23/include/osmocom/bb/common/lapdm.h new file mode 100644 index 00000000..de954fb1 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/lapdm.h @@ -0,0 +1,100 @@ +#ifndef _OSMOCOM_LAPDM_H +#define _OSMOCOM_LAPDM_H + +#include <stdint.h> + +#include <osmocore/timer.h> +#include <osmocore/msgb.h> + +#include <l1ctl_proto.h> + +enum lapdm_state { + LAPDm_STATE_NULL = 0, + LAPDm_STATE_IDLE, + LAPDm_STATE_SABM_SENT, + LAPDm_STATE_MF_EST, + LAPDm_STATE_TIMER_RECOV, + LAPDm_STATE_DISC_SENT, +}; + +struct lapdm_entity; +struct osmocom_ms; + +struct lapdm_msg_ctx { + struct lapdm_datalink *dl; + int lapdm_fmt; + uint8_t n201; + uint8_t chan_nr; + uint8_t link_id; + uint8_t addr; + uint8_t ctrl; + uint8_t ta_ind; + uint8_t tx_power_ind; +}; + +/* TS 04.06 / Section 3.5.2 */ +struct lapdm_datalink { + uint8_t V_send; /* seq nr of next I frame to be transmitted */ + uint8_t V_ack; /* last frame ACKed by peer */ + uint8_t N_send; /* ? set to V_send at Tx time*/ + uint8_t V_recv; /* seq nr of next I frame expected to be received */ + uint8_t N_recv; /* expected send seq nr of the next received I frame */ + uint32_t state; + int seq_err_cond; /* condition of sequence error */ + uint8_t own_busy, peer_busy; + struct timer_list t200; + uint8_t retrans_ctr; + struct llist_head send_queue; /* frames from L3 */ + struct msgb *send_buffer; /* current frame transmitting */ + int send_out; /* how much was sent from send_buffer */ + uint8_t tx_hist[8][200]; /* tx history buffer */ + int tx_length[8]; /* length in history buffer */ + struct llist_head tx_queue; /* frames to L1 */ + struct lapdm_msg_ctx mctx; /* context of established connection */ + struct msgb *rcv_buffer; /* buffer to assemble the received message */ + + struct lapdm_entity *entity; +}; + +enum lapdm_dl_sapi { + DL_SAPI0 = 0, + DL_SAPI3 = 1, + _NR_DL_SAPI +}; + +struct lapdm_entity { + struct lapdm_datalink datalink[_NR_DL_SAPI]; + int last_tx_dequeue; /* last entity that was dequeued */ + int tx_pending; /* currently a pending frame not confirmed by L1 */ + struct osmocom_ms *ms; +}; + +const char *get_rsl_name(int value); +extern const char *lapdm_state_names[]; + +/* initialize a LAPDm entity */ +void lapdm_init(struct lapdm_entity *le, struct osmocom_ms *ms); + +/* deinitialize a LAPDm entity */ +void lapdm_exit(struct lapdm_entity *le); + +/* input into layer2 (from layer 1) */ +int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, struct l1ctl_info_dl *l1i); +int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le); + +/* L1 confirms channel request */ +int l2_ph_chan_conf(struct msgb *msg, struct osmocom_ms *ms, + struct l1ctl_info_dl *dl); + +/* input into layer2 (from layer 3) */ +int rslms_recvmsg(struct msgb *msg, struct osmocom_ms *ms); + +/* sending messages up from L2 to L3 */ +int rslms_sendmsg(struct msgb *msg, struct osmocom_ms *ms); + +typedef int (*osmol2_cb_t)(struct msgb *msg, struct osmocom_ms *ms); + +/* register message handler for messages that are sent from L2->L3 */ +int osmol2_register_handler(struct osmocom_ms *ms, osmol2_cb_t cb); + +#endif /* _OSMOCOM_LAPDM_H */ diff --git a/src/host/layer23/include/osmocom/bb/common/logging.h b/src/host/layer23/include/osmocom/bb/common/logging.h new file mode 100644 index 00000000..eb9f4821 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/logging.h @@ -0,0 +1,28 @@ +#ifndef _LOGGING_H +#define _LOGGING_H + +#define DEBUG +#include <osmocore/logging.h> + +enum { + DRSL, + DRR, + DPLMN, + DCS, + DMM, + DCC, + DSMS, + DMNCC, + DMEAS, + DPAG, + DLAPDM, + DL1C, + DSAP, + DSUM, + DSIM, + DGPS, +}; + +extern const struct log_info log_info; + +#endif /* _LOGGING_H */ diff --git a/src/host/layer23/include/osmocom/bb/common/networks.h b/src/host/layer23/include/osmocom/bb/common/networks.h new file mode 100644 index 00000000..e8c1b18e --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/networks.h @@ -0,0 +1,22 @@ +#ifndef _NETWORKS_H +#define _NETWORKS_H + +struct gsm_networks { + uint16_t mcc; + int16_t mnc; + const char *name; +}; + +int gsm_match_mcc(uint16_t mcc, char *imsi); +int gsm_match_mnc(uint16_t mcc, uint8_t mnc, char *imsi); +const char *gsm_print_mcc(uint16_t mcc); +const char *gsm_print_mnc(uint16_t mcc); +const char *gsm_get_mcc(uint16_t mcc); +const char *gsm_get_mnc(uint16_t mcc, uint16_t mnc); +const char *gsm_imsi_mcc(char *imsi); +const char *gsm_imsi_mnc(char *imsi); +const uint16_t gsm_input_mcc(char *string); +const uint16_t gsm_input_mnc(char *string); + +#endif /* _NETWORKS_H */ + diff --git a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h new file mode 100644 index 00000000..749c144c --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h @@ -0,0 +1,114 @@ +#ifndef osmocom_data_h +#define osmocom_data_h + +#include <osmocore/select.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/write_queue.h> + +struct osmocom_ms; + + /* FIXME no 'mobile' specific stuff should be here */ +#include <osmocom/bb/mobile/support.h> +#include <osmocom/bb/mobile/settings.h> +#include <osmocom/bb/mobile/subscriber.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/common/sap_interface.h> +#include <osmocom/bb/mobile/gsm48_rr.h> +#include <osmocom/bb/common/sysinfo.h> +#include <osmocom/bb/mobile/gsm322.h> +#include <osmocom/bb/mobile/gsm48_mm.h> +#include <osmocom/bb/mobile/gsm48_cc.h> +#include <osmocom/bb/common/sim.h> + +/* A layer2 entity */ +struct osmol2_entity { + struct lapdm_entity lapdm_dcch; + struct lapdm_entity lapdm_acch; + osmol2_cb_t msg_handler; +}; + +struct osmosap_entity { + osmosap_cb_t msg_handler; +}; + +/* RX measurement statistics */ +struct rx_meas_stat { + uint32_t last_fn; + + /* cumulated values of current cell from SACCH dl */ + uint32_t frames; + uint32_t snr; + uint32_t berr; + uint32_t rxlev; + + /* counters loss criterion */ + int16_t dsc, ds_fail; + int16_t s, rl_fail; +}; + +/* One Mobilestation for osmocom */ +struct osmocom_ms { + struct llist_head entity; + char name[32]; + struct write_queue l2_wq, sap_wq; + uint16_t test_arfcn; + + uint8_t deleting, shutdown, started; + struct gsm_support support; + struct gsm_settings settings; + struct gsm_subscriber subscr; + struct gsm_sim sim; + struct osmol2_entity l2_entity; + struct osmosap_entity sap_entity; + struct rx_meas_stat meas; + struct gsm48_rrlayer rrlayer; + struct gsm322_plmn plmn; + struct gsm322_cellsel cellsel; + struct gsm48_mmlayer mmlayer; + struct gsm48_cclayer cclayer; + struct llist_head trans_list; +}; + +enum osmobb_sig_subsys { + SS_L1CTL, + SS_GLOBAL, +}; + +enum osmobb_l1ctl_sig { + S_L1CTL_FBSB_ERR, + S_L1CTL_FBSB_RESP, + S_L1CTL_RESET, + S_L1CTL_PM_RES, + S_L1CTL_PM_DONE, + S_L1CTL_CCCH_MODE_CONF, + S_L1CTL_TCH_MODE_CONF, + S_L1CTL_LOSS_IND, +}; + +enum osmobb_global_sig { + S_GLOBAL_SHUTDOWN, +}; + +struct osmobb_fbsb_res { + struct osmocom_ms *ms; + int8_t snr; + uint8_t bsic; +}; + +struct osmobb_meas_res { + struct osmocom_ms *ms; + uint16_t band_arfcn; + uint8_t rx_lev; +}; + +struct osmobb_ccch_mode_conf { + struct osmocom_ms *ms; + uint8_t ccch_mode; +}; + +struct osmobb_tch_mode_conf { + struct osmocom_ms *ms; + uint8_t tch_mode; +}; + +#endif diff --git a/src/host/layer23/include/osmocom/bb/common/sap_interface.h b/src/host/layer23/include/osmocom/bb/common/sap_interface.h new file mode 100644 index 00000000..f2f577a7 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/sap_interface.h @@ -0,0 +1,11 @@ +#ifndef _SAP_INTERFACE_H +#define _SAP_INTERFACE_H + +typedef int (*osmosap_cb_t)(struct msgb *msg, struct osmocom_ms *ms); + +int sap_open(struct osmocom_ms *ms, const char *socket_path); +int sap_close(struct osmocom_ms *ms); +int osmosap_send(struct osmocom_ms *ms, struct msgb *msg); +int osmosap_register_handler(struct osmocom_ms *ms, osmosap_cb_t cb); + +#endif /* _SAP_INTERFACE_H */ diff --git a/src/host/layer23/include/osmocom/bb/common/sim.h b/src/host/layer23/include/osmocom/bb/common/sim.h new file mode 100644 index 00000000..a676b92b --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/sim.h @@ -0,0 +1,274 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +/* 9.2 commands */ +#define GSM1111_CLASS_GSM 0xa0 +#define GSM1111_INST_SELECT 0xa4 +#define GSM1111_INST_STATUS 0xf2 +#define GSM1111_INST_READ_BINARY 0xb0 +#define GSM1111_INST_UPDATE_BINARY 0xd6 +#define GSM1111_INST_READ_RECORD 0xb2 +#define GSM1111_INST_UPDATE_RECORD 0xdc +#define GSM1111_INST_SEEK 0xa2 +#define GSM1111_INST_INCREASE 0x32 +#define GSM1111_INST_VERIFY_CHV 0x20 +#define GSM1111_INST_CHANGE_CHV 0x24 +#define GSM1111_INST_DISABLE_CHV 0x26 +#define GSM1111_INST_ENABLE_CHV 0x28 +#define GSM1111_INST_UNBLOCK_CHV 0x2c +#define GSM1111_INST_INVALIDATE 0x04 +#define GSM1111_INST_REHABLILITATE 0x44 +#define GSM1111_INST_RUN_GSM_ALGO 0x88 +#define GSM1111_INST_SLEEP 0xfa +#define GSM1111_INST_GET_RESPONSE 0xc0 +#define GSM1111_INST_TERMINAL_PROFILE 0x10 +#define GSM1111_INST_ENVELOPE 0xc2 +#define GSM1111_INST_FETCH 0x12 +#define GSM1111_INST_TERMINAL_RESPONSE 0x14 + +/* 9.3 access conditions */ +#define GSM1111_ACC_ALWAYS 0x0 +#define GSM1111_ACC_CHV1 0x1 +#define GSM1111_ACC_CHV2 0x2 +#define GSM1111_ACC_RFU 0x3 +#define GSM1111_ACC_NEW 0xf +/* others are ADM */ + +/* 9.3 type of file */ +#define GSM1111_TOF_RFU 0x00 +#define GSM1111_TOF_MF 0x01 +#define GSM1111_TOF_DF 0x02 +#define GSM1111_TOF_EF 0x04 + +/* 9.3 struct of file */ +#define GSM1111_SOF_TRANSPARENT 0x00 +#define GSM1111_SOF_LINEAR 0x01 +#define GSM1111_SOF_CYCLIC 0x03 + +/* 9.4 status */ +#define GSM1111_STAT_NORMAL 0x90 +#define GSM1111_STAT_PROACTIVE 0x91 +#define GSM1111_STAT_DL_ERROR 0x9e +#define GSM1111_STAT_RESPONSE 0x9f +#define GSM1111_STAT_RESPONSE_TOO 0x61 +#define GSM1111_STAT_APP_TK_BUSY 0x93 +#define GSM1111_STAT_MEM_PROBLEM 0x92 +#define GSM1111_STAT_REFERENCING 0x94 +#define GSM1111_STAT_SECURITY 0x98 +#define GSM1111_STAT_INCORR_P3 0x67 +#define GSM1111_STAT_INCORR_P1_P2 0x6b +#define GSM1111_STAT_UKN_INST 0x6d +#define GSM1111_STAT_WRONG_CLASS 0x6e +#define GSM1111_STAT_TECH_PROBLEM 0x6f + +/* 9.4.4 Referencing management SW2 */ +#define GSM1111_REF_NO_EF 0x00 +#define GSM1111_REF_OUT_OF_RANGE 0x02 +#define GSM1111_REF_FILE_NOT_FOUND 0x04 +#define GSM1111_REF_FILE_INCONSI 0x08 + +/* 9.4.5 Security management SW2 */ +#define GSM1111_SEC_NO_CHV 0x02 +#define GSM1111_SEC_NO_ACCESS 0x04 +#define GSM1111_SEC_CONTRA_CHV 0x08 +#define GSM1111_SEC_CONTRA_INVAL 0x10 +#define GSM1111_SEC_BLOCKED 0x40 +#define GSM1111_SEC_MAX_VALUE 0x50 + +/* messages from application to sim client */ +enum { + /* requests */ + SIM_JOB_READ_BINARY, + SIM_JOB_UPDATE_BINARY, + SIM_JOB_READ_RECORD, + SIM_JOB_UPDATE_RECORD, + SIM_JOB_SEEK_RECORD, + SIM_JOB_INCREASE, + SIM_JOB_INVALIDATE, + SIM_JOB_REHABILITATE, + SIM_JOB_RUN_GSM_ALGO, + SIM_JOB_PIN1_UNLOCK, + SIM_JOB_PIN1_CHANGE, + SIM_JOB_PIN1_DISABLE, + SIM_JOB_PIN1_ENABLE, + SIM_JOB_PIN1_UNBLOCK, + SIM_JOB_PIN2_UNLOCK, + SIM_JOB_PIN2_CHANGE, + SIM_JOB_PIN2_UNBLOCK, + + /* results */ + SIM_JOB_OK, + SIM_JOB_ERROR, +}; + +/* messages from sim client to application */ +#define SIM_JOB_OK 0 +#define SIM_JOB_ERROR 1 + +/* error causes */ +#define SIM_CAUSE_NO_SIM 0 /* no SIM present, if detectable */ +#define SIM_CAUSE_SIM_ERROR 1 /* any error while reading SIM */ +#define SIM_CAUSE_REQUEST_ERROR 2 /* error in request */ +#define SIM_CAUSE_PIN1_REQUIRED 3 /* CHV1 is required for access */ +#define SIM_CAUSE_PIN2_REQUIRED 4 /* CHV2 is required for access */ +#define SIM_CAUSE_PIN1_BLOCKED 5 /* CHV1 was entered too many times */ +#define SIM_CAUSE_PIN2_BLOCKED 6 /* CHV2 was entered too many times */ +#define SIM_CAUSE_PUC_BLOCKED 7 /* unblock entered too many times */ + +/* job states */ +enum { + SIM_JST_IDLE = 0, + SIM_JST_SELECT_MFDF, /* SELECT sent */ + SIM_JST_SELECT_MFDF_RESP, /* GET RESPONSE sent */ + SIM_JST_SELECT_EF, /* SELECT sent */ + SIM_JST_SELECT_EF_RESP, /* GET RESPONSE sent */ + SIM_JST_WAIT_FILE, /* file command sent */ + SIM_JST_RUN_GSM_ALGO, /* wait for algorithm to process */ + SIM_JST_RUN_GSM_ALGO_RESP, /* wait for response */ + SIM_JST_PIN1_UNLOCK, + SIM_JST_PIN1_CHANGE, + SIM_JST_PIN1_DISABLE, + SIM_JST_PIN1_ENABLE, + SIM_JST_PIN1_UNBLOCK, + SIM_JST_PIN2_UNLOCK, + SIM_JST_PIN2_CHANGE, + SIM_JST_PIN2_UNBLOCK, +}; + +#define MAX_SIM_PATH_LENGTH 6 + 1 /* one for the termination */ + +struct gsm_sim_handler { + struct llist_head entry; + + uint32_t handle; + void (*cb)(struct osmocom_ms *ms, struct msgb *msg); +}; + +struct gsm_sim { + struct llist_head handlers; /* gsm_sim_handler */ + struct llist_head jobs; /* messages */ + uint16_t path[MAX_SIM_PATH_LENGTH]; + uint16_t file; + + struct msgb *job_msg; + uint32_t job_handle; + int job_state; + + uint8_t reset; + uint8_t chv1_remain, chv2_remain; + uint8_t unblk1_remain, unblk2_remain; +}; + +struct sim_hdr { + int handle; + uint8_t job_type; + uint16_t path[MAX_SIM_PATH_LENGTH]; + uint16_t file; + uint8_t rec_no, rec_mode; /* in case of record */ + uint8_t seek_type_mode; /* in case of seek command */ +}; + +#define SIM_ALLOC_SIZE 512 +#define SIM_ALLOC_HEADROOM 64 + +struct msgb *gsm_sim_msgb_alloc(uint32_t handle, uint8_t job_type); +uint32_t sim_open(struct osmocom_ms *ms, + void (*cb)(struct osmocom_ms *ms, struct msgb *msg)); +void sim_close(struct osmocom_ms *ms, uint32_t handle); +void sim_job(struct osmocom_ms *ms, struct msgb *msg); + +/* Section 9.2.1 (response to selecting DF or MF) */ +struct gsm1111_response_mfdf { + uint16_t rfu1; + uint16_t free_mem; + uint16_t file_id; + uint8_t tof; + uint8_t rfu2[5]; + uint8_t length; + uint8_t gsm_data[0]; +} __attribute__ ((packed)); + +struct gsm1111_response_mfdf_gsm { + uint8_t file_char; + uint8_t num_df; + uint8_t num_ef; + uint8_t num_codes; + uint8_t rfu1; + uint8_t chv1_remain:4, + rfu2:3, + chv1_init:1; + uint8_t unblk1_remain:4, + rfu3:3, + unblk1_init:1; + uint8_t chv2_remain:4, + rfu4:3, + chv2_init:1; + uint8_t unblk2_remain:4, + rfu5:3, + unblk2_init:1; + uint8_t more_data[0]; +} __attribute__ ((packed)); + +/* Section 9.2.1 (response to selecting EF) */ +struct gsm1111_response_ef { + uint16_t rfu1; + uint16_t file_size; + uint16_t file_id; + uint8_t tof; + uint8_t inc_allowed; + uint8_t acc_update:4, + acc_read:4; + uint8_t rfu2:4, + acc_inc:4; + uint8_t acc_inval:4, + acc_reha:4; + uint8_t not_inval:1, + rfu3:1, + ru_inval:1, + rfu4:5; + uint8_t length; + uint8_t structure; +} __attribute__ ((packed)); + +/* Section 10.3.17 */ +struct gsm1111_ef_loci { + uint32_t tmsi; + struct gsm48_loc_area_id lai; + uint8_t tmsi_time; + uint8_t lupd_status; +} __attribute__ ((packed)); + +/* Section 10.5.1 */ +struct gsm1111_ef_adn { + uint8_t len_bcd; + uint8_t ton_npi; + uint8_t number[10]; + uint8_t capa_conf; + uint8_t ext_id; +} __attribute__ ((packed)); + +int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg); +int gsm_sim_init(struct osmocom_ms *ms); +int gsm_sim_exit(struct osmocom_ms *ms); +int gsm_sim_job_dequeue(struct osmocom_ms *ms); + + diff --git a/src/host/layer23/include/osmocom/bb/common/sysinfo.h b/src/host/layer23/include/osmocom/bb/common/sysinfo.h new file mode 100644 index 00000000..07daafa7 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/sysinfo.h @@ -0,0 +1,149 @@ +#ifndef _SYSINFO_H +#define _SYSINFO_H + +#include <osmocore/gsm48_ie.h> + +/* collection of system information of the current cell */ + +/* frequency mask flags of frequency type */ +#define FREQ_TYPE_SERV 0x01 /* frequency of the serving cell */ +#define FREQ_TYPE_HOPP 0x02 /* frequency used for channel hopping */ +#define FREQ_TYPE_NCELL 0x1c /* frequency of the neighbor cell */ +#define FREQ_TYPE_NCELL_2 0x04 /* sub channel of SI 2 */ +#define FREQ_TYPE_NCELL_2bis 0x08 /* sub channel of SI 2bis */ +#define FREQ_TYPE_NCELL_2ter 0x10 /* sub channel of SI 2ter */ +#define FREQ_TYPE_REP 0xe0 /* frequency to be reported */ +#define FREQ_TYPE_REP_5 0x20 /* sub channel of SI 5 */ +#define FREQ_TYPE_REP_5bis 0x40 /* sub channel of SI 5bis */ +#define FREQ_TYPE_REP_5ter 0x80 /* sub channel of SI 5ter */ + +/* structure of all received system informations */ +struct gsm48_sysinfo { + /* flags of available information */ + uint8_t si1, si2, si2bis, si2ter, si3, + si4, si5, si5bis, si5ter, si6; + + /* memory maps to simply detect change in system info messages */ + uint8_t si1_msg[23]; + uint8_t si2_msg[23]; + uint8_t si2b_msg[23]; + uint8_t si2t_msg[23]; + uint8_t si3_msg[23]; + uint8_t si4_msg[23]; + uint8_t si5_msg[18]; + uint8_t si5b_msg[18]; + uint8_t si5t_msg[18]; + uint8_t si6_msg[18]; + + struct gsm_sysinfo_freq freq[1024]; /* all frequencies */ + uint16_t hopping[64]; /* hopping arfcn */ + uint8_t hopp_len; + + /* serving cell */ + uint8_t bsic; + uint16_t cell_id; + uint16_t mcc, mnc, lac; /* LAI */ + uint8_t max_retrans; /* decoded */ + uint8_t tx_integer; /* decoded */ + uint8_t reest_denied; /* 1 = denied */ + uint8_t cell_barr; /* 1 = barred */ + uint16_t class_barr; /* bit 10 is emergency */ + + /* si3 rest */ + uint8_t sp; + uint8_t sp_cbq; + uint8_t sp_cro; + uint8_t sp_to; + uint8_t sp_pt; + uint8_t po; + uint8_t po_value; + uint8_t si2ter_ind; + uint8_t ecsm; + uint8_t sched; + uint8_t sched_where; + uint8_t gi_ra_colour; + uint8_t gi_si13_pos; + + /* cell selection */ + int8_t ms_txpwr_max_cch; + int8_t cell_resel_hyst_db; + int8_t rxlev_acc_min_db; + uint8_t neci; + uint8_t acs; + /* bcch options */ + uint8_t bcch_radio_link_timeout; + uint8_t bcch_dtx; + uint8_t bcch_pwrc; + /* sacch options */ + uint8_t sacch_radio_link_timeout; + uint8_t sacch_dtx; + uint8_t sacch_pwrc; + /* control channel */ + uint8_t ccch_conf; + uint8_t bs_ag_blks_res; + uint8_t att_allowed; + uint8_t pag_mf_periods; + int32_t t3212; /* real value in seconds */ + /* channel description */ + uint8_t tsc; + uint8_t h; /* using hopping */ + uint16_t arfcn; + uint8_t maio; + uint8_t hsn; + uint8_t chan_nr; /* type, slot, sub slot */ + + /* neighbor cell */ + uint8_t nb_ext_ind_si2; + uint8_t nb_ba_ind_si2; + uint8_t nb_ext_ind_si2bis; + uint8_t nb_ba_ind_si2bis; + uint8_t nb_multi_rep_si2ter; /* see GSM 05.08 8.4.3 */ + uint8_t nb_ba_ind_si2ter; + uint8_t nb_ext_ind_si5; + uint8_t nb_ba_ind_si5; + uint8_t nb_ext_ind_si5bis; + uint8_t nb_ba_ind_si5bis; + uint8_t nb_multi_rep_si5ter; + uint8_t nb_ba_ind_si5ter; + uint8_t nb_ncc_permitted_si2; + uint8_t nb_ncc_permitted_si6; + uint8_t nb_max_retrans; /* decoded */ + uint8_t nb_tx_integer; /* decoded */ + uint8_t nb_reest_denied; /* 1 = denied */ + uint8_t nb_cell_barr; /* 1 = barred */ + uint16_t nb_class_barr; /* bit 10 is emergency */ +}; + +int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, + void (*print)(void *, const char *, ...), void *priv); +int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc, + uint16_t *mnc, uint16_t *lac); +int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc, + uint16_t *arfcn); +int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc, + uint8_t *maio, uint8_t *hsn); +int gsm48_decode_sysinfo1(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_1 *si, int len); +int gsm48_decode_sysinfo2(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_2 *si, int len); +int gsm48_decode_sysinfo2bis(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_2bis *si, int len); +int gsm48_decode_sysinfo2ter(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_2ter *si, int len); +int gsm48_decode_sysinfo3(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_3 *si, int len); +int gsm48_decode_sysinfo4(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_4 *si, int len); +int gsm48_decode_sysinfo5(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_5 *si, int len); +int gsm48_decode_sysinfo5bis(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_5bis *si, int len); +int gsm48_decode_sysinfo5ter(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_5ter *si, int len); +int gsm48_decode_sysinfo6(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_6 *si, int len); +int gsm48_decode_mobile_alloc(struct gsm_sysinfo_freq *freq, + uint8_t *ma, uint8_t len, uint16_t *hopping, uint8_t *hopp_len, + int si4); + +#endif /* _SYSINFO_H */ diff --git a/src/host/layer23/include/osmocom/bb/misc/Makefile.am b/src/host/layer23/include/osmocom/bb/misc/Makefile.am new file mode 100644 index 00000000..71c9d389 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/misc/Makefile.am @@ -0,0 +1 @@ +noinst_HEADERS = layer3.h rslms.h diff --git a/src/host/layer23/include/osmocom/bb/misc/cell_log.h b/src/host/layer23/include/osmocom/bb/misc/cell_log.h new file mode 100644 index 00000000..bce066eb --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/misc/cell_log.h @@ -0,0 +1,25 @@ +/* Cell Scanning code for OsmocomBB */ + +/* (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +int scan_init(struct osmocom_ms *_ms); +int scan_exit(void); + diff --git a/src/host/layer23/include/osmocom/bb/misc/layer3.h b/src/host/layer23/include/osmocom/bb/misc/layer3.h new file mode 100644 index 00000000..7d364e7a --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/misc/layer3.h @@ -0,0 +1,17 @@ +#ifndef _OSMOCOM_L3_H +#define _OSMOCOM_L3_H + +#include <osmocore/msgb.h> +#include <osmocom/bb/common/osmocom_data.h> + +int gsm48_rx_ccch(struct msgb *msg, struct osmocom_ms *ms); +int gsm48_rx_dcch(struct msgb *msg, struct osmocom_ms *ms); +int gsm48_rx_bcch(struct msgb *msg, struct osmocom_ms *ms); + +/* Initialize layer3 for the MS, hook it to L2 */ +int layer3_init(struct osmocom_ms *ms); + +/* Reset the 'aplication' state */ +void layer3_app_reset(void); + +#endif diff --git a/src/host/layer23/include/osmocom/bb/misc/rslms.h b/src/host/layer23/include/osmocom/bb/misc/rslms.h new file mode 100644 index 00000000..8b62ba92 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/misc/rslms.h @@ -0,0 +1,23 @@ +#ifndef _OSMOCOM_RSLMS_H +#define _OSMOCOM_RSLMS_H + +#include <osmocore/msgb.h> +#include <osmocom/bb/common/osmocom_data.h> + +/* From L3 into RSLMS (direction -> L2) */ + +/* Send a 'simple' RLL request to L2 */ +int rslms_tx_rll_req(struct osmocom_ms *ms, uint8_t msg_type, + uint8_t chan_nr, uint8_t link_id); + +/* Send a RLL request (including L3 info) to L2 */ +int rslms_tx_rll_req_l3(struct osmocom_ms *ms, uint8_t msg_type, + uint8_t chan_nr, uint8_t link_id, struct msgb *msg); + + +/* From L2 into RSLMS (direction -> L3) */ + +/* input function that L2 calls when sending messages up to L3 */ +//int rslms_sendmsg(struct msgb *msg, struct osmocom_ms *ms); + +#endif /* _OSMOCOM_RSLMS_H */ diff --git a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am new file mode 100644 index 00000000..951e4d19 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am @@ -0,0 +1,2 @@ +noinst_HEADERS = gsm322.h gsm48_cc.h gsm48_mm.h gsm48_rr.h mncc.h settings.h \ + subscriber.h support.h transaction.h vty.h diff --git a/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h b/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h new file mode 100644 index 00000000..138fbe04 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h @@ -0,0 +1,15 @@ +#ifndef APP_MOBILE_H +#define APP_MOBILE_H + +int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *), + const char *config_file, uint16_t vty_port); +int l23_app_exit(void); +int l23_app_work(int *quit); +int mobile_delete(struct osmocom_ms *ms, int force); +struct osmocom_ms *mobile_new(char *name); +int mobile_init(struct osmocom_ms *ms); +int mobile_exit(struct osmocom_ms *ms, int force); +int mobile_work(struct osmocom_ms *ms); + +#endif + diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm322.h b/src/host/layer23/include/osmocom/bb/mobile/gsm322.h new file mode 100644 index 00000000..467ff39a --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm322.h @@ -0,0 +1,204 @@ +#ifndef _GSM322_H +#define _GSM322_H + +/* 4.3.1.1 List of states for PLMN slection process (automatic mode) */ +#define GSM322_A0_NULL 0 +#define GSM322_A1_TRYING_RPLMN 1 +#define GSM322_A2_ON_PLMN 2 +#define GSM322_A3_TRYING_PLMN 3 +#define GSM322_A4_WAIT_FOR_PLMN 4 +#define GSM322_A5_HPLMN_SEARCH 5 +#define GSM322_A6_NO_SIM 6 + +/* 4.3.1.2 List of states for PLMN slection process (manual mode) */ +#define GSM322_M0_NULL 0 +#define GSM322_M1_TRYING_RPLMN 1 +#define GSM322_M2_ON_PLMN 2 +#define GSM322_M3_NOT_ON_PLMN 3 +#define GSM322_M4_TRYING_PLMN 4 +#define GSM322_M5_NO_SIM 5 + +/* 4.3.2 List of states for cell selection process */ +#define GSM322_C0_NULL 0 +#define GSM322_C1_NORMAL_CELL_SEL 1 +#define GSM322_C2_STORED_CELL_SEL 2 +#define GSM322_C3_CAMPED_NORMALLY 3 +#define GSM322_C4_NORMAL_CELL_RESEL 4 +#define GSM322_C5_CHOOSE_CELL 5 +#define GSM322_C6_ANY_CELL_SEL 6 +#define GSM322_C7_CAMPED_ANY_CELL 7 +#define GSM322_C8_ANY_CELL_RESEL 8 +#define GSM322_C9_CHOOSE_ANY_CELL 9 +#define GSM322_PLMN_SEARCH 10 +#define GSM322_HPLMN_SEARCH 11 + +/* GSM 03.22 events */ +#define GSM322_EVENT_SWITCH_ON 1 +#define GSM322_EVENT_SWITCH_OFF 2 +#define GSM322_EVENT_SIM_INSERT 3 +#define GSM322_EVENT_SIM_REMOVE 4 +#define GSM322_EVENT_REG_SUCCESS 5 +#define GSM322_EVENT_REG_FAILED 6 +#define GSM322_EVENT_ROAMING_NA 7 +#define GSM322_EVENT_INVALID_SIM 8 +#define GSM322_EVENT_NEW_PLMN 9 +#define GSM322_EVENT_ON_PLMN 10 +#define GSM322_EVENT_PLMN_SEARCH_START 11 +#define GSM322_EVENT_PLMN_SEARCH_END 12 +#define GSM322_EVENT_USER_RESEL 13 +#define GSM322_EVENT_PLMN_AVAIL 14 +#define GSM322_EVENT_CHOOSE_PLMN 15 +#define GSM322_EVENT_SEL_MANUAL 16 +#define GSM322_EVENT_SEL_AUTO 17 +#define GSM322_EVENT_CELL_FOUND 18 +#define GSM322_EVENT_NO_CELL_FOUND 19 +#define GSM322_EVENT_LEAVE_IDLE 20 +#define GSM322_EVENT_RET_IDLE 21 +#define GSM322_EVENT_CELL_RESEL 22 +#define GSM322_EVENT_SYSINFO 23 +#define GSM322_EVENT_HPLMN_SEARCH 24 + +enum { + PLMN_MODE_MANUAL, + PLMN_MODE_AUTO +}; + +/* node for each PLMN */ +struct gsm322_plmn_list { + struct llist_head entry; + uint16_t mcc, mnc; + int8_t rxlev; /* rx level in range format */ + uint8_t cause; /* cause value, if PLMN is not allowed */ +}; + +/* node for each forbidden LA */ +struct gsm322_la_list { + struct llist_head entry; + uint16_t mcc, mnc, lac; + uint8_t cause; +}; + +/* node for each BA-List */ +struct gsm322_ba_list { + struct llist_head entry; + uint16_t mcc, mnc; + /* Band allocation for 1024 frequencies. + * First bit of first index is frequency 0. + */ + uint8_t freq[128]; +}; + +#define GSM322_CS_FLAG_SUPPORT 0x01 /* frequency is supported by radio */ +#define GSM322_CS_FLAG_BA 0x02 /* frequency is part of the current ba */ +#define GSM322_CS_FLAG_POWER 0x04 /* frequency was power scanned */ +#define GSM322_CS_FLAG_SIGNAL 0x08 /* valid signal detected */ +#define GSM322_CS_FLAG_SYSINFO 0x10 /* complete sysinfo received */ +#define GSM322_CS_FLAG_BARRED 0x20 /* cell is barred */ +#define GSM322_CS_FLAG_FORBIDD 0x40 /* cell in list of forbidden LAs */ +#define GSM322_CS_FLAG_TEMP_AA 0x80 /* if temporary available and allowable */ + +/* Cell selection list */ +struct gsm322_cs_list { + uint8_t flags; /* see GSM322_CS_FLAG_* */ + int8_t rxlev; /* rx level range format */ + struct gsm48_sysinfo *sysinfo; +#if 0 + int8_t min_dbm; /* minimum level to enter cell */ + int8_t max_pwr; /* maximum power to access cell */ + uint16_t class_barr; /* barred classes */ + uint16_t mcc, mnc, lac; /* received mcc, mnc, lac */ +#endif +}; + +/* PLMN search process */ +struct gsm322_plmn { + struct osmocom_ms *ms; + int state; /* GSM322_Ax_* or GSM322_Mx_* */ + + struct llist_head event_queue; /* event messages */ + struct llist_head sorted_plmn; /* list of sorted PLMN */ + struct llist_head forbidden_la; /* forbidden LAs */ + + struct timer_list timer; + + int plmn_curr; /* current index in sorted_plmn */ + uint16_t mcc, mnc; /* current network selected */ +}; + +/* state of CCCH activation */ +#define GSM322_CCCH_ST_IDLE 0 /* no connection */ +#define GSM322_CCCH_ST_INIT 1 /* initalized */ +#define GSM322_CCCH_ST_SYNC 2 /* got sync */ +#define GSM322_CCCH_ST_DATA 3 /* receiveing data */ + +struct gsm48_sysinfo; +/* Cell selection process */ +struct gsm322_cellsel { + struct osmocom_ms *ms; + int state; /* GSM322_Cx_* */ + + struct llist_head event_queue; /* event messages */ + struct llist_head ba_list; /* BCCH Allocation per PLMN */ + + struct timer_list timer; + + uint16_t mcc, mnc; /* current network to search for */ + struct gsm322_cs_list list[1024]; /* cell selection list per freq. */ + + uint8_t powerscan; /* currently scanning for power */ + uint32_t scan_state; /* special state of current scan */ + uint8_t ccch_state; /* special state of current ccch */ + uint16_t arfcn; /* current tuned idle mode arfcn */ + uint8_t ccch_mode; /* curren CCCH_MODE_* */ + struct gsm48_sysinfo *si; /* current sysinfo */ + + uint8_t selected; /* if a cell is selected */ + uint16_t sel_arfcn; + struct gsm48_sysinfo sel_si; /* copy of selected cell, will update */ + uint16_t sel_mcc, sel_mnc, sel_lac, sel_id; +}; + +/* GSM 03.22 message */ +struct gsm322_msg { + int msg_type; + uint16_t mcc, mnc; + uint8_t sysinfo; /* system information type */ + uint8_t same_cell; /* select same cell when RET_IDLE */ + uint8_t reject; /* location update reject cause */ +}; + +#define GSM322_ALLOC_SIZE sizeof(struct gsm322_msg) +#define GSM322_ALLOC_HEADROOM 0 + +int gsm322_init(struct osmocom_ms *ms); +int gsm322_exit(struct osmocom_ms *ms); +struct msgb *gsm322_msgb_alloc(int msg_type); +int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg); +int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg); +int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg); +int gsm322_plmn_dequeue(struct osmocom_ms *ms); +int gsm322_cs_dequeue(struct osmocom_ms *ms); +int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, + uint16_t mnc, uint16_t lac, uint8_t cause); +int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, + uint16_t mnc, uint16_t lac); +int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, + uint16_t lac); +int gsm322_dump_sorted_plmn(struct osmocom_ms *ms); +int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags, + void (*print)(void *, const char *, ...), void *priv); +int gsm322_dump_forbidden_la(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv); +int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc, + void (*print)(void *, const char *, ...), void *priv); +void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro); +void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro); +extern const char *plmn_a_state_names[]; +extern const char *plmn_m_state_names[]; +extern const char *cs_state_names[]; +int gsm322_l1_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data); + +char *gsm_print_rxlev(uint8_t rxlev); + +#endif /* _GSM322_H */ diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm48_cc.h b/src/host/layer23/include/osmocom/bb/mobile/gsm48_cc.h new file mode 100644 index 00000000..d6ea5756 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_cc.h @@ -0,0 +1,18 @@ +#ifndef _GSM48_CC_H +#define _GSM48_CC_H + +struct gsm48_cclayer { + struct osmocom_ms *ms; + + struct llist_head mncc_upqueue; + int (*mncc_recv)(struct osmocom_ms *, int, void *); +}; + +int gsm48_cc_init(struct osmocom_ms *ms); +int gsm48_cc_exit(struct osmocom_ms *ms); +int gsm48_rcv_cc(struct osmocom_ms *ms, struct msgb *msg); +int mncc_dequeue(struct osmocom_ms *ms); +int mncc_send(struct osmocom_ms *ms, int msg_type, void *arg); + +#endif /* _GSM48_CC_H */ + diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h b/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h new file mode 100644 index 00000000..447dc95f --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h @@ -0,0 +1,228 @@ +#ifndef _GSM48_MM_H +#define _GSM48_MM_H + +/* GSM 04.07 9.2.2 */ +#define GSM48_MMXX_MASK 0xf00 +#define GSM48_MMCC_CLASS 0x100 +#define GSM48_MMSS_CLASS 0x200 +#define GSM48_MMSMS_CLASS 0x300 +#define GSM48_MMCC_EST_REQ 0x110 +#define GSM48_MMCC_EST_IND 0x112 +#define GSM48_MMCC_EST_CNF 0x111 +#define GSM48_MMCC_REL_REQ 0x120 +#define GSM48_MMCC_REL_IND 0x122 +#define GSM48_MMCC_DATA_REQ 0x130 +#define GSM48_MMCC_DATA_IND 0x132 +#define GSM48_MMCC_UNIT_DATA_REQ 0x140 +#define GSM48_MMCC_UNIT_DATA_IND 0x142 +#define GSM48_MMCC_SYNC_IND 0x152 +#define GSM48_MMCC_REEST_REQ 0x160 +#define GSM48_MMCC_REEST_CNF 0x161 +#define GSM48_MMCC_ERR_IND 0x172 +#define GSM48_MMCC_PROMPT_IND 0x182 +#define GSM48_MMCC_PROMPT_REJ 0x184 +#define GSM48_MMSS_EST_REQ 0x210 +#define GSM48_MMSS_EST_IND 0x212 +#define GSM48_MMSS_EST_CNF 0x211 +#define GSM48_MMSS_REL_REQ 0x220 +#define GSM48_MMSS_REL_IND 0x222 +#define GSM48_MMSS_DATA_REQ 0x230 +#define GSM48_MMSS_DATA_IND 0x232 +#define GSM48_MMSS_UNIT_DATA_REQ 0x240 +#define GSM48_MMSS_UNIT_DATA_IND 0x242 +#define GSM48_MMSS_REEST_REQ 0x260 +#define GSM48_MMSS_REEST_CNF 0x261 +#define GSM48_MMSS_ERR_IND 0x272 +#define GSM48_MMSS_PROMPT_IND 0x282 +#define GSM48_MMSS_PROMPT_REJ 0x284 +#define GSM48_MMSMS_EST_REQ 0x310 +#define GSM48_MMSMS_EST_IND 0x312 +#define GSM48_MMSMS_EST_CNF 0x311 +#define GSM48_MMSMS_REL_REQ 0x320 +#define GSM48_MMSMS_REL_IND 0x322 +#define GSM48_MMSMS_DATA_REQ 0x330 +#define GSM48_MMSMS_DATA_IND 0x332 +#define GSM48_MMSMS_UNIT_DATA_REQ 0x340 +#define GSM48_MMSMS_UNIT_DATA_IND 0x342 +#define GSM48_MMSMS_REEST_REQ 0x360 +#define GSM48_MMSMS_REEST_CNF 0x361 +#define GSM48_MMSMS_ERR_IND 0x372 +#define GSM48_MMSMS_PROMPT_IND 0x382 +#define GSM48_MMSMS_PROMPT_REJ 0x384 + +#define MMXX_ALLOC_SIZE 256 +#define MMXX_ALLOC_HEADROOM 64 + +/* MMxx-SAP header */ +struct gsm48_mmxx_hdr { + int msg_type; /* MMxx_* primitive */ + uint32_t ref; /* reference to transaction */ + uint32_t transaction_id; /* transaction identifier */ + uint8_t emergency; /* emergency type of call */ + uint8_t cause; /* cause used for release */ +}; + +/* GSM 6.1.2 */ +#define GSM48_MMR_REG_REQ 0x01 +#define GSM48_MMR_REG_CNF 0x02 +#define GSM48_MMR_NREG_REQ 0x03 +#define GSM48_MMR_NREG_IND 0x04 + +/* MMR-SAP header */ +struct gsm48_mmr { + int msg_type; + + uint8_t cause; +}; + +/* GSM 04.07 9.2.1 */ +#define GSM48_MMXX_ST_IDLE 0 +#define GSM48_MMXX_ST_CONN_PEND 1 +#define GSM48_MMXX_ST_DEDICATED 2 +#define GSM48_MMXX_ST_CONN_SUSP 3 +#define GSM48_MMXX_ST_REESTPEND 4 + +/* GSM 04.08 4.1.2.1 */ +#define GSM48_MM_ST_NULL 0 +#define GSM48_MM_ST_LOC_UPD_INIT 3 +#define GSM48_MM_ST_WAIT_OUT_MM_CONN 5 +#define GSM48_MM_ST_MM_CONN_ACTIVE 6 +#define GSM48_MM_ST_IMSI_DETACH_INIT 7 +#define GSM48_MM_ST_PROCESS_CM_SERV_P 8 +#define GSM48_MM_ST_WAIT_NETWORK_CMD 9 +#define GSM48_MM_ST_LOC_UPD_REJ 10 +#define GSM48_MM_ST_WAIT_RR_CONN_LUPD 13 +#define GSM48_MM_ST_WAIT_RR_CONN_MM_CON 14 +#define GSM48_MM_ST_WAIT_RR_CONN_IMSI_D 15 +#define GSM48_MM_ST_WAIT_REEST 17 +#define GSM48_MM_ST_WAIT_RR_ACTIVE 18 +#define GSM48_MM_ST_MM_IDLE 19 +#define GSM48_MM_ST_WAIT_ADD_OUT_MM_CON 20 +#define GSM48_MM_ST_MM_CONN_ACTIVE_VGCS 21 +#define GSM48_MM_ST_WAIT_RR_CONN_VGCS 22 +#define GSM48_MM_ST_LOC_UPD_PEND 23 +#define GSM48_MM_ST_IMSI_DETACH_PEND 24 +#define GSM48_MM_ST_RR_CONN_RELEASE_NA 25 + +/* GSM 04.08 4.1.2.1 */ +#define GSM48_MM_SST_NORMAL_SERVICE 1 +#define GSM48_MM_SST_ATTEMPT_UPDATE 2 +#define GSM48_MM_SST_LIMITED_SERVICE 3 +#define GSM48_MM_SST_NO_IMSI 4 +#define GSM48_MM_SST_NO_CELL_AVAIL 5 +#define GSM48_MM_SST_LOC_UPD_NEEDED 6 +#define GSM48_MM_SST_PLMN_SEARCH 7 +#define GSM48_MM_SST_PLMN_SEARCH_NORMAL 8 +#define GSM48_MM_SST_RX_VGCS_NORMAL 9 +#define GSM48_MM_SST_RX_VGCS_LIMITED 10 + +/* MM events */ +#define GSM48_MM_EVENT_CELL_SELECTED 1 +#define GSM48_MM_EVENT_NO_CELL_FOUND 2 +#define GSM48_MM_EVENT_TIMEOUT_T3210 3 +#define GSM48_MM_EVENT_TIMEOUT_T3211 4 +#define GSM48_MM_EVENT_TIMEOUT_T3212 5 +#define GSM48_MM_EVENT_TIMEOUT_T3213 6 +#define GSM48_MM_EVENT_TIMEOUT_T3220 7 +#define GSM48_MM_EVENT_TIMEOUT_T3230 8 +#define GSM48_MM_EVENT_TIMEOUT_T3240 9 +#define GSM48_MM_EVENT_IMSI_DETACH 10 +#define GSM48_MM_EVENT_POWER_OFF 11 +#define GSM48_MM_EVENT_PAGING 12 +#define GSM48_MM_EVENT_AUTH_RESPONSE 13 +#define GSM48_MM_EVENT_SYSINFO 14 +#define GSM48_MM_EVENT_USER_PLMN_SEL 15 + +/* message for MM events */ +struct gsm48_mm_event { + uint32_t msg_type; + + uint8_t sres[4]; +}; + +/* GSM 04.08 MM timers */ +#define GSM_T3210_MS 20, 0 +#define GSM_T3211_MS 15, 0 +/* T3212 is given by SYSTEM INFORMATION */ +#define GSM_T3213_MS 4, 0 +#define GSM_T3220_MS 5, 0 +#define GSM_T3230_MS 15, 0 +#define GSM_T3240_MS 10, 0 +#define GSM_T3241_MS 300, 0 + +/* MM sublayer instance */ +struct gsm48_mmlayer { + struct osmocom_ms *ms; + int state; + int substate; + + /* queue for RR-SAP, MMxx-SAP, MMR-SAP, events message upwards */ + struct llist_head rr_upqueue; + struct llist_head mmxx_upqueue; + struct llist_head mmr_downqueue; + struct llist_head event_queue; + + /* timers */ + struct timer_list t3210, t3211, t3212, t3213; + struct timer_list t3220, t3230, t3240; + int t3212_value; + int start_t3211; /* remember to start timer */ + + /* list of MM connections */ + struct llist_head mm_conn; + + /* network name */ + char name_short[32]; + char name_long[32]; + + /* location update */ + uint8_t lupd_pending; /* current pending loc. upd. */ + uint8_t lupd_type; /* current coded type */ + uint8_t lupd_attempt; /* attempt counter */ + uint8_t lupd_ra_failure;/* random access failed */ + uint8_t lupd_rej_cause; /* cause of last reject */ + uint8_t lupd_periodic; /* periodic update pending */ + uint8_t lupd_retry; /* pending T3211/T3213 to */ + uint16_t lupd_mcc, lupd_mnc, lupd_lac; + + /* imsi detach */ + uint8_t delay_detach; /* do detach when possible */ + + /* other */ + uint8_t est_cause; /* cause of establishment msg */ + int mr_substate; /* rem most recent substate */ + uint8_t power_off_idle; /* waits for IDLE before po */ +}; + +/* MM connection entry */ +struct gsm48_mm_conn { + struct llist_head list; + struct gsm48_mmlayer *mm; + + /* ref and type form a unique tupple */ + uint32_t ref; /* reference to trans */ + uint8_t protocol; + uint8_t transaction_id; + + int state; +}; + +int gsm48_mm_init(struct osmocom_ms *ms); +int gsm48_mm_exit(struct osmocom_ms *ms); +struct msgb *gsm48_mmr_msgb_alloc(int msg_type); +struct msgb *gsm48_mmevent_msgb_alloc(int msg_type); +int gsm48_mmevent_msg(struct osmocom_ms *ms, struct msgb *msg); +int gsm48_mmr_downmsg(struct osmocom_ms *ms, struct msgb *msg); +int gsm48_rr_dequeue(struct osmocom_ms *ms); +int gsm48_mmxx_dequeue(struct osmocom_ms *ms); +int gsm48_mmr_dequeue(struct osmocom_ms *ms); +int gsm48_mmevent_dequeue(struct osmocom_ms *ms); +int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg); +struct msgb *gsm48_mmxx_msgb_alloc(int msg_type, uint32_t ref, + uint8_t transaction_id); +const char *get_mmr_name(int value); +const char *get_mmxx_name(int value); +extern const char *gsm48_mm_state_names[]; +extern const char *gsm48_mm_substate_names[]; + +#endif /* _GSM48_MM_H */ diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h new file mode 100644 index 00000000..830c8cf5 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h @@ -0,0 +1,197 @@ +#ifndef _GSM48_RR_H +#define _GSM48_RR_H + +#include "osmocore/protocol/gsm_04_08.h" + +#define GSM_TA_CM 55385 + +/* GSM 04.07 9.1.2 */ +#define GSM48_RR_EST_REQ 0x10 +#define GSM48_RR_EST_IND 0x12 +#define GSM48_RR_EST_CNF 0x11 +#define GSM48_RR_REL_IND 0x22 +#define GSM48_RR_SYNC_IND 0x32 +#define GSM48_RR_DATA_REQ 0x40 +#define GSM48_RR_DATA_IND 0x42 +#define GSM48_RR_UNIT_DATA_IND 0x52 +#define GSM48_RR_ABORT_REQ 0x60 +#define GSM48_RR_ABORT_IND 0x62 +#define GSM48_RR_ACT_REQ 0x70 + +#define RR_EST_CAUSE_EMERGENCY 1 +#define RR_EST_CAUSE_REESTAB_TCH_F 2 +#define RR_EST_CAUSE_REESTAB_TCH_H 3 +#define RR_EST_CAUSE_REESTAB_2_TCH_H 4 +#define RR_EST_CAUSE_ANS_PAG_ANY 5 +#define RR_EST_CAUSE_ANS_PAG_SDCCH 6 +#define RR_EST_CAUSE_ANS_PAG_TCH_F 7 +#define RR_EST_CAUSE_ANS_PAG_TCH_ANY 8 +#define RR_EST_CAUSE_ORIG_TCHF 9 +#define RR_EST_CAUSE_LOC_UPD 12 +#define RR_EST_CAUSE_OTHER_SDCCH 13 + +#define RR_REL_CAUSE_UNDEFINED 0 +#define RR_REL_CAUSE_NORMAL 1 +#define RR_REL_CAUSE_NOT_AUTHORIZED 2 +#define RR_REL_CAUSE_RA_FAILURE 3 +#define RR_REL_CAUSE_T3122 4 +#define RR_REL_CAUSE_TRY_LATER 5 +#define RR_REL_CAUSE_EMERGENCY_ONLY 6 +#define RR_REL_CAUSE_LOST_SIGNAL 7 +#define RR_REL_CAUSE_LINK_FAILURE 8 + +#define RR_SYNC_CAUSE_CIPHERING 1 + +#define L3_ALLOC_SIZE 256 +#define L3_ALLOC_HEADROOM 64 + +#define RSL_ALLOC_SIZE 256 +#define RSL_ALLOC_HEADROOM 64 + +#define RR_ALLOC_SIZE 256 +#define RR_ALLOC_HEADROOM 64 + +/* GSM 04.08 RR-SAP header */ +struct gsm48_rr_hdr { + uint32_t msg_type; /* RR-* primitive */ + uint8_t cause; +}; + +/* GSM 04.07 9.1.1 */ +#define GSM48_RR_ST_IDLE 0 +#define GSM48_RR_ST_CONN_PEND 1 +#define GSM48_RR_ST_DEDICATED 2 +#define GSM48_RR_ST_REL_PEND 3 + +/* modify state */ +#define GSM48_RR_MOD_NONE 0 +#define GSM48_RR_MOD_IMM_ASS 1 +#define GSM48_RR_MOD_ASSIGN 2 +#define GSM48_RR_MOD_HANDO 3 +#define GSM48_RR_MOD_ASSIGN_RESUME 4 +#define GSM48_RR_MOD_HANDO_RESUME 5 + +/* channel description */ +struct gsm48_rr_cd { + uint8_t tsc; + uint8_t h; /* using hopping */ + uint16_t arfcn; /* dedicated mode */ + uint8_t maio; + uint8_t hsn; + uint8_t chan_nr; /* type, slot, sub slot */ + uint8_t link_id; + uint8_t ind_tx_power; /* last indicated power */ + uint8_t ind_ta; /* last indicated ta */ + uint8_t mob_alloc_lv[9]; /* len + up to 64 bits */ + uint8_t freq_list_lv[131]; /* len + 130 octets */ + uint8_t freq_seq_lv[10]; /* len + 9 octets */ + uint8_t cell_desc_lv[17]; /* len + 16 octets */ + uint8_t start; /* start time available */ + struct gsm_time start_tm; /* start time */ + uint8_t mode; /* mode of channel */ + uint8_t cipher; /* ciphering of channel */ +}; + +struct gsm48_cr_hist { + uint8_t valid; + struct gsm48_req_ref ref; +}; + +/* neighbor cell measurements */ +struct gsm48_rr_meas { + /* note: must be sorted by arfcn 1..1023,0 according to SI5* */ + uint8_t nc_num; /* number of measured cells (32 max) */ + int8_t nc_rxlev[32]; /* -128 = no value */ + uint8_t nc_bsic[32]; + uint16_t nc_arfcn[32]; +}; + +/* RR sublayer instance */ +struct gsm48_rrlayer { + struct osmocom_ms *ms; + int state; + + /* queue for RSL-SAP message upwards */ + struct llist_head rsl_upqueue; + + /* queue for messages while RR connection is built up */ + struct llist_head downqueue; + + /* timers */ + struct timer_list t_starting; /* starting time for chan. access */ + struct timer_list t_rel_wait; /* wait for L2 to transmit UA */ + struct timer_list t3110; + struct timer_list t3122; + struct timer_list t3124; + struct timer_list t3126; + int t3126_value; +#ifndef TODO + struct timer_list temp_rach_ti; /* temporary timer */ +#endif + + /* states if RR-EST-REQ was used */ + uint8_t rr_est_req; + struct msgb *rr_est_msg; + uint8_t est_cause; /* cause used for establishment */ + + /* channel request states */ + uint8_t wait_assign; /* waiting for assignment state */ + uint8_t n_chan_req; /* number left, incl. current */ + uint8_t chan_req_val; /* current request value */ + uint8_t chan_req_mask; /* mask of random bits */ + + /* state of dedicated mdoe */ + uint8_t dm_est; + + /* cr_hist */ + uint8_t cr_ra; /* stores requested ra until confirmed */ + struct gsm48_cr_hist cr_hist[3]; + + /* V(SD) sequence numbers */ + uint16_t v_sd; /* 16 PD 1-bit sequence numbers packed */ + + /* current channel descriptions */ + struct gsm48_rr_cd cd_now; + + /* current cipering */ + uint8_t cipher_on; + uint8_t cipher_type; /* 10.5.2.9 */ + + /* special states when assigning channel */ + uint8_t modify_state; + uint8_t hando_sync_ind, hando_rot, hando_nci, hando_act; + struct gsm48_rr_cd cd_last; /* store last cd in case of failure */ + struct gsm48_rr_cd cd_before; /* before start time */ + struct gsm48_rr_cd cd_after; /* after start time */ + + /* BA range */ + uint8_t ba_ranges; + uint32_t ba_range[16]; + + /* measurements */ + struct timer_list t_meas; + struct gsm48_rr_meas meas; + uint8_t monitor; +}; + +const char *get_rr_name(int value); +extern int gsm48_rr_init(struct osmocom_ms *ms); +extern int gsm48_rr_exit(struct osmocom_ms *ms); +int gsm48_rsl_dequeue(struct osmocom_ms *ms); +int gsm48_rr_downmsg(struct osmocom_ms *ms, struct msgb *msg); +struct msgb *gsm48_l3_msgb_alloc(void); +struct msgb *gsm48_rr_msgb_alloc(int msg_type); +int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc, + uint16_t *mnc, uint16_t *lac); +int gsm48_encode_lai(struct gsm48_loc_area_id *lai, uint16_t mcc, + uint16_t mnc, uint16_t lac); +int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm); +int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg); +int gsm48_rr_los(struct osmocom_ms *ms); +int gsm48_rr_rach_conf(struct osmocom_ms *ms, uint32_t fn); +extern const char *gsm48_rr_state_names[]; +int gsm48_rr_start_monitor(struct osmocom_ms *ms); +int gsm48_rr_stop_monitor(struct osmocom_ms *ms); +int gsm48_rr_alter_delay(struct osmocom_ms *ms); + +#endif /* _GSM48_RR_H */ diff --git a/src/host/layer23/include/osmocom/bb/mobile/mncc.h b/src/host/layer23/include/osmocom/bb/mobile/mncc.h new file mode 100644 index 00000000..151f0b4b --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/mncc.h @@ -0,0 +1,181 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _MNCC_H +#define _MNCC_H + +#include <osmocore/linuxlist.h> +#include <osmocore/mncc.h> + +struct gsm_call { + struct llist_head entry; + + struct osmocom_ms *ms; + + uint32_t callref; + + uint8_t init; /* call initiated, no response yet */ + uint8_t hold; /* call on hold */ + uint8_t ring; /* call ringing/knocking */ + + struct timer_list dtmf_timer; + uint8_t dtmf_state; + uint8_t dtmf_index; + char dtmf[32]; /* dtmf sequence */ +}; + +#define DTMF_ST_IDLE 0 /* no DTMF active */ +#define DTMF_ST_START 1 /* DTMF started, waiting for resp. */ +#define DTMF_ST_MARK 2 /* wait tone duration */ +#define DTMF_ST_STOP 3 /* DTMF stopped, waiting for resp. */ +#define DTMF_ST_SPACE 4 /* wait space between tones */ + +#define MNCC_SETUP_REQ 0x0101 +#define MNCC_SETUP_IND 0x0102 +#define MNCC_SETUP_RSP 0x0103 +#define MNCC_SETUP_CNF 0x0104 +#define MNCC_SETUP_COMPL_REQ 0x0105 +#define MNCC_SETUP_COMPL_IND 0x0106 +/* MNCC_REJ_* is perfomed via MNCC_REL_* */ +#define MNCC_CALL_CONF_IND 0x0107 +#define MNCC_CALL_PROC_REQ 0x0108 +#define MNCC_PROGRESS_REQ 0x0109 +#define MNCC_ALERT_REQ 0x010a +#define MNCC_ALERT_IND 0x010b +#define MNCC_NOTIFY_REQ 0x010c +#define MNCC_NOTIFY_IND 0x010d +#define MNCC_DISC_REQ 0x010e +#define MNCC_DISC_IND 0x010f +#define MNCC_REL_REQ 0x0110 +#define MNCC_REL_IND 0x0111 +#define MNCC_REL_CNF 0x0112 +#define MNCC_FACILITY_REQ 0x0113 +#define MNCC_FACILITY_IND 0x0114 +#define MNCC_START_DTMF_IND 0x0115 +#define MNCC_START_DTMF_RSP 0x0116 +#define MNCC_START_DTMF_REJ 0x0117 +#define MNCC_STOP_DTMF_IND 0x0118 +#define MNCC_STOP_DTMF_RSP 0x0119 +#define MNCC_MODIFY_REQ 0x011a +#define MNCC_MODIFY_IND 0x011b +#define MNCC_MODIFY_RSP 0x011c +#define MNCC_MODIFY_CNF 0x011d +#define MNCC_MODIFY_REJ 0x011e +#define MNCC_HOLD_IND 0x011f +#define MNCC_HOLD_CNF 0x0120 +#define MNCC_HOLD_REJ 0x0121 +#define MNCC_RETRIEVE_IND 0x0122 +#define MNCC_RETRIEVE_CNF 0x0123 +#define MNCC_RETRIEVE_REJ 0x0124 +#define MNCC_USERINFO_REQ 0x0125 +#define MNCC_USERINFO_IND 0x0126 +#define MNCC_REJ_REQ 0x0127 +#define MNCC_REJ_IND 0x0128 +#define MNCC_PROGRESS_IND 0x0129 +#define MNCC_CALL_PROC_IND 0x012a +#define MNCC_CALL_CONF_REQ 0x012b +#define MNCC_START_DTMF_REQ 0x012c +#define MNCC_STOP_DTMF_REQ 0x012d +#define MNCC_HOLD_REQ 0x012e +#define MNCC_RETRIEVE_REQ 0x012f + +#define MNCC_BRIDGE 0x0200 +#define MNCC_FRAME_RECV 0x0201 +#define MNCC_FRAME_DROP 0x0202 +#define MNCC_LCHAN_MODIFY 0x0203 + +#define GSM_TCHF_FRAME 0x0300 +#define GSM_TCHF_FRAME_EFR 0x0301 + +#define MS_NEW 0x0400 +#define MS_DELETE 0x0401 + +#define GSM_MAX_FACILITY 128 +#define GSM_MAX_SSVERSION 128 +#define GSM_MAX_USERUSER 128 + +#define MNCC_F_BEARER_CAP 0x0001 +#define MNCC_F_CALLED 0x0002 +#define MNCC_F_CALLING 0x0004 +#define MNCC_F_REDIRECTING 0x0008 +#define MNCC_F_CONNECTED 0x0010 +#define MNCC_F_CAUSE 0x0020 +#define MNCC_F_USERUSER 0x0040 +#define MNCC_F_PROGRESS 0x0080 +#define MNCC_F_EMERGENCY 0x0100 +#define MNCC_F_FACILITY 0x0200 +#define MNCC_F_SSVERSION 0x0400 +#define MNCC_F_CCCAP 0x0800 +#define MNCC_F_KEYPAD 0x1000 +#define MNCC_F_SIGNAL 0x2000 + +struct gsm_mncc { + /* context based information */ + u_int32_t msg_type; + u_int32_t callref; + + /* which fields are present */ + u_int32_t fields; + + /* data derived informations (MNCC_F_ based) */ + struct gsm_mncc_bearer_cap bearer_cap; + struct gsm_mncc_number called; + struct gsm_mncc_number calling; + struct gsm_mncc_number redirecting; + struct gsm_mncc_number connected; + struct gsm_mncc_cause cause; + struct gsm_mncc_progress progress; + struct gsm_mncc_useruser useruser; + struct gsm_mncc_facility facility; + struct gsm_mncc_cccap cccap; + struct gsm_mncc_ssversion ssversion; + struct { + int sup; + int inv; + } clir; + int signal; + + /* data derived information, not MNCC_F based */ + int keypad; + int more; + int notify; /* 0..127 */ + int emergency; + char imsi[16]; + + unsigned char lchan_mode; +}; + +struct gsm_data_frame { + u_int32_t msg_type; + u_int32_t callref; + unsigned char data[0]; +}; + +const char *get_mncc_name(int value); +int mncc_recv(struct osmocom_ms *ms, int msg_type, void *arg); +void mncc_set_cause(struct gsm_mncc *data, int loc, int val); + +#endif + diff --git a/src/host/layer23/include/osmocom/bb/mobile/settings.h b/src/host/layer23/include/osmocom/bb/mobile/settings.h new file mode 100644 index 00000000..f666f378 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/settings.h @@ -0,0 +1,95 @@ +#ifndef _settings_h +#define _settings_h + +/* type of test SIM key */ +enum { + GSM_SIM_KEY_XOR = 0, + GSM_SIM_KEY_COMP128 +}; + +struct gsm_settings { + char layer2_socket_path[128]; + char sap_socket_path[128]; + + /* IMEI */ + char imei[16]; + char imeisv[17]; + char imei_random; + + /* network search */ + int plmn_mode; /* PLMN_MODE_* */ + + /* SIM */ + int sim_type; /* selects card on power on */ + char emergency_imsi[20]; /* just in case... */ + + /* test card simulator settings */ + char test_imsi[20]; /* just in case... */ + uint32_t test_tmsi; + uint8_t test_ki_type; + uint8_t test_ki[16]; /* 128 bit max */ + uint8_t test_barr; + uint8_t test_rplmn_valid; + uint16_t test_rplmn_mcc, test_rplmn_mnc; + uint16_t test_lac; + uint8_t test_always; /* ...search hplmn... */ + + /* call related settings */ + uint8_t cw; /* set if call-waiting is allowed */ + uint8_t auto_answer; + uint8_t clip, clir; + uint8_t half, half_prefer; + + /* changing default behavior */ + uint8_t alter_tx_power; + uint8_t alter_tx_power_value; + int8_t alter_delay; + uint8_t stick; + uint16_t stick_arfcn; + uint8_t no_lupd; + + /* supported by configuration */ + uint8_t cc_dtmf; + uint8_t sms_ptp; + uint8_t a5_1; + uint8_t a5_2; + uint8_t a5_3; + uint8_t a5_4; + uint8_t a5_5; + uint8_t a5_6; + uint8_t a5_7; + uint8_t p_gsm; + uint8_t e_gsm; + uint8_t r_gsm; + uint8_t dcs; + uint8_t class_900; + uint8_t class_dcs; + uint8_t full_v1; + uint8_t full_v2; + uint8_t full_v3; + uint8_t half_v1; + uint8_t half_v3; + uint8_t ch_cap; /* channel capability */ + int8_t min_rxlev_db; /* min DB to access */ + + /* radio */ + uint16_t dsc_max; + + /* dialing */ + struct llist_head abbrev; +}; + +struct gsm_settings_abbrev { + struct llist_head list; + char abbrev[4]; + char number[32]; + char name[32]; +}; + +int gsm_settings_init(struct osmocom_ms *ms); +int gsm_settings_exit(struct osmocom_ms *ms); +char *gsm_check_imei(const char *imei, const char *sv); +int gsm_random_imei(struct gsm_settings *set); + +#endif /* _settings_h */ + diff --git a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h new file mode 100644 index 00000000..c6cf57a0 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h @@ -0,0 +1,105 @@ +#ifndef _SUBSCRIBER_H +#define _SUBSCRIBER_H + +/* GSM 04.08 4.1.2.2 SIM update status */ +#define GSM_SIM_U0_NULL 0 +#define GSM_SIM_U1_UPDATED 1 +#define GSM_SIM_U2_NOT_UPDATED 2 +#define GSM_SIM_U3_ROAMING_NA 3 + +struct gsm_sub_plmn_list { + struct llist_head entry; + uint16_t mcc, mnc; +}; + +struct gsm_sub_plmn_na { + struct llist_head entry; + uint16_t mcc, mnc; + uint8_t cause; +}; + +#define GSM_IMSI_LENGTH 16 + +enum { + GSM_SIM_TYPE_NONE = 0, + GSM_SIM_TYPE_READER, + GSM_SIM_TYPE_TEST +}; + +struct gsm_subscriber { + struct osmocom_ms *ms; + + /* status */ + uint8_t sim_type; /* type of sim */ + uint8_t sim_valid; /* sim inserted and valid */ + uint8_t ustate; /* update status */ + uint8_t imsi_attached; /* attached state */ + + /* IMSI & co */ + char imsi[GSM_IMSI_LENGTH]; + char msisdn[31]; /* may include access codes */ + char iccid[21]; /* 20 + termination */ + + /* TMSI / LAI */ + uint32_t tmsi; /* invalid tmsi: 0xffffffff */ + uint16_t mcc, mnc, lac; /* invalid lac: 0x0000 */ + + + /* key */ + uint8_t key_seq; /* ciphering key sequence number */ + uint8_t key[8]; /* 64 bit */ + + /* other */ + struct llist_head plmn_list; /* PLMN Selector field */ + struct llist_head plmn_na; /* not allowed PLMNs */ + uint8_t t6m_hplmn; /* timer for hplmn search */ + + /* special things */ + uint8_t always_search_hplmn; + /* search hplmn in other countries also (for test cards) */ + char sim_name[31]; /* name to load/save sim */ + char sim_spn[17]; /* name of service privider */ + + /* PLMN last registered */ + uint8_t plmn_valid; + uint16_t plmn_mcc, plmn_mnc; + + /* our access */ + uint8_t acc_barr; /* if we may access, if cell barred */ + uint16_t acc_class; /* bitmask of what we may access */ + + /* talk to SIM */ + uint8_t sim_state; + uint8_t sim_pin_required; /* state: wait for PIN */ + uint8_t sim_file_index; + uint32_t sim_handle_query; + uint32_t sim_handle_update; + uint32_t sim_handle_key; +}; + +int gsm_subscr_init(struct osmocom_ms *ms); +int gsm_subscr_exit(struct osmocom_ms *ms); +int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, + uint16_t lac, uint32_t tmsi); +int gsm_subscr_simcard(struct osmocom_ms *ms); +void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2, + int8_t mode); +int gsm_subscr_write_loci(struct osmocom_ms *ms); +int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, + uint8_t *rand, uint8_t no_sim); +int gsm_subscr_remove(struct osmocom_ms *ms); +void new_sim_ustate(struct gsm_subscriber *subscr, int state); +int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc); +int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc, uint8_t cause); +int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc); +int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv); +void gsm_subscr_dump(struct gsm_subscriber *subscr, + void (*print)(void *, const char *, ...), void *priv); +char *gsm_check_imsi(const char *imsi); + +#endif /* _SUBSCRIBER_H */ + diff --git a/src/host/layer23/include/osmocom/bb/mobile/support.h b/src/host/layer23/include/osmocom/bb/mobile/support.h new file mode 100644 index 00000000..cbe6e19b --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/support.h @@ -0,0 +1,102 @@ +#ifndef _SUPPORT_H +#define _SUPPORT_H + +#define GSM_CIPHER_A5_1 0 +#define GSM_CIPHER_A5_2 1 +#define GSM_CIPHER_A5_3 2 +#define GSM_CIPHER_A5_4 3 +#define GSM_CIPHER_A5_5 4 +#define GSM_CIPHER_A5_6 5 +#define GSM_CIPHER_A5_7 6 +#define GSM_CIPHER_RESERVED 7 + +#define GSM_CAP_SDCCH 0 +#define GSM_CAP_SDCCH_TCHF 1 +#define GSM_CAP_SDCCH_TCHF_TCHH 2 + +struct gsm_support { + struct osmocom_ms *ms; + + /* controlled early classmark sending */ + uint8_t es_ind; + /* revision level */ + uint8_t rev_lev; + /* support of VGCS */ + uint8_t vgcs; + /* support of VBS */ + uint8_t vbs; + /* support of SMS */ + uint8_t sms_ptp; + /* screening indicator */ + uint8_t ss_ind; + /* pseudo synchronised capability */ + uint8_t ps_cap; + /* CM service prompt */ + uint8_t cmsp; + /* solsa support */ + uint8_t solsa; + /* location service support */ + uint8_t lcsva; + /* codec supprot */ + uint8_t a5_1; + uint8_t a5_2; + uint8_t a5_3; + uint8_t a5_4; + uint8_t a5_5; + uint8_t a5_6; + uint8_t a5_7; + /* radio support */ + uint8_t p_gsm; + uint8_t e_gsm; + uint8_t r_gsm; + uint8_t dcs; + uint8_t class_900; + uint8_t class_dcs; + uint8_t freq_map[128]; + /* multi slot support */ + uint8_t ms_sup; + /* ucs2 treatment */ + uint8_t ucs2_treat; + /* support extended measurements */ + uint8_t ext_meas; + /* support switched measurement capability */ + uint8_t meas_cap; + uint8_t sms_val; + uint8_t sm_val; + /* positioning method capability */ + uint8_t loc_serv; + uint8_t e_otd_ass; + uint8_t e_otd_based; + uint8_t gps_ass; + uint8_t gps_based; + uint8_t gps_conv; + + /* radio */ + uint8_t ch_cap; /* channel capability */ + int8_t min_rxlev_db; + uint8_t scan_to; + uint8_t sync_to; + uint16_t dsc_max; /* maximum dl signal failure counter */ + + /* codecs */ + uint8_t full_v1; + uint8_t full_v2; + uint8_t full_v3; + uint8_t half_v1; + uint8_t half_v3; +}; + +struct gsm_support_scan_max { + uint16_t start; + uint16_t end; + uint16_t max; + uint16_t temp; +}; +extern struct gsm_support_scan_max gsm_sup_smax[]; + +void gsm_support_init(struct osmocom_ms *ms); +void gsm_support_dump(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv); + +#endif /* _SUPPORT_H */ + diff --git a/src/host/layer23/include/osmocom/bb/mobile/transaction.h b/src/host/layer23/include/osmocom/bb/mobile/transaction.h new file mode 100644 index 00000000..4be82c19 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/transaction.h @@ -0,0 +1,71 @@ +#ifndef _TRANSACT_H +#define _TRANSACT_H + +#include <osmocore/linuxlist.h> + +/* One transaction */ +struct gsm_trans { + /* Entry in list of all transactions */ + struct llist_head entry; + + /* The protocol within which we live */ + uint8_t protocol; + + /* The current transaction ID */ + uint8_t transaction_id; + + /* To whom we belong */ + struct osmocom_ms *ms; + + /* reference from MNCC or other application */ + uint32_t callref; + + /* if traffic channel receive was requested */ + int tch_recv; + + union { + struct { + + /* current call state */ + int state; + + /* most recent progress indicator */ + uint8_t prog_ind; + + /* current timer and message queue */ + int Tcurrent; /* current CC timer */ + int T308_second; /* used to send release again */ + struct timer_list timer; + struct gsm_mncc msg; /* stores setup/disconnect/release message */ + } cc; +#if 0 + struct { + uint8_t link_id; /* RSL Link ID to be used for this trans */ + int is_mt; /* is this a MO (0) or MT (1) transfer */ + enum gsm411_cp_state cp_state; + struct timer_list cp_timer; + + enum gsm411_rp_state rp_state; + + struct gsm_sms *sms; + } sms; +#endif + }; +}; + + + +struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms, + uint8_t proto, uint8_t trans_id); +struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, + uint32_t callref); + +struct gsm_trans *trans_alloc(struct osmocom_ms *ms, + uint8_t protocol, uint8_t trans_id, + uint32_t callref); +void trans_free(struct gsm_trans *trans); + +int trans_assign_trans_id(struct osmocom_ms *ms, + uint8_t protocol, uint8_t ti_flag); + +#endif diff --git a/src/host/layer23/include/osmocom/bb/mobile/vty.h b/src/host/layer23/include/osmocom/bb/mobile/vty.h new file mode 100644 index 00000000..1f1341bc --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/vty.h @@ -0,0 +1,20 @@ +#ifndef OSMOCOM_VTY_H +#define OSMOCOM_VTY_H + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/vty/command.h> + +enum ms_vty_node { + MS_NODE = _LAST_OSMOVTY_NODE + 1, + TESTSIM_NODE, + SUPPORT_NODE, +}; + +enum node_type ms_vty_go_parent(struct vty *vty); +int ms_vty_init(void); +extern void vty_notify(struct osmocom_ms *ms, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + +#endif + diff --git a/src/host/layer23/src/Makefile.am b/src/host/layer23/src/Makefile.am new file mode 100644 index 00000000..58a5f7fb --- /dev/null +++ b/src/host/layer23/src/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = common misc mobile diff --git a/src/host/layer23/src/common/Makefile.am b/src/host/layer23/src/common/Makefile.am new file mode 100644 index 00000000..4e2686c4 --- /dev/null +++ b/src/host/layer23/src/common/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) + +noinst_LIBRARIES = liblayer23.a +liblayer23_a_SOURCES = l1ctl.c l1l2_interface.c sap_interface.c lapdm.c \ + logging.c networks.c sim.c sysinfo.c gps.c diff --git a/src/host/layer23/src/common/gps.c b/src/host/layer23/src/common/gps.c new file mode 100644 index 00000000..a092cd03 --- /dev/null +++ b/src/host/layer23/src/common/gps.c @@ -0,0 +1,251 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <sys/file.h> +#include <termios.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <time.h> + +#include <osmocore/utils.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/gps.h> + +struct gps gps = { + 0, + "/dev/ttyACM0", + 0, + + 0, + 0, + 0,0, +}; + +static struct bsc_fd gps_bfd; +static struct termios gps_termios, gps_old_termios; + +static int gps_line(char *line) +{ + time_t gps_now, host_now; + struct tm *tm; + int32_t diff; + double latitude, longitude; + + if (!!strncmp(line, "$GPGLL", 6)) + return 0; + line += 7; + if (strlen(line) < 37) + return 0; + line[37] = '\0'; + /* ddmm.mmmm,N,dddmm.mmmm,E,hhmmss.mmm,A */ + + /* valid position */ + if (line[36] != 'A') { + LOGP(DGPS, LOGL_INFO, "%s (invalid)\n", line); + gps.valid = 0; + return 0; + } + gps.valid = 1; + + /* time stamp */ + gps_now = line[30] - '0'; + gps_now += (line[29] - '0') * 10; + gps_now += (line[28] - '0') * 60; + gps_now += (line[27] - '0') * 600; + gps_now += (line[26] - '0') * 3600; + gps_now += (line[25] - '0') * 36000; + time(&host_now); + /* calculate the number of seconds the host differs from GPS */ + diff = host_now % 86400 - gps_now; + if (diff < 0) + diff += 86400; + if (diff >= 43200) + diff -= 86400; + /* apply the "date" part to the GPS time */ + gps_now = host_now - diff; + gps.gmt = gps_now; + tm = localtime(&gps_now); + + /* position */ + latitude = (double)(line[0] - '0') * 10.0; + latitude += (double)(line[1] - '0'); + latitude += (double)(line[2] - '0') / 6.0; + latitude += (double)(line[3] - '0') / 60.0; + latitude += (double)(line[5] - '0') / 600.0; + latitude += (double)(line[6] - '0') / 6000.0; + latitude += (double)(line[7] - '0') / 60000.0; + latitude += (double)(line[8] - '0') / 600000.0; + if (line[10] == 'S') + latitude = 0.0 - latitude; + gps.latitude = latitude; + longitude = (double)(line[12] - '0') * 100.0; + longitude += (double)(line[13] - '0') * 10.0; + longitude += (double)(line[14] - '0'); + longitude += (double)(line[15] - '0') / 6.0; + longitude += (double)(line[16] - '0') / 60.0; + longitude += (double)(line[18] - '0') / 600.0; + longitude += (double)(line[19] - '0') / 6000.0; + longitude += (double)(line[20] - '0') / 60000.0; + longitude += (double)(line[21] - '0') / 600000.0; + if (line[23] == 'W') + longitude = 360.0 - longitude; + gps.longitude = longitude; + + LOGP(DGPS, LOGL_DEBUG, "%s\n", line); + LOGP(DGPS, LOGL_INFO, " time=%02d:%02d:%02d %04d-%02d-%02d, " + "diff-to-host=%d, latitude=%do%.4f, longitude=%do%.4f\n", + tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_year + 1900, + tm->tm_mday, tm->tm_mon + 1, diff, + (int)gps.latitude, + (gps.latitude - ((int)gps.latitude)) * 60.0, + (int)gps.longitude, + (gps.longitude - ((int)gps.longitude)) * 60.0); + return 0; +} + +static int nmea_checksum(char *line) +{ + uint8_t checksum = 0; + + while (*line) { + if (*line == '$') { + line++; + continue; + } + if (*line == '*') + break; + checksum ^= *line++; + } + return (strtoul(line+1, NULL, 16) == checksum); +} + +int gps_cb(struct bsc_fd *bfd, unsigned int what) +{ + char buff[128]; + static char line[128]; + static int lpos = 0; + int i = 0, len; + + len = read(bfd->fd, buff, sizeof(buff)); + if (len <= 0) { + fprintf(stderr, "error reading GPS device (errno=%d)\n", errno); + return len; + } + while(i < len) { + if (buff[i] == 13) { + i++; + continue; + } + if (buff[i] == 10) { + line[lpos] = '\0'; + lpos = 0; + i++; + if (!nmea_checksum(line)) + fprintf(stderr, "NMEA checksum error\n"); + else + gps_line(line); + continue; + } + line[lpos++] = buff[i++]; + if (lpos == sizeof(line)) + lpos--; + } + + return 0; +} + +int gps_open(void) +{ + int baud = 0; + + if (gps_bfd.fd > 0) + return 0; + + LOGP(DGPS, LOGL_INFO, "Open GPS device '%s'\n", gps.device); + + gps_bfd.data = NULL; + gps_bfd.when = BSC_FD_READ; + gps_bfd.cb = gps_cb; + gps_bfd.fd = open(gps.device, O_RDONLY); + if (gps_bfd.fd < 0) + return gps_bfd.fd; + + switch (gps.baud) { + case 4800: + baud = B4800; break; + case 9600: + baud = B9600; break; + case 19200: + baud = B19200; break; + case 38400: + baud = B38400; break; + case 57600: + baud = B57600; break; + case 115200: + baud = B115200; break; + } + + if (isatty(gps_bfd.fd)) + { + /* get termios */ + tcgetattr(gps_bfd.fd, &gps_old_termios); + tcgetattr(gps_bfd.fd, &gps_termios); + /* set baud */ + if (baud) { + gps_termios.c_cflag |= baud; + cfsetispeed(&gps_termios, baud); + cfsetospeed(&gps_termios, baud); + } + if (tcsetattr(gps_bfd.fd, TCSANOW, &gps_termios)) + printf("Failed to set termios for GPS\n"); + } + + bsc_register_fd(&gps_bfd); + + return 0; +} + +void gps_close(void) +{ + if (gps_bfd.fd <= 0) + return; + + LOGP(DGPS, LOGL_INFO, "Close GPS device\n"); + + bsc_unregister_fd(&gps_bfd); + + if (isatty(gps_bfd.fd)) + tcsetattr(gps_bfd.fd, TCSANOW, &gps_old_termios); + + close(gps_bfd.fd); + gps_bfd.fd = -1; /* -1 or 0 indicates: 'close' */ +} + +void gps_init(void) +{ + memset(&gps_bfd, 0, sizeof(gps_bfd)); +} + + diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c new file mode 100644 index 00000000..ac8bac87 --- /dev/null +++ b/src/host/layer23/src/common/l1ctl.c @@ -0,0 +1,790 @@ +/* Layer1 control code, talking L1CTL protocol with L1 on the phone */ + +/* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <arpa/inet.h> + +#include <l1ctl_proto.h> + +#include <osmocore/signal.h> +#include <osmocore/logging.h> +#include <osmocore/timer.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/gsmtap_util.h> +#include <osmocore/protocol/gsm_04_08.h> +#include <osmocore/protocol/gsm_08_58.h> +#include <osmocore/rsl.h> + +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1l2_interface.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/common/logging.h> + +static struct msgb *osmo_l1_alloc(uint8_t msg_type) +{ + struct l1ctl_hdr *l1h; + struct msgb *msg = msgb_alloc_headroom(256, 4, "osmo_l1"); + + if (!msg) { + LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory.\n"); + return NULL; + } + + msg->l1h = msgb_put(msg, sizeof(*l1h)); + l1h = (struct l1ctl_hdr *) msg->l1h; + l1h->msg_type = msg_type; + + return msg; +} + + +static int osmo_make_band_arfcn(struct osmocom_ms *ms, uint16_t arfcn) +{ + /* TODO: Include the band */ + return arfcn; +} + +static int rx_l1_fbsb_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1ctl_info_dl *dl; + struct l1ctl_fbsb_conf *sb; + struct gsm_time tm; + struct osmobb_fbsb_res fr; + + if (msgb_l3len(msg) < sizeof(*dl) + sizeof(*sb)) { + LOGP(DL1C, LOGL_ERROR, "FBSB RESP: MSG too short %u\n", + msgb_l3len(msg)); + return -1; + } + + dl = (struct l1ctl_info_dl *) msg->l1h; + sb = (struct l1ctl_fbsb_conf *) dl->payload; + + LOGP(DL1C, LOGL_INFO, "snr=%04x, arfcn=%u result=%u\n", dl->snr, + ntohs(dl->band_arfcn), sb->result); + + if (sb->result != 0) { + LOGP(DL1C, LOGL_ERROR, "FBSB RESP: result=%u\n", sb->result); + dispatch_signal(SS_L1CTL, S_L1CTL_FBSB_ERR, ms); + return 0; + } + + gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr)); + DEBUGP(DL1C, "SCH: SNR: %u TDMA: (%.4u/%.2u/%.2u) bsic: %d\n", + dl->snr, tm.t1, tm.t2, tm.t3, sb->bsic); + fr.ms = ms; + fr.snr = dl->snr; + fr.bsic = sb->bsic; + dispatch_signal(SS_L1CTL, S_L1CTL_FBSB_RESP, &fr); + + return 0; +} + +static int rx_l1_rach_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1ctl_info_dl *dl; + + if (msgb_l2len(msg) < sizeof(*dl)) { + LOGP(DL1C, LOGL_ERROR, "RACH CONF: MSG too short %u\n", + msgb_l3len(msg)); + msgb_free(msg); + return -1; + } + + dl = (struct l1ctl_info_dl *) msg->l1h; + + l2_ph_chan_conf(msg, ms, dl); + + return 0; +} + +/* Receive L1CTL_DATA_IND (Data Indication from L1) */ +static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1ctl_info_dl *dl, dl_cpy; + struct l1ctl_data_ind *ccch; + struct lapdm_entity *le; + struct rx_meas_stat *meas = &ms->meas; + uint8_t chan_type, chan_ts, chan_ss; + uint8_t gsmtap_chan_type; + struct gsm_time tm; + + if (msgb_l3len(msg) < sizeof(*ccch)) { + LOGP(DL1C, LOGL_ERROR, "MSG too short Data Ind: %u\n", + msgb_l3len(msg)); + msgb_free(msg); + return -1; + } + + dl = (struct l1ctl_info_dl *) msg->l1h; + msg->l2h = dl->payload; + ccch = (struct l1ctl_data_ind *) msg->l2h; + + gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr)); + rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts); + DEBUGP(DL1C, "%s (%.4u/%.2u/%.2u) %d dBm: %s\n", + rsl_chan_nr_str(dl->chan_nr), tm.t1, tm.t2, tm.t3, + (int)dl->rx_level-110, + hexdump(ccch->data, sizeof(ccch->data))); + + meas->last_fn = ntohl(dl->frame_nr); + meas->frames++; + meas->snr += dl->snr; + meas->berr += dl->num_biterr; + meas->rxlev += dl->rx_level; + + /* counting loss criteria */ + if (!(dl->link_id & 0x40)) { + switch (chan_type) { + case RSL_CHAN_PCH_AGCH: + if (!meas->ds_fail) + break; + if (dl->fire_crc >= 2) + meas->dsc -= 4; + else + meas->dsc += 1; + if (meas->dsc > meas->ds_fail) + meas->dsc = meas->ds_fail; + if (meas->dsc < meas->ds_fail) + printf("LOSS counter for CCCH %d\n", meas->dsc); + if (meas->dsc > 0) + break; + meas->ds_fail = 0; + dispatch_signal(SS_L1CTL, S_L1CTL_LOSS_IND, ms); + break; + } + } else { + switch (chan_type) { + case RSL_CHAN_Bm_ACCHs: + case RSL_CHAN_Lm_ACCHs: + case RSL_CHAN_SDCCH4_ACCH: + case RSL_CHAN_SDCCH8_ACCH: + if (!meas->rl_fail) + break; + if (dl->fire_crc >= 2) + meas->s -= 1; + else + meas->s += 2; + if (meas->s > meas->rl_fail) + meas->s = meas->rl_fail; + if (meas->s < meas->rl_fail) + printf("LOSS counter for ACCH %d\n", meas->s); + if (meas->s > 0) + break; + meas->rl_fail = 0; + dispatch_signal(SS_L1CTL, S_L1CTL_LOSS_IND, ms); + break; + } + } + + if (dl->fire_crc >= 2) { +printf("Dropping frame with %u bit errors\n", dl->num_biterr); + LOGP(DL1C, LOGL_NOTICE, "Dropping frame with %u bit errors\n", + dl->num_biterr); + return 0; + } + + /* send CCCH data via GSMTAP */ + gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, dl->link_id); + gsmtap_sendmsg(ntohs(dl->band_arfcn), chan_ts, gsmtap_chan_type, chan_ss, + tm.fn, dl->rx_level-110, dl->snr, ccch->data, + sizeof(ccch->data)); + + /* determine LAPDm entity based on SACCH or not */ + if (dl->link_id & 0x40) + le = &ms->l2_entity.lapdm_acch; + else + le = &ms->l2_entity.lapdm_dcch; + /* make local stack copy of l1ctl_info_dl, as LAPDm will + * overwrite skb hdr */ + memcpy(&dl_cpy, dl, sizeof(dl_cpy)); + + /* pull the L1 header from the msgb */ + msgb_pull(msg, msg->l2h - (msg->l1h-sizeof(struct l1ctl_hdr))); + msg->l1h = NULL; + + /* send it up into LAPDm */ + l2_ph_data_ind(msg, le, &dl_cpy); + + return 0; +} + +/* Receive L1CTL_DATA_CONF (Data Confirm from L1) */ +static int rx_ph_data_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1ctl_info_dl *dl; + struct lapdm_entity *le; + + dl = (struct l1ctl_info_dl *) msg->l1h; + + /* determine LAPDm entity based on SACCH or not */ + if (dl->link_id & 0x40) + le = &ms->l2_entity.lapdm_acch; + else + le = &ms->l2_entity.lapdm_dcch; + + /* send it up into LAPDm */ + l2_ph_data_conf(msg, le); + + return 0; +} + +/* Transmit L1CTL_DATA_REQ */ +int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg, + uint8_t chan_nr, uint8_t link_id) +{ + struct l1ctl_hdr *l1h; + struct l1ctl_info_ul *l1i_ul; + uint8_t chan_type, chan_ts, chan_ss; + uint8_t gsmtap_chan_type; + + DEBUGP(DL1C, "(%s)\n", hexdump(msg->l2h, msgb_l2len(msg))); + + if (msgb_l2len(msg) > 23) { + LOGP(DL1C, LOGL_ERROR, "L1 cannot handle message length " + "> 23 (%u)\n", msgb_l2len(msg)); + msgb_free(msg); + return -EINVAL; + } else if (msgb_l2len(msg) < 23) + LOGP(DL1C, LOGL_ERROR, "L1 message length < 23 (%u) " + "doesn't seem right!\n", msgb_l2len(msg)); + + /* send copy via GSMTAP */ + rsl_dec_chan_nr(chan_nr, &chan_type, &chan_ss, &chan_ts); + gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, link_id); + gsmtap_sendmsg(0|0x4000, chan_ts, gsmtap_chan_type, chan_ss, + 0, 127, 255, msg->l2h, msgb_l2len(msg)); + + /* prepend uplink info header */ + l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul)); + + l1i_ul->chan_nr = chan_nr; + l1i_ul->link_id = link_id; + + /* prepend l1 header */ + msg->l1h = msgb_push(msg, sizeof(*l1h)); + l1h = (struct l1ctl_hdr *) msg->l1h; + l1h->msg_type = L1CTL_DATA_REQ; + + return osmo_send_l1(ms, msg); +} + +/* Transmit FBSB_REQ */ +int l1ctl_tx_fbsb_req(struct osmocom_ms *ms, uint16_t arfcn, + uint8_t flags, uint16_t timeout, uint8_t sync_info_idx, + uint8_t ccch_mode) +{ + struct msgb *msg; + struct l1ctl_fbsb_req *req; + + LOGP(DL1C, LOGL_INFO, "Sync Req\n"); + + msg = osmo_l1_alloc(L1CTL_FBSB_REQ); + if (!msg) + return -1; + + req = (struct l1ctl_fbsb_req *) msgb_put(msg, sizeof(*req)); + req->band_arfcn = htons(osmo_make_band_arfcn(ms, arfcn)); + req->timeout = htons(timeout); + /* Threshold when to consider FB_MODE1: 4kHz - 1kHz */ + req->freq_err_thresh1 = htons(11000 - 1000); + /* Threshold when to consider SCH: 1kHz - 200Hz */ + req->freq_err_thresh2 = htons(1000 - 200); + /* not used yet! */ + req->num_freqerr_avg = 3; + req->flags = flags; + req->sync_info_idx = sync_info_idx; + req->ccch_mode = ccch_mode; + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_CCCH_MODE_REQ */ +int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode) +{ + struct msgb *msg; + struct l1ctl_ccch_mode_req *req; + + LOGP(DL1C, LOGL_INFO, "CCCH Mode Req\n"); + + msg = osmo_l1_alloc(L1CTL_CCCH_MODE_REQ); + if (!msg) + return -1; + + req = (struct l1ctl_ccch_mode_req *) msgb_put(msg, sizeof(*req)); + req->ccch_mode = ccch_mode; + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_TCH_MODE_REQ */ +int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode) +{ + struct msgb *msg; + struct l1ctl_tch_mode_req *req; + + LOGP(DL1C, LOGL_INFO, "TCH Mode Req\n"); + + msg = osmo_l1_alloc(L1CTL_TCH_MODE_REQ); + if (!msg) + return -1; + + req = (struct l1ctl_tch_mode_req *) msgb_put(msg, sizeof(*req)); + req->tch_mode = tch_mode; + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_PARAM_REQ */ +int l1ctl_tx_param_req(struct osmocom_ms *ms, uint8_t ta, uint8_t tx_power) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + struct l1ctl_par_req *req; + + msg = osmo_l1_alloc(L1CTL_PARAM_REQ); + if (!msg) + return -1; + + DEBUGP(DL1C, "PARAM Req. ta=%d, tx_power=%d\n", ta, tx_power); + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + req = (struct l1ctl_par_req *) msgb_put(msg, sizeof(*req)); + req->tx_power = tx_power; + req->ta = ta; + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_CRYPTO_REQ */ +int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t algo, uint8_t *key, + uint8_t len) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + struct l1ctl_crypto_req *req; + + msg = osmo_l1_alloc(L1CTL_CRYPTO_REQ); + if (!msg) + return -1; + + DEBUGP(DL1C, "CRYPTO Req. algo=%d, len=%d\n", algo, len); + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + req = (struct l1ctl_crypto_req *) msgb_put(msg, sizeof(*req) + len); + req->algo = algo; + if (len) + memcpy(req->key, key, len); + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_RACH_REQ */ +int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset, + uint8_t combined) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + struct l1ctl_rach_req *req; + + msg = osmo_l1_alloc(L1CTL_RACH_REQ); + if (!msg) + return -1; + + DEBUGP(DL1C, "RACH Req. offset=%d combined=%d\n", offset, combined); + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + req = (struct l1ctl_rach_req *) msgb_put(msg, sizeof(*req)); + req->ra = ra; + req->offset = htons(offset); + req->combined = combined; + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_DM_EST_REQ */ +int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, + uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + struct l1ctl_dm_est_req *req; + + msg = osmo_l1_alloc(L1CTL_DM_EST_REQ); + if (!msg) + return -1; + + LOGP(DL1C, LOGL_INFO, "Tx Dedic.Mode Est Req (arfcn=%u, " + "chan_nr=0x%02x)\n", band_arfcn, chan_nr); + + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + ul->chan_nr = chan_nr; + ul->link_id = 0; + + req = (struct l1ctl_dm_est_req *) msgb_put(msg, sizeof(*req)); + req->tsc = tsc; + req->h = 0; + req->h0.band_arfcn = htons(band_arfcn); + req->tch_mode = tch_mode; + + return osmo_send_l1(ms, msg); +} + +int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, + uint16_t *ma, uint8_t ma_len, + uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + struct l1ctl_dm_est_req *req; + int i; + + msg = osmo_l1_alloc(L1CTL_DM_EST_REQ); + if (!msg) + return -1; + + LOGP(DL1C, LOGL_INFO, "Tx Dedic.Mode Est Req (maio=%u, hsn=%u, " + "chan_nr=0x%02x)\n", maio, hsn, chan_nr); + + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + ul->chan_nr = chan_nr; + ul->link_id = 0; + + req = (struct l1ctl_dm_est_req *) msgb_put(msg, sizeof(*req)); + req->tsc = tsc; + req->h = 1; + req->h1.maio = maio; + req->h1.hsn = hsn; + req->h1.n = ma_len; + for (i = 0; i < ma_len; i++) + req->h1.ma[i] = htons(ma[i]); + req->tch_mode = tch_mode; + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_DM_FREQ_REQ */ +int l1ctl_tx_dm_freq_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, + uint8_t tsc, uint16_t fn) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + struct l1ctl_dm_freq_req *req; + + msg = osmo_l1_alloc(L1CTL_DM_FREQ_REQ); + if (!msg) + return -1; + + LOGP(DL1C, LOGL_INFO, "Tx Dedic.Mode Freq Req (arfcn=%u, fn=%d)\n", + band_arfcn, fn); + + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + ul->chan_nr = 0; + ul->link_id = 0; + + req = (struct l1ctl_dm_freq_req *) msgb_put(msg, sizeof(*req)); + req->fn = htons(fn); + req->tsc = tsc; + req->h = 0; + req->h0.band_arfcn = htons(band_arfcn); + + return osmo_send_l1(ms, msg); +} + +int l1ctl_tx_dm_freq_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, + uint16_t *ma, uint8_t ma_len, + uint8_t tsc, uint16_t fn) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + struct l1ctl_dm_freq_req *req; + int i; + + msg = osmo_l1_alloc(L1CTL_DM_FREQ_REQ); + if (!msg) + return -1; + + LOGP(DL1C, LOGL_INFO, "Tx Dedic.Mode Freq Req (maio=%u, hsn=%u, " + "fn=%d)\n", maio, hsn, fn); + + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + ul->chan_nr = 0; + ul->link_id = 0; + + req = (struct l1ctl_dm_freq_req *) msgb_put(msg, sizeof(*req)); + req->fn = htons(fn); + req->tsc = tsc; + req->h = 1; + req->h1.maio = maio; + req->h1.hsn = hsn; + req->h1.n = ma_len; + for (i = 0; i < ma_len; i++) + req->h1.ma[i] = htons(ma[i]); + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_DM_REL_REQ */ +int l1ctl_tx_dm_rel_req(struct osmocom_ms *ms) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + + msg = osmo_l1_alloc(L1CTL_DM_REL_REQ); + if (!msg) + return -1; + + LOGP(DL1C, LOGL_INFO, "Tx Dedic.Mode Rel Req\n"); + + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + + return osmo_send_l1(ms, msg); +} + +int l1ctl_tx_echo_req(struct osmocom_ms *ms, unsigned int len) +{ + struct msgb *msg; + uint8_t *data; + unsigned int i; + + msg = osmo_l1_alloc(L1CTL_ECHO_REQ); + if (!msg) + return -1; + + data = msgb_put(msg, len); + for (i = 0; i < len; i++) + data[i] = i % 8; + + return osmo_send_l1(ms, msg); +} + +int l1ctl_tx_sim_req(struct osmocom_ms *ms, uint8_t *data, uint16_t length) +{ + struct msgb *msg; + uint8_t *dat; + + msg = osmo_l1_alloc(L1CTL_SIM_REQ); + if (!msg) + return -1; + + dat = msgb_put(msg, length); + memcpy(dat, data, length); + + return osmo_send_l1(ms, msg); +} + +/* just forward the SIM response to the SIM handler */ +static int rx_l1_sim_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + uint16_t len = msg->len - sizeof(struct l1ctl_hdr); + uint8_t *data = msg->data + sizeof(struct l1ctl_hdr); + + LOGP(DL1C, LOGL_INFO, "SIM %s\n", hexdump(data, len)); + + /* pull the L1 header from the msgb */ + msgb_pull(msg, sizeof(struct l1ctl_hdr)); + msg->l1h = NULL; + + sim_apdu_resp(ms, msg); + + return 0; +} + +/* Transmit L1CTL_PM_REQ */ +int l1ctl_tx_pm_req_range(struct osmocom_ms *ms, uint16_t arfcn_from, + uint16_t arfcn_to) +{ + struct msgb *msg; + struct l1ctl_pm_req *pm; + + msg = osmo_l1_alloc(L1CTL_PM_REQ); + if (!msg) + return -1; + + LOGP(DL1C, LOGL_INFO, "Tx PM Req (%u-%u)\n", arfcn_from, arfcn_to); + pm = (struct l1ctl_pm_req *) msgb_put(msg, sizeof(*pm)); + pm->type = 1; + pm->range.band_arfcn_from = htons(arfcn_from); + pm->range.band_arfcn_to = htons(arfcn_to); + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_RESET_REQ */ +int l1ctl_tx_reset_req(struct osmocom_ms *ms, uint8_t type) +{ + struct msgb *msg; + struct l1ctl_reset *res; + + msg = osmo_l1_alloc(L1CTL_RESET_REQ); + if (!msg) + return -1; + + LOGP(DL1C, LOGL_INFO, "Tx Reset Req (%u)\n", type); + res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res)); + res->type = type; + + return osmo_send_l1(ms, msg); +} + +/* Receive L1CTL_RESET_IND */ +static int rx_l1_reset(struct osmocom_ms *ms) +{ + LOGP(DL1C, LOGL_INFO, "Layer1 Reset indication\n"); + dispatch_signal(SS_L1CTL, S_L1CTL_RESET, ms); + + return 0; +} + +/* Receive L1CTL_PM_CONF */ +static int rx_l1_pm_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1ctl_pm_conf *pmr; + + for (pmr = (struct l1ctl_pm_conf *) msg->l1h; + (uint8_t *) pmr < msg->tail; pmr++) { + struct osmobb_meas_res mr; + DEBUGP(DL1C, "PM MEAS: ARFCN: %4u RxLev: %3d %3d\n", + ntohs(pmr->band_arfcn), pmr->pm[0], pmr->pm[1]); + mr.band_arfcn = ntohs(pmr->band_arfcn); + mr.rx_lev = pmr->pm[0]; + mr.ms = ms; + dispatch_signal(SS_L1CTL, S_L1CTL_PM_RES, &mr); + } + return 0; +} + +/* Receive L1CTL_CCCH_MODE_CONF */ +static int rx_l1_ccch_mode_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct osmobb_ccch_mode_conf mc; + struct l1ctl_ccch_mode_conf *conf; + + if (msgb_l3len(msg) < sizeof(*conf)) { + LOGP(DL1C, LOGL_ERROR, "CCCH MODE CONF: MSG too short %u\n", + msgb_l3len(msg)); + return -1; + } + + conf = (struct l1ctl_ccch_mode_conf *) msg->l1h; + + LOGP(DL1C, LOGL_INFO, "CCCH MODE CONF: mode=%u\n", conf->ccch_mode); + + mc.ccch_mode = conf->ccch_mode; + mc.ms = ms; + dispatch_signal(SS_L1CTL, S_L1CTL_CCCH_MODE_CONF, &mc); + + return 0; +} + +/* Receive L1CTL_TCH_MODE_CONF */ +static int rx_l1_tch_mode_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct osmobb_tch_mode_conf mc; + struct l1ctl_tch_mode_conf *conf; + + if (msgb_l3len(msg) < sizeof(*conf)) { + LOGP(DL1C, LOGL_ERROR, "TCH MODE CONF: MSG too short %u\n", + msgb_l3len(msg)); + return -1; + } + + conf = (struct l1ctl_tch_mode_conf *) msg->l1h; + + LOGP(DL1C, LOGL_INFO, "TCH MODE CONF: mode=%u\n", conf->tch_mode); + + mc.tch_mode = conf->tch_mode; + mc.ms = ms; + dispatch_signal(SS_L1CTL, S_L1CTL_TCH_MODE_CONF, &mc); + + return 0; +} + +/* Receive incoming data from L1 using L1CTL format */ +int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg) +{ + int rc = 0; + struct l1ctl_hdr *l1h; + struct l1ctl_info_dl *dl; + + if (msgb_l2len(msg) < sizeof(*dl)) { + LOGP(DL1C, LOGL_ERROR, "Short Layer2 message: %u\n", + msgb_l2len(msg)); + msgb_free(msg); + return -1; + } + + l1h = (struct l1ctl_hdr *) msg->l1h; + + /* move the l1 header pointer to point _BEHIND_ l1ctl_hdr, + as the l1ctl header is of no interest to subsequent code */ + msg->l1h = l1h->data; + + switch (l1h->msg_type) { + case L1CTL_FBSB_CONF: + rc = rx_l1_fbsb_conf(ms, msg); + msgb_free(msg); + break; + case L1CTL_DATA_IND: + rc = rx_ph_data_ind(ms, msg); + break; + case L1CTL_DATA_CONF: + rc = rx_ph_data_conf(ms, msg); + break; + case L1CTL_RESET_IND: + case L1CTL_RESET_CONF: + rc = rx_l1_reset(ms); + msgb_free(msg); + break; + case L1CTL_PM_CONF: + rc = rx_l1_pm_conf(ms, msg); + if (l1h->flags & L1CTL_F_DONE) + dispatch_signal(SS_L1CTL, S_L1CTL_PM_DONE, ms); + msgb_free(msg); + break; + case L1CTL_RACH_CONF: + rc = rx_l1_rach_conf(ms, msg); + break; + case L1CTL_CCCH_MODE_CONF: + rc = rx_l1_ccch_mode_conf(ms, msg); + msgb_free(msg); + break; + case L1CTL_TCH_MODE_CONF: + rc = rx_l1_tch_mode_conf(ms, msg); + msgb_free(msg); + break; + case L1CTL_SIM_CONF: + rc = rx_l1_sim_conf(ms, msg); + break; + default: + LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type); + msgb_free(msg); + break; + } + + return rc; +} diff --git a/src/host/layer23/src/common/l1l2_interface.c b/src/host/layer23/src/common/l1l2_interface.c new file mode 100644 index 00000000..74c88754 --- /dev/null +++ b/src/host/layer23/src/common/l1l2_interface.c @@ -0,0 +1,179 @@ +/* Layer 1 socket interface of layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/l1l2_interface.h> + +#include <osmocore/utils.h> + +#include <sys/socket.h> +#include <sys/un.h> + +#include <arpa/inet.h> + +#define _GNU_SOURCE +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +#define GSM_L2_LENGTH 256 +#define GSM_L2_HEADROOM 32 + +static int layer2_read(struct bsc_fd *fd) +{ + struct msgb *msg; + u_int16_t len; + int rc; + + msg = msgb_alloc_headroom(GSM_L2_LENGTH+GSM_L2_HEADROOM, GSM_L2_HEADROOM, "Layer2"); + if (!msg) { + LOGP(DL1C, LOGL_ERROR, "Failed to allocate msg.\n"); + return -ENOMEM; + } + + rc = read(fd->fd, &len, sizeof(len)); + if (rc < sizeof(len)) { + fprintf(stderr, "Layer2 socket failed\n"); + msgb_free(msg); + if (rc >= 0) + rc = -EIO; + layer2_close((struct osmocom_ms *) fd->data); + return rc; + } + + len = ntohs(len); + if (len > GSM_L2_LENGTH) { + LOGP(DL1C, LOGL_ERROR, "Length is too big: %u\n", len); + msgb_free(msg); + return -EINVAL; + } + + + msg->l1h = msgb_put(msg, len); + rc = read(fd->fd, msg->l1h, msgb_l1len(msg)); + if (rc != msgb_l1len(msg)) { + LOGP(DL1C, LOGL_ERROR, "Can not read data: len=%d rc=%d " + "errno=%d\n", len, rc, errno); + msgb_free(msg); + return rc; + } + + l1ctl_recv((struct osmocom_ms *) fd->data, msg); + + return 0; +} + +static int layer2_write(struct bsc_fd *fd, struct msgb *msg) +{ + int rc; + + if (fd->fd <= 0) + return -EINVAL; + + rc = write(fd->fd, msg->data, msg->len); + if (rc != msg->len) { + LOGP(DL1C, LOGL_ERROR, "Failed to write data: rc: %d\n", rc); + return rc; + } + + return 0; +} + +int layer2_open(struct osmocom_ms *ms, const char *socket_path) +{ + int rc; + struct sockaddr_un local; + + ms->l2_wq.bfd.fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (ms->l2_wq.bfd.fd < 0) { + fprintf(stderr, "Failed to create unix domain socket.\n"); + return ms->l2_wq.bfd.fd; + } + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, socket_path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + + rc = connect(ms->l2_wq.bfd.fd, (struct sockaddr *) &local, + sizeof(local)); + if (rc < 0) { + fprintf(stderr, "Failed to connect to '%s': %s\n", local.sun_path, + strerror(errno)); + close(ms->l2_wq.bfd.fd); + return rc; + } + + write_queue_init(&ms->l2_wq, 100); + ms->l2_wq.bfd.data = ms; + ms->l2_wq.bfd.when = BSC_FD_READ; + ms->l2_wq.read_cb = layer2_read; + ms->l2_wq.write_cb = layer2_write; + + rc = bsc_register_fd(&ms->l2_wq.bfd); + if (rc != 0) { + fprintf(stderr, "Failed to register fd.\n"); + close(ms->l2_wq.bfd.fd); + return rc; + } + + return 0; +} + +int layer2_close(struct osmocom_ms *ms) +{ + if (ms->l2_wq.bfd.fd <= 0) + return -EINVAL; + + close(ms->l2_wq.bfd.fd); + ms->l2_wq.bfd.fd = -1; + bsc_unregister_fd(&ms->l2_wq.bfd); + + return 0; +} + +int osmo_send_l1(struct osmocom_ms *ms, struct msgb *msg) +{ + uint16_t *len; + + DEBUGP(DL1C, "Sending: '%s'\n", hexdump(msg->data, msg->len)); + + if (msg->l1h != msg->data) + LOGP(DL1C, LOGL_ERROR, "Message L1 header != Message Data\n"); + + /* prepend 16bit length before sending */ + len = (uint16_t *) msgb_push(msg, sizeof(*len)); + *len = htons(msg->len - sizeof(*len)); + + if (write_queue_enqueue(&ms->l2_wq, msg) != 0) { + LOGP(DL1C, LOGL_ERROR, "Failed to enqueue msg.\n"); + msgb_free(msg); + return -1; + } + + return 0; +} + + diff --git a/src/host/layer23/src/common/lapdm.c b/src/host/layer23/src/common/lapdm.c new file mode 100644 index 00000000..dc9c9165 --- /dev/null +++ b/src/host/layer23/src/common/lapdm.c @@ -0,0 +1,2305 @@ +/* GSM LAPDm (TS 04.06) implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* Notes on Buffering: rcv_buffer, tx_queue, tx_hist, send_buffer, send_queue + * + * RX data is stored in the rcv_buffer (pointer). If the message is complete, it + * is removed from rcv_buffer pointer and forwarded to L3. If the RX data is + * received while there is an incomplete rcv_buffer, it is appended to it. + * + * TX data is stored in the send_queue first. When transmitting a frame, + * the first message in the send_queue is moved to the send_buffer. There it + * resides until all fragments are acknowledged. Fragments to be sent by I + * frames are stored in the tx_hist buffer for resend, if required. Also the + * current fragment is copied into the tx_queue. There it resides until it is + * forwarded to layer 1. + * + * In case we have SAPI 0, we only have a window size of 1, so the unack- + * nowledged message resides always in the send_buffer. In case of a suspend, + * it can be written back to the first position of the send_queue. + * + * The layer 1 normally sends a PH-READY-TO-SEND. But because we use + * asynchronous transfer between layer 1 and layer 2 (serial link), we must + * send a frame before layer 1 reaches the right timeslot to send it. So we + * move the tx_queue to layer 1 when there is not already a pending frame, and + * wait until acknowledge after the frame has been sent. If we receive an + * acknowledge, we can send the next frame from the buffer, if any. + * + * The moving of tx_queue to layer 1 may also trigger T200, if desired. Also it + * will trigger next I frame, if possible. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <arpa/inet.h> + +#include <osmocore/logging.h> +#include <osmocore/timer.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/utils.h> +#include <osmocore/rsl.h> +#include <osmocore/protocol/gsm_04_08.h> +#include <osmocore/protocol/gsm_08_58.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/common/logging.h> + +#include <l1ctl_proto.h> + +/* TS 04.06 Figure 4 / Section 3.2 */ +#define LAPDm_LPD_NORMAL 0 +#define LAPDm_LPD_SMSCB 1 +#define LAPDm_SAPI_NORMAL 0 +#define LAPDm_SAPI_SMS 3 +#define LAPDm_ADDR(lpd, sapi, cr) ((((lpd) & 0x3) << 5) | (((sapi) & 0x7) << 2) | (((cr) & 0x1) << 1) | 0x1) + +#define LAPDm_ADDR_SAPI(addr) (((addr) >> 2) & 0x7) +#define LAPDm_ADDR_CR(addr) (((addr) >> 1) & 0x1) +#define LAPDm_ADDR_EA(addr) ((addr) & 0x1) + +/* TS 04.06 Table 3 / Section 3.4.3 */ +#define LAPDm_CTRL_I(nr, ns, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((ns) & 0x7) << 1)) +#define LAPDm_CTRL_S(nr, s, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((s) & 0x3) << 2) | 0x1) +#define LAPDm_CTRL_U(u, p) ((((u) & 0x1c) << (5-2)) | (((p) & 0x1) << 4) | (((u) & 0x3) << 2) | 0x3) + +#define LAPDm_CTRL_is_I(ctrl) (((ctrl) & 0x1) == 0) +#define LAPDm_CTRL_is_S(ctrl) (((ctrl) & 0x3) == 1) +#define LAPDm_CTRL_is_U(ctrl) (((ctrl) & 0x3) == 3) + +#define LAPDm_CTRL_U_BITS(ctrl) ((((ctrl) & 0xC) >> 2) | ((ctrl) & 0xE0) >> 3) +#define LAPDm_CTRL_PF_BIT(ctrl) (((ctrl) >> 4) & 0x1) + +#define LAPDm_CTRL_S_BITS(ctrl) (((ctrl) & 0xC) >> 2) + +#define LAPDm_CTRL_I_Ns(ctrl) (((ctrl) & 0xE) >> 1) +#define LAPDm_CTRL_Nr(ctrl) (((ctrl) & 0xE0) >> 5) + +/* TS 04.06 Table 4 / Section 3.8.1 */ +#define LAPDm_U_SABM 0x7 +#define LAPDm_U_DM 0x3 +#define LAPDm_U_UI 0x0 +#define LAPDm_U_DISC 0x8 +#define LAPDm_U_UA 0xC + +#define LAPDm_S_RR 0x0 +#define LAPDm_S_RNR 0x1 +#define LAPDm_S_REJ 0x2 + +#define LAPDm_LEN(len) ((len << 2) | 0x1) +#define LAPDm_MORE 0x2 + +/* TS 04.06 Section 5.8.3 */ +#define N201_AB_SACCH 18 +#define N201_AB_SDCCH 20 +#define N201_AB_FACCH 20 +#define N201_Bbis 23 +#define N201_Bter_SACCH 21 +#define N201_Bter_SDCCH 23 +#define N201_Bter_FACCH 23 +#define N201_B4 19 + +/* 5.8.2.1 N200 during establish and release */ +#define N200_EST_REL 5 +/* 5.8.2.1 N200 during timer recovery state */ +#define N200_TR_SACCH 5 +#define N200_TR_SDCCH 23 +#define N200_TR_FACCH_FR 34 +#define N200_TR_EFACCH_FR 48 +#define N200_TR_FACCH_HR 29 +/* FIXME: this depends on chan type */ +#define N200 N200_TR_SACCH + +#define CR_MS2BS_CMD 0 +#define CR_MS2BS_RESP 1 +#define CR_BS2MS_CMD 1 +#define CR_BS2MS_RESP 0 + +/* Set T200 to 1 Second (OpenBTS uses 900ms) */ +#define T200 1, 0 + +/* k value for each SAPI */ +static uint8_t k_sapi[] = {1, 1, 1, 1, 1, 1, 1, 1}; + +enum lapdm_format { + LAPDm_FMT_A, + LAPDm_FMT_B, + LAPDm_FMT_Bbis, + LAPDm_FMT_Bter, + LAPDm_FMT_B4, +}; + +static void lapdm_t200_cb(void *data); +static int rslms_send_i(struct lapdm_msg_ctx *mctx, int line); + +/* UTILITY FUNCTIONS */ + +static inline uint8_t inc_mod8(uint8_t x) +{ + return (x + 1) & 7; +} + +static inline uint8_t add_mod8(uint8_t x, uint8_t y) +{ + return (x + y) & 7; +} + +static inline uint8_t sub_mod8(uint8_t x, uint8_t y) +{ + return (x - y) & 7; /* handle negative results correctly */ +} + +static void lapdm_dl_init(struct lapdm_datalink *dl, + struct lapdm_entity *entity) +{ + memset(dl, 0, sizeof(*dl)); + INIT_LLIST_HEAD(&dl->send_queue); + INIT_LLIST_HEAD(&dl->tx_queue); + dl->state = LAPDm_STATE_IDLE; + dl->t200.data = dl; + dl->t200.cb = &lapdm_t200_cb; + dl->entity = entity; +} + +void lapdm_init(struct lapdm_entity *le, struct osmocom_ms *ms) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(le->datalink); i++) + lapdm_dl_init(&le->datalink[i], le); + + le->ms = ms; +} + +static void lapdm_dl_flush_send(struct lapdm_datalink *dl) +{ + struct msgb *msg; + + /* Flush send-queue */ + while ((msg = msgb_dequeue(&dl->send_queue))) + msgb_free(msg); + + /* Clear send-buffer */ + if (dl->send_buffer) { + msgb_free(dl->send_buffer); + dl->send_buffer = NULL; + } +} + +static void lapdm_dl_flush_tx(struct lapdm_datalink *dl) +{ + struct msgb *msg; + unsigned int i; + + while ((msg = msgb_dequeue(&dl->tx_queue))) + msgb_free(msg); + for (i = 0; i < 8; i++) + dl->tx_length[i] = 0; +} + +void lapdm_exit(struct lapdm_entity *le) +{ + unsigned int i; + struct lapdm_datalink *dl; + + for (i = 0; i < ARRAY_SIZE(le->datalink); i++) { + dl = &le->datalink[i]; + lapdm_dl_flush_tx(dl); + lapdm_dl_flush_send(dl); + if (dl->rcv_buffer) + msgb_free(dl->rcv_buffer); + } +} + +static void lapdm_dl_newstate(struct lapdm_datalink *dl, uint32_t state) +{ + LOGP(DLAPDM, LOGL_INFO, "new state %s -> %s\n", + lapdm_state_names[dl->state], lapdm_state_names[state]); + + dl->state = state; +} + +static struct lapdm_datalink *datalink_for_sapi(struct lapdm_entity *le, uint8_t sapi) +{ + switch (sapi) { + case LAPDm_SAPI_NORMAL: + return &le->datalink[0]; + case LAPDm_SAPI_SMS: + return &le->datalink[1]; + default: + return NULL; + } +} + +/* remove the L2 header from a MSGB */ +static inline unsigned char *msgb_pull_l2h(struct msgb *msg) +{ + unsigned char *ret = msgb_pull(msg, msg->l3h - msg->l2h); + msg->l2h = NULL; + return ret; +} + +/* Append padding (if required) */ +static void lapdm_pad_msgb(struct msgb *msg, uint8_t n201) +{ + int pad_len = n201 - msgb_l2len(msg); + uint8_t *data; + + if (pad_len < 0) { + LOGP(DLAPDM, LOGL_ERROR, + "cannot pad message that is already too big!\n"); + return; + } + + data = msgb_put(msg, pad_len); + memset(data, 0x2B, pad_len); +} + +/* write a frame into the tx queue */ +static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg, + uint8_t chan_nr, uint8_t link_id, uint8_t n201) +{ + struct lapdm_entity *le = dl->entity; + struct osmocom_ms *ms = le->ms; + + /* if there is a pending message, queue it */ + if (le->tx_pending) { + *msgb_push(msg, 1) = n201; + *msgb_push(msg, 1) = link_id; + *msgb_push(msg, 1) = chan_nr; + msgb_enqueue(&dl->tx_queue, msg); + return -EBUSY; + } + + /* send the frame now */ + le->tx_pending = 0; /* disabled flow control */ + lapdm_pad_msgb(msg, n201); + return l1ctl_tx_data_req(ms, msg, chan_nr, link_id); +} + +/* get next frame from the tx queue. because the ms has multiple datalinks, + * each datalink's queue is read round-robin. + */ +int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le) +{ + struct osmocom_ms *ms = le->ms; + struct lapdm_datalink *dl; + int last = le->last_tx_dequeue; + int i = last, n = ARRAY_SIZE(le->datalink); + uint8_t chan_nr, link_id, n201; + + /* we may send again */ + le->tx_pending = 0; + + /* free confirm message */ + msgb_free(msg); + + /* round-robin dequeue */ + do { + /* next */ + i = (i + 1) % n; + dl = &le->datalink[i]; + if ((msg = msgb_dequeue(&dl->tx_queue))) + break; + } while (i != last); + + /* no message in all queues */ + if (!msg) + return 0; + + /* Pull chan_nr and link_id */ + chan_nr = *msg->data; + msgb_pull(msg, 1); + link_id = *msg->data; + msgb_pull(msg, 1); + n201 = *msg->data; + msgb_pull(msg, 1); + + /* Set last dequeue position */ + le->last_tx_dequeue = i; + + /* Pad the frame, we can transmit now */ + le->tx_pending = 1; + lapdm_pad_msgb(msg, n201); + return l1ctl_tx_data_req(ms, msg, chan_nr, link_id); +} + +/* Create RSLms various RSLms messages */ +static int send_rslms_rll_l3(uint8_t msg_type, struct lapdm_msg_ctx *mctx, + struct msgb *msg) +{ + /* Add the RSL + RLL header */ + rsl_rll_push_l3(msg, msg_type, mctx->chan_nr, mctx->link_id, 1); + + /* send off the RSLms message to L3 */ + return rslms_sendmsg(msg, mctx->dl->entity->ms); +} + +/* Take a B4 format message from L1 and create RSLms UNIT DATA IND */ +static int send_rslms_rll_l3_ui(struct lapdm_msg_ctx *mctx, struct msgb *msg) +{ + uint8_t l3_len = msg->tail - (uint8_t *)msgb_l3(msg); + struct abis_rsl_rll_hdr *rllh; + + /* Add the RSL + RLL header */ + msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); + msgb_push(msg, 2 + 2); + rsl_rll_push_hdr(msg, RSL_MT_UNIT_DATA_IND, mctx->chan_nr, + mctx->link_id, 1); + rllh = (struct abis_rsl_rll_hdr *)msgb_l2(msg); + + rllh->data[0] = RSL_IE_TIMING_ADVANCE; + rllh->data[1] = mctx->ta_ind; + + rllh->data[2] = RSL_IE_MS_POWER; + rllh->data[3] = mctx->tx_power_ind; + + return rslms_sendmsg(msg, mctx->dl->entity->ms); +} + +static int send_rll_simple(uint8_t msg_type, struct lapdm_msg_ctx *mctx) +{ + struct msgb *msg; + + msg = rsl_rll_simple(msg_type, mctx->chan_nr, mctx->link_id, 1); + + /* send off the RSLms message to L3 */ + return rslms_sendmsg(msg, mctx->dl->entity->ms); +} + +static int rsl_rll_error(uint8_t cause, struct lapdm_msg_ctx *mctx) +{ + struct msgb *msg; + uint8_t *tlv; + + LOGP(DLAPDM, LOGL_NOTICE, "sending MDL-ERROR-IND %d\n", cause); + msg = rsl_rll_simple(RSL_MT_ERROR_IND, mctx->chan_nr, mctx->link_id, 1); + msg->l2h = msgb_put(msg, sizeof(struct abis_rsl_rll_hdr) + 3); + tlv = msg->l2h + sizeof(struct abis_rsl_rll_hdr); + tlv[0] = RSL_IE_RLM_CAUSE; + tlv[1] = 1; + tlv[2] = cause; + return rslms_sendmsg(msg, mctx->dl->entity->ms); +} + +static int check_length_ind(struct lapdm_msg_ctx *mctx, uint8_t length_ind) +{ + if (!(length_ind & 0x01)) { + /* G.4.1 If the EL bit is set to "0", an MDL-ERROR-INDICATION + * primitive with cause "frame not implemented" is sent to the + * mobile management entity. */ + LOGP(DLAPDM, LOGL_NOTICE, + "we don't support multi-octet length\n"); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + return 0; +} + +static int lapdm_send_resend(struct lapdm_datalink *dl) +{ + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm resend"); + int length; + + /* Resend SABM/DISC from tx_hist */ + length = dl->tx_length[0]; + msg->l2h = msgb_put(msg, length); + memcpy(msg->l2h, dl->tx_hist[dl->V_send], length); + + return tx_ph_data_enqueue(dl, msg, dl->mctx.chan_nr, dl->mctx.link_id, + dl->mctx.n201); +} + +static int lapdm_send_ua(struct lapdm_msg_ctx *mctx, uint8_t len, uint8_t *data) +{ + uint8_t sapi = mctx->link_id & 7; + uint8_t f_bit = LAPDm_CTRL_PF_BIT(mctx->ctrl); + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm UA"); + msg->l2h = msgb_put(msg, 3 + len); + + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP); + msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UA, f_bit); + msg->l2h[2] = LAPDm_LEN(len); + if (len) + memcpy(msg->l2h + 3, data, len); + + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, + mctx->n201); +} + +static int lapdm_send_dm(struct lapdm_msg_ctx *mctx) +{ + uint8_t sapi = mctx->link_id & 7; + uint8_t f_bit = LAPDm_CTRL_PF_BIT(mctx->ctrl); + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm DM"); + msg->l2h = msgb_put(msg, 3); + + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP); + msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_DM, f_bit); + msg->l2h[2] = 0; + + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, + mctx->n201); +} + +static int lapdm_send_rr(struct lapdm_msg_ctx *mctx, uint8_t f_bit) +{ + uint8_t sapi = mctx->link_id & 7; + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm RR"); + msg->l2h = msgb_put(msg, 3); + + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP); + msg->l2h[1] = LAPDm_CTRL_S(mctx->dl->V_recv, LAPDm_S_RR, f_bit); + msg->l2h[2] = LAPDm_LEN(0); + + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, + mctx->n201); +} + +static int lapdm_send_rnr(struct lapdm_msg_ctx *mctx, uint8_t f_bit) +{ + uint8_t sapi = mctx->link_id & 7; + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm RNR"); + msg->l2h = msgb_put(msg, 3); + + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP); + msg->l2h[1] = LAPDm_CTRL_S(mctx->dl->V_recv, LAPDm_S_RNR, f_bit); + msg->l2h[2] = LAPDm_LEN(0); + + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, + mctx->n201); +} + +static int lapdm_send_rej(struct lapdm_msg_ctx *mctx, uint8_t f_bit) +{ + uint8_t sapi = mctx->link_id & 7; + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm REJ"); + msg->l2h = msgb_put(msg, 3); + + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP); + msg->l2h[1] = LAPDm_CTRL_S(mctx->dl->V_recv, LAPDm_S_REJ, f_bit); + msg->l2h[2] = LAPDm_LEN(0); + + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, + mctx->n201); +} + +/* Timer callback on T200 expiry */ +static void lapdm_t200_cb(void *data) +{ + struct lapdm_datalink *dl = data; + + LOGP(DLAPDM, LOGL_INFO, "lapdm_t200_cb(%p) state=%u\n", dl, dl->state); + + switch (dl->state) { + case LAPDm_STATE_SABM_SENT: + /* 5.4.1.3 */ + if (dl->retrans_ctr + 1 >= N200_EST_REL + 1) { + /* send RELEASE INDICATION to L3 */ + send_rll_simple(RSL_MT_REL_IND, &dl->mctx); + /* send MDL ERROR INIDCATION to L3 */ + rsl_rll_error(RLL_CAUSE_T200_EXPIRED, &dl->mctx); + /* flush tx buffers */ + lapdm_dl_flush_tx(dl); + /* go back to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + /* NOTE: we must not change any other states or buffers + * and queues, since we may reconnect after handover + * failure. the buffered messages is replaced there */ + break; + } + /* retransmit SABM command */ + lapdm_send_resend(dl); + /* increment re-transmission counter */ + dl->retrans_ctr++; + /* restart T200 (PH-READY-TO-SEND) */ + bsc_schedule_timer(&dl->t200, T200); + break; + case LAPDm_STATE_DISC_SENT: + /* 5.4.4.3 */ + if (dl->retrans_ctr + 1 >= N200_EST_REL + 1) { + /* send RELEASE INDICATION to L3 */ + send_rll_simple(RSL_MT_REL_CONF, &dl->mctx); + /* send MDL ERROR INIDCATION to L3 */ + rsl_rll_error(RLL_CAUSE_T200_EXPIRED, &dl->mctx); + /* flush buffers */ + lapdm_dl_flush_tx(dl); + lapdm_dl_flush_send(dl); + /* go back to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + /* NOTE: we must not change any other states or buffers + * and queues, since we may reconnect after handover + * failure. the buffered messages is replaced there */ + break; + } + /* retransmit DISC command */ + lapdm_send_resend(dl); + /* increment re-transmission counter */ + dl->retrans_ctr++; + /* restart T200 (PH-READY-TO-SEND) */ + bsc_schedule_timer(&dl->t200, T200); + break; + case LAPDm_STATE_MF_EST: + /* 5.5.7 */ + dl->retrans_ctr = 0; + lapdm_dl_newstate(dl, LAPDm_STATE_TIMER_RECOV); + /* fall through */ + case LAPDm_STATE_TIMER_RECOV: + dl->retrans_ctr++; + if (dl->retrans_ctr < N200) { + /* retransmit I frame (V_s-1) with P=1, if any */ + if (dl->tx_length[sub_mod8(dl->V_send, 1)]) { + struct msgb *msg; + int length; + + LOGP(DLAPDM, LOGL_INFO, "retransmit last frame " + "V(S)=%d\n", sub_mod8(dl->V_send, 1)); + /* Create I frame (segment) from tx_hist */ + length = dl->tx_length[sub_mod8(dl->V_send, 1)]; + msg = msgb_alloc_headroom(23+10, 10, "LAPDm I"); + msg->l2h = msgb_put(msg, length); + memcpy(msg->l2h, + dl->tx_hist[sub_mod8(dl->V_send, 1)], + length); + msg->l2h[1] = LAPDm_CTRL_I(dl->V_recv, + sub_mod8(dl->V_send, 1), 1); /* P=1 */ + tx_ph_data_enqueue(dl, msg, dl->mctx.chan_nr, + dl->mctx.link_id, dl->mctx.n201); + } else { + /* OR send appropriate supervision frame with P=1 */ + if (!dl->own_busy && !dl->seq_err_cond) { + lapdm_send_rr(&dl->mctx, 1); + /* NOTE: In case of sequence error + * condition, the REJ frame has been + * transmitted when entering the + * condition, so it has not be done + * here + */ + } else if (dl->own_busy) { + lapdm_send_rnr(&dl->mctx, 1); + } else { + LOGP(DLAPDM, LOGL_INFO, "unhandled, " + "pls. fix\n"); + } + } + /* restart T200 (PH-READY-TO-SEND) */ + bsc_schedule_timer(&dl->t200, T200); + } else { + /* send MDL ERROR INIDCATION to L3 */ + rsl_rll_error(RLL_CAUSE_T200_EXPIRED, &dl->mctx); + } + break; + default: + LOGP(DLAPDM, LOGL_INFO, "T200 expired in unexpected " + "dl->state %u\n", dl->state); + } +} + +/* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value */ +static void lapdm_acknowledge(struct lapdm_msg_ctx *mctx) +{ + struct lapdm_datalink *dl = mctx->dl; + uint8_t nr = LAPDm_CTRL_Nr(mctx->ctrl); + int s = 0, rej = 0, t200_reset = 0; + int i; + + /* supervisory frame ? */ + if (LAPDm_CTRL_is_S(mctx->ctrl)) + s = 1; + /* REJ frame ? */ + if (s && LAPDm_CTRL_S_BITS(mctx->ctrl) == LAPDm_S_REJ) + rej = 1; + + /* Flush all transmit buffers of acknowledged frames */ + for (i = dl->V_ack; i != nr; i = inc_mod8(i)) { + if (dl->tx_length[i]) { + dl->tx_length[i] = 0; + LOGP(DLAPDM, LOGL_INFO, "ack frame %d\n", i); + } + } + + if (dl->state != LAPDm_STATE_TIMER_RECOV) { + /* When not in the timer recovery condition, the data + * link layer entity shall reset the timer T200 on + * receipt of a valid I frame with N(R) higher than V(A), + * or an REJ with an N(R) equal to V(A). */ + if ((!rej && nr != dl->V_ack) + || (rej && nr == dl->V_ack)) { + LOGP(DLAPDM, LOGL_INFO, "reset t200\n"); + t200_reset = 1; + bsc_del_timer(&dl->t200); + /* 5.5.3.1 Note 1 + 2 imply timer recovery cond. */ + } + /* 5.7.4: N(R) sequence error + * N(R) is called valid, if and only if + * (N(R)-V(A)) mod 8 <= (V(S)-V(A)) mod 8. + */ + if (sub_mod8(nr, dl->V_ack) > sub_mod8(dl->V_send, dl->V_ack)) { + LOGP(DLAPDM, LOGL_NOTICE, "N(R) sequence error\n"); + rsl_rll_error(RLL_CAUSE_SEQ_ERR, mctx); + } + } + + /* V(A) shall be set to the value of N(R) */ + dl->V_ack = nr; + + /* If T200 has been reset by the receipt of an I, RR or RNR frame, + * and if there are outstanding I frames, restart T200 */ + if (t200_reset && !rej) { + if (dl->tx_length[dl->V_send - 1]) { + LOGP(DLAPDM, LOGL_INFO, "start T200, due to unacked I " + "frame(s)\n"); + bsc_schedule_timer(&dl->t200, T200); + } + } +} + +/* L1 -> L2 */ + +/* Receive a LAPDm U (Unnumbered) message from L1 */ +static int lapdm_rx_u(struct msgb *msg, struct lapdm_msg_ctx *mctx) +{ + struct lapdm_datalink *dl = mctx->dl; + uint8_t length; + int rc; + int rsl_msg; + + switch (LAPDm_CTRL_U_BITS(mctx->ctrl)) { + case LAPDm_U_SABM: + rsl_msg = RSL_MT_EST_IND; + + LOGP(DLAPDM, LOGL_INFO, "SABM received\n"); + /* 5.7.1 */ + dl->seq_err_cond = 0; + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) { + LOGP(DLAPDM, LOGL_NOTICE, "SABM response error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + + length = msg->l2h[2] >> 2; + /* G.4.5 If SABM is received with L>N201 or with M bit + * set, AN MDL-ERROR-INDICATION is sent to MM. + */ + if ((msg->l2h[2] & LAPDm_MORE) || length + 3 > mctx->n201) { + LOGP(DLAPDM, LOGL_NOTICE, "SABM too large error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_UFRM_INC_PARAM, mctx); + return -EIO; + } + + /* Must be Format B */ + rc = check_length_ind(mctx, msg->l2h[2]); + if (rc < 0) { + msgb_free(msg); + return rc; + } + switch (dl->state) { + case LAPDm_STATE_IDLE: + /* Set chan_nr and link_id for established connection */ + memset(&dl->mctx, 0, sizeof(dl->mctx)); + dl->mctx.dl = dl; + dl->mctx.chan_nr = mctx->chan_nr; + dl->mctx.link_id = mctx->link_id; + break; + case LAPDm_STATE_MF_EST: + if (length == 0) { + rsl_msg = RSL_MT_EST_CONF; + break; + } + LOGP(DLAPDM, LOGL_INFO, "SABM command, multiple " + "frame established state\n"); + /* check for contention resoultion */ + if (dl->tx_hist[0][2] >> 2) { + LOGP(DLAPDM, LOGL_NOTICE, "SABM not allowed " + "during contention resolution\n"); + rsl_rll_error(RLL_CAUSE_SABM_INFO_NOTALL, mctx); + } + msgb_free(msg); + return 0; + case LAPDm_STATE_DISC_SENT: + /* 5.4.6.2 send DM with F=P */ + lapdm_send_dm(mctx); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + msgb_free(msg); + return send_rll_simple(RSL_MT_REL_CONF, mctx); + default: + lapdm_send_ua(mctx, length, msg->l2h + 3); + msgb_free(msg); + return 0; + } + /* send UA response */ + lapdm_send_ua(mctx, length, msg->l2h + 3); + /* set Vs, Vr and Va to 0 */ + dl->V_send = dl->V_recv = dl->V_ack = 0; + /* clear tx_hist */ + dl->tx_length[0] = 0; + /* enter multiple-frame-established state */ + lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST); + /* send notification to L3 */ + if (length == 0) { + /* 5.4.1.2 Normal establishment procedures */ + rc = send_rll_simple(rsl_msg, mctx); + msgb_free(msg); + } else { + /* 5.4.1.4 Contention resolution establishment */ + msg->l3h = msg->l2h + 3; + msgb_pull_l2h(msg); + rc = send_rslms_rll_l3(rsl_msg, mctx, msg); + } + break; + case LAPDm_U_DM: + LOGP(DLAPDM, LOGL_INFO, "DM received\n"); + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD) { + LOGP(DLAPDM, LOGL_NOTICE, "DM command error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + if (!LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + /* 5.4.1.2 DM responses with the F bit set to "0" + * shall be ignored. + */ + msgb_free(msg); + return 0; + } + switch (dl->state) { + case LAPDm_STATE_SABM_SENT: + break; + case LAPDm_STATE_MF_EST: + if (LAPDm_CTRL_PF_BIT(mctx->ctrl) == 1) { + LOGP(DLAPDM, LOGL_INFO, "unsolicited DM " + "response\n"); + rsl_rll_error(RLL_CAUSE_UNSOL_DM_RESP, mctx); + } else { + LOGP(DLAPDM, LOGL_INFO, "unsolicited DM " + "response, multiple frame established " + "state\n"); + rsl_rll_error(RLL_CAUSE_UNSOL_DM_RESP_MF, mctx); + } + msgb_free(msg); + return 0; + case LAPDm_STATE_TIMER_RECOV: + /* DM is normal in case PF = 1 */ + if (LAPDm_CTRL_PF_BIT(mctx->ctrl) == 0) { + LOGP(DLAPDM, LOGL_INFO, "unsolicited DM " + "response, multiple frame established " + "state\n"); + rsl_rll_error(RLL_CAUSE_UNSOL_DM_RESP_MF, mctx); + msgb_free(msg); + return 0; + } + break; + case LAPDm_STATE_DISC_SENT: + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* go to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + rc = send_rll_simple(RSL_MT_REL_CONF, mctx); + msgb_free(msg); + return 0; + case LAPDm_STATE_IDLE: + /* 5.4.5 all other frame types shall be discarded */ + default: + LOGP(DLAPDM, LOGL_INFO, "unsolicited DM response! " + "(discarding)\n"); + msgb_free(msg); + return 0; + } + /* reset T200 */ + bsc_del_timer(&dl->t200); + rc = send_rll_simple(RSL_MT_REL_IND, mctx); + msgb_free(msg); + break; + case LAPDm_U_UI: + LOGP(DLAPDM, LOGL_INFO, "UI received\n"); + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) { + LOGP(DLAPDM, LOGL_NOTICE, "UI indicates response " + "error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + + length = msg->l2h[2] >> 2; + /* FIXME: G.4.5 If UI is received with L>N201 or with M bit + * set, AN MDL-ERROR-INDICATION is sent to MM. + */ + + if (mctx->lapdm_fmt == LAPDm_FMT_B4) { + length = N201_B4; + msg->l3h = msg->l2h + 2; + } else { + rc = check_length_ind(mctx, msg->l2h[2]); + if (rc < 0) { + msgb_free(msg); + return rc; + } + length = msg->l2h[2] >> 2; + msg->l3h = msg->l2h + 3; + } + /* do some length checks */ + if (length == 0) { + /* 5.3.3 UI frames received with the length indicator + * set to "0" shall be ignored + */ + LOGP(DLAPDM, LOGL_INFO, "length=0 (discarding)\n"); + msgb_free(msg); + return 0; + } + switch (LAPDm_ADDR_SAPI(mctx->addr)) { + case LAPDm_SAPI_NORMAL: + case LAPDm_SAPI_SMS: + break; + default: + /* 5.3.3 UI frames with invalid SAPI values shall be + * discarded + */ + LOGP(DLAPDM, LOGL_INFO, "sapi=%u (discarding)\n", + LAPDm_ADDR_SAPI(mctx->addr)); + msgb_free(msg); + return 0; + } + msgb_pull_l2h(msg); + rc = send_rslms_rll_l3_ui(mctx, msg); + break; + case LAPDm_U_DISC: + rsl_msg = RSL_MT_REL_IND; + + LOGP(DLAPDM, LOGL_INFO, "DISC received\n"); + /* flush buffers */ + lapdm_dl_flush_tx(dl); + lapdm_dl_flush_send(dl); + /* 5.7.1 */ + dl->seq_err_cond = 0; + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) { + LOGP(DLAPDM, LOGL_NOTICE, "DISC response error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + length = msg->l2h[2] >> 2; + if (length > 0 || msg->l2h[2] & 0x02) { + /* G.4.4 If a DISC or DM frame is received with L>0 or + * with the M bit set to "1", an MDL-ERROR-INDICATION + * primitive with cause "U frame with incorrect + * parameters" is sent to the mobile management entity. + */ + LOGP(DLAPDM, LOGL_NOTICE, + "U frame iwth incorrect parameters "); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_UFRM_INC_PARAM, mctx); + return -EIO; + } + msgb_free(msg); + switch (dl->state) { + case LAPDm_STATE_IDLE: + LOGP(DLAPDM, LOGL_INFO, "DISC in idle state\n"); + /* send DM with F=P */ + return lapdm_send_dm(mctx); + case LAPDm_STATE_SABM_SENT: + LOGP(DLAPDM, LOGL_INFO, "DISC in SABM state\n"); + /* 5.4.6.2 send DM with F=P */ + lapdm_send_dm(mctx); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + return send_rll_simple(RSL_MT_REL_IND, mctx); + case LAPDm_STATE_MF_EST: + case LAPDm_STATE_TIMER_RECOV: + LOGP(DLAPDM, LOGL_INFO, "DISC in est state\n"); + break; + case LAPDm_STATE_DISC_SENT: + LOGP(DLAPDM, LOGL_INFO, "DISC in disc state\n"); + rsl_msg = RSL_MT_REL_CONF; + break; + default: + lapdm_send_ua(mctx, length, msg->l2h + 3); + return 0; + } + /* send UA response */ + lapdm_send_ua(mctx, length, msg->l2h + 3); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* enter idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + /* send notification to L3 */ + rc = send_rll_simple(rsl_msg, mctx); + break; + case LAPDm_U_UA: + LOGP(DLAPDM, LOGL_INFO, "UA received\n"); + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD) { + LOGP(DLAPDM, LOGL_NOTICE, "UA indicates command " + "error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + + length = msg->l2h[2] >> 2; + /* G.4.5 If UA is received with L>N201 or with M bit + * set, AN MDL-ERROR-INDICATION is sent to MM. + */ + if ((msg->l2h[2] & LAPDm_MORE) || length + 3 > mctx->n201) { + LOGP(DLAPDM, LOGL_NOTICE, "UA too large error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_UFRM_INC_PARAM, mctx); + return -EIO; + } + + if (!LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + /* 5.4.1.2 A UA response with the F bit set to "0" + * shall be ignored. + */ + LOGP(DLAPDM, LOGL_INFO, "F=0 (discarding)\n"); + msgb_free(msg); + return 0; + } + switch (dl->state) { + case LAPDm_STATE_SABM_SENT: + break; + case LAPDm_STATE_MF_EST: + case LAPDm_STATE_TIMER_RECOV: + LOGP(DLAPDM, LOGL_INFO, "unsolicited UA response! " + "(discarding)\n"); + rsl_rll_error(RLL_CAUSE_UNSOL_UA_RESP, mctx); + msgb_free(msg); + return 0; + case LAPDm_STATE_DISC_SENT: + LOGP(DLAPDM, LOGL_INFO, "UA in disconnect state\n"); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* go to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + rc = send_rll_simple(RSL_MT_REL_CONF, mctx); + msgb_free(msg); + return 0; + case LAPDm_STATE_IDLE: + /* 5.4.5 all other frame types shall be discarded */ + default: + LOGP(DLAPDM, LOGL_INFO, "unsolicited UA response! " + "(discarding)\n"); + msgb_free(msg); + return 0; + } + LOGP(DLAPDM, LOGL_INFO, "UA in SABM state\n"); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* compare UA with SABME if contention resolution is applied */ + if (dl->tx_hist[0][2] >> 2) { + rc = check_length_ind(mctx, msg->l2h[2]); + if (rc < 0) { + rc = send_rll_simple(RSL_MT_REL_IND, mctx); + msgb_free(msg); + /* go to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + return 0; + } + length = msg->l2h[2] >> 2; + if (length != (dl->tx_hist[0][2] >> 2) + || !!memcmp(dl->tx_hist[0] + 3, msg->l2h + 3, + length)) { + LOGP(DLAPDM, LOGL_INFO, "**** UA response " + "mismatches ****\n"); + rc = send_rll_simple(RSL_MT_REL_IND, mctx); + msgb_free(msg); + /* go to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + return 0; + } + } + /* set Vs, Vr and Va to 0 */ + dl->V_send = dl->V_recv = dl->V_ack = 0; + /* clear tx_hist */ + dl->tx_length[0] = 0; + /* enter multiple-frame-established state */ + lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST); + /* send outstanding frames, if any (resume / reconnect) */ + rslms_send_i(mctx, __LINE__); + /* send notification to L3 */ + rc = send_rll_simple(RSL_MT_EST_CONF, mctx); + msgb_free(msg); + break; + default: + /* G.3.1 */ + LOGP(DLAPDM, LOGL_NOTICE, "Unnumbered frame not allowed.\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + return rc; +} + +/* Receive a LAPDm S (Supervisory) message from L1 */ +static int lapdm_rx_s(struct msgb *msg, struct lapdm_msg_ctx *mctx) +{ + struct lapdm_datalink *dl = mctx->dl; + uint8_t length; + + length = msg->l2h[2] >> 2; + if (length > 0 || msg->l2h[2] & 0x02) { + /* G.4.3 If a supervisory frame is received with L>0 or + * with the M bit set to "1", an MDL-ERROR-INDICATION + * primitive with cause "S frame with incorrect + * parameters" is sent to the mobile management entity. */ + LOGP(DLAPDM, LOGL_NOTICE, + "S frame with incorrect parameters\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_SFRM_INC_PARAM, mctx); + return -EIO; + } + + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP + && LAPDm_CTRL_PF_BIT(mctx->ctrl) + && dl->state != LAPDm_STATE_TIMER_RECOV) { + /* 5.4.2.2: Inidcate error on supervisory reponse F=1 */ + LOGP(DLAPDM, LOGL_NOTICE, "S frame response with F=1 error\n"); + rsl_rll_error(RLL_CAUSE_UNSOL_SPRV_RESP, mctx); + } + + switch (dl->state) { + case LAPDm_STATE_IDLE: + /* if P=1, respond DM with F=1 (5.2.2) */ + /* 5.4.5 all other frame types shall be discarded */ + if (LAPDm_CTRL_PF_BIT(mctx->ctrl)) + lapdm_send_dm(mctx); /* F=P */ + /* fall though */ + case LAPDm_STATE_SABM_SENT: + case LAPDm_STATE_DISC_SENT: + LOGP(DLAPDM, LOGL_NOTICE, "S frame ignored in this state\n"); + msgb_free(msg); + return 0; + } + switch (LAPDm_CTRL_S_BITS(mctx->ctrl)) { + case LAPDm_S_RR: + LOGP(DLAPDM, LOGL_INFO, "RR received\n"); + /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ + lapdm_acknowledge(mctx); + + /* 5.5.3.2 */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD + && LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + if (!dl->own_busy && !dl->seq_err_cond) { + LOGP(DLAPDM, LOGL_NOTICE, "RR frame command " + "with polling bit set and we are not " + "busy, so we reply with RR frame\n"); + lapdm_send_rr(mctx, 1); + /* NOTE: In case of sequence error condition, + * the REJ frame has been transmitted when + * entering the condition, so it has not be + * done here + */ + } else if (dl->own_busy) { + LOGP(DLAPDM, LOGL_NOTICE, "RR frame command " + "with polling bit set and we are busy, " + "so we reply with RR frame\n"); + lapdm_send_rnr(mctx, 1); + } + } else if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP + && LAPDm_CTRL_PF_BIT(mctx->ctrl) + && dl->state == LAPDm_STATE_TIMER_RECOV) { + LOGP(DLAPDM, LOGL_INFO, "RR response with F==1, " + "and we are in timer recovery state, so " + "we leave that state\n"); + /* V(S) to the N(R) in the RR frame */ + dl->V_send = LAPDm_CTRL_Nr(mctx->ctrl); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* 5.5.7 Clear timer recovery condition */ + lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST); + } + /* Send message, if possible due to acknowledged data */ + rslms_send_i(mctx, __LINE__); + + break; + case LAPDm_S_RNR: + LOGP(DLAPDM, LOGL_INFO, "RNR received\n"); + /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ + lapdm_acknowledge(mctx); + + /* 5.5.5 */ + /* Set peer receiver busy condition */ + dl->peer_busy = 1; + + if (LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD) { + if (!dl->own_busy) { + LOGP(DLAPDM, LOGL_INFO, "RNR poll " + "command and we are not busy, " + "so we reply with RR final " + "response\n"); + /* Send RR with F=1 */ + lapdm_send_rr(mctx, 1); + } else { + LOGP(DLAPDM, LOGL_INFO, "RNR poll " + "command and we are busy, so " + "we reply with RNR final " + "response\n"); + /* Send RNR with F=1 */ + lapdm_send_rnr(mctx, 1); + } + } else if (dl->state == LAPDm_STATE_TIMER_RECOV) { + LOGP(DLAPDM, LOGL_INFO, "RNR poll response " + "and we in timer recovery state, so " + "we leave that state\n"); + /* 5.5.7 Clear timer recovery condition */ + lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST); + /* V(S) to the N(R) in the RNR frame */ + dl->V_send = LAPDm_CTRL_Nr(mctx->ctrl); + } + } else + LOGP(DLAPDM, LOGL_INFO, "RNR not polling/final state " + "received\n"); + + /* Send message, if possible due to acknowledged data */ + rslms_send_i(mctx, __LINE__); + + break; + case LAPDm_S_REJ: + LOGP(DLAPDM, LOGL_INFO, "REJ received\n"); + /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ + lapdm_acknowledge(mctx); + + /* 5.5.4.1 */ + if (dl->state != LAPDm_STATE_TIMER_RECOV) { + /* Clear an existing peer receiver busy condition */ + dl->peer_busy = 0; + /* V(S) and V(A) to the N(R) in the REJ frame */ + dl->V_send = dl->V_ack = LAPDm_CTRL_Nr(mctx->ctrl); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* 5.5.3.2 */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD + && LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + if (!dl->own_busy && !dl->seq_err_cond) { + LOGP(DLAPDM, LOGL_INFO, "REJ poll " + "command not in timer recovery " + "state and not in own busy " + "condition received, so we " + "respond with RR final " + "response\n"); + lapdm_send_rr(mctx, 1); + /* NOTE: In case of sequence error + * condition, the REJ frame has been + * transmitted when entering the + * condition, so it has not be done + * here + */ + } else if (dl->own_busy) { + LOGP(DLAPDM, LOGL_INFO, "REJ poll " + "command not in timer recovery " + "state and in own busy " + "condition received, so we " + "respond with RNR final " + "response\n"); + lapdm_send_rnr(mctx, 1); + } + } else + LOGP(DLAPDM, LOGL_INFO, "REJ response or not " + "polling command not in timer recovery " + "state received\n"); + /* send MDL ERROR INIDCATION to L3 */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP + && LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + rsl_rll_error(RLL_CAUSE_UNSOL_SPRV_RESP, mctx); + } + + } else if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP + && LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + LOGP(DLAPDM, LOGL_INFO, "REJ poll response in timer " + "recovery state received\n"); + /* Clear an existing peer receiver busy condition */ + dl->peer_busy = 0; + /* 5.5.7 Clear timer recovery condition */ + lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST); + /* V(S) and V(A) to the N(R) in the REJ frame */ + dl->V_send = dl->V_ack = LAPDm_CTRL_Nr(mctx->ctrl); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + } else { + /* Clear an existing peer receiver busy condition */ + dl->peer_busy = 0; + /* V(S) and V(A) to the N(R) in the REJ frame */ + dl->V_send = dl->V_ack = LAPDm_CTRL_Nr(mctx->ctrl); + /* 5.5.3.2 */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD + && LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + if (!dl->own_busy && !dl->seq_err_cond) { + LOGP(DLAPDM, LOGL_INFO, "REJ poll " + "command in timer recovery " + "state and not in own busy " + "condition received, so we " + "respond with RR final " + "response\n"); + lapdm_send_rr(mctx, 1); + /* NOTE: In case of sequence error + * condition, the REJ frame has been + * transmitted when entering the + * condition, so it has not be done + * here + */ + } else if (dl->own_busy) { + LOGP(DLAPDM, LOGL_INFO, "REJ poll " + "command in timer recovery " + "state and in own busy " + "condition received, so we " + "respond with RNR final " + "response\n"); + lapdm_send_rnr(mctx, 1); + } + } else + LOGP(DLAPDM, LOGL_INFO, "REJ response or not " + "polling command in timer recovery " + "state received\n"); + } + + /* FIXME: 5.5.4.2 2) */ + + /* Send message, if possible due to acknowledged data */ + rslms_send_i(mctx, __LINE__); + + break; + default: + /* G.3.1 */ + LOGP(DLAPDM, LOGL_NOTICE, "Supervisory frame not allowed.\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + msgb_free(msg); + return 0; +} + +/* Receive a LAPDm I (Information) message from L1 */ +static int lapdm_rx_i(struct msgb *msg, struct lapdm_msg_ctx *mctx) +{ + struct lapdm_datalink *dl = mctx->dl; + //uint8_t nr = LAPDm_CTRL_Nr(mctx->ctrl); + uint8_t ns = LAPDm_CTRL_I_Ns(mctx->ctrl); + uint8_t length; + int rc; + + LOGP(DLAPDM, LOGL_NOTICE, "I received\n"); + + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) { + LOGP(DLAPDM, LOGL_NOTICE, "I frame response not allowed\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + + length = msg->l2h[2] >> 2; + if (length == 0 || length + 3 > mctx->n201) { + /* G.4.2 If the length indicator of an I frame is set + * to a numerical value L>N201 or L=0, an MDL-ERROR-INDICATION + * primitive with cause "I frame with incorrect length" + * is sent to the mobile management entity. */ + LOGP(DLAPDM, LOGL_NOTICE, "I frame length not allowed\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_IFRM_INC_LEN, mctx); + return -EIO; + } + + /* G.4.2 If the numerical value of L is L<N201 and the M + * bit is set to "1", then an MDL-ERROR-INDICATION primitive with + * cause "I frame with incorrect use of M bit" is sent to the + * mobile management entity. */ + if ((msg->l2h[2] & LAPDm_MORE) && length + 3 < mctx->n201) { + LOGP(DLAPDM, LOGL_NOTICE, "I frame with M bit too short\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_IFRM_INC_MBITS, mctx); + return -EIO; + } + + switch (dl->state) { + case LAPDm_STATE_IDLE: + /* if P=1, respond DM with F=1 (5.2.2) */ + /* 5.4.5 all other frame types shall be discarded */ + if (LAPDm_CTRL_PF_BIT(mctx->ctrl)) + lapdm_send_dm(mctx); /* F=P */ + /* fall though */ + case LAPDm_STATE_SABM_SENT: + case LAPDm_STATE_DISC_SENT: + LOGP(DLAPDM, LOGL_NOTICE, "I frame ignored in this state\n"); + msgb_free(msg); + return 0; + } + + /* 5.7.1: N(s) sequence error */ + if (ns != dl->V_recv) { + LOGP(DLAPDM, LOGL_NOTICE, "N(S) sequence error: N(S)=%u, " + "V(R)=%u\n", ns, dl->V_recv); + /* discard data */ + msgb_free(msg); + if (!dl->seq_err_cond) { + /* FIXME: help me understand what exactly todo here + dl->seq_err_cond = 1; + */ + lapdm_send_rej(mctx, LAPDm_CTRL_PF_BIT(mctx->ctrl)); + } else { + } + return -EIO; + } + dl->seq_err_cond = 0; + + /* Increment receiver state */ + dl->V_recv = inc_mod8(dl->V_recv); + LOGP(DLAPDM, LOGL_NOTICE, "incrementing V(R) to %u\n", dl->V_recv); + + /* 5.5.3.1: Acknowlege all transmitted frames up the the N(R)-1 */ + lapdm_acknowledge(mctx); /* V(A) is also set here */ + + /* Only if we are not in own receiver busy condition */ + if (!dl->own_busy) { + /* if the frame carries a complete segment */ + if (!(msg->l2h[2] & LAPDm_MORE) + && !dl->rcv_buffer) { + LOGP(DLAPDM, LOGL_INFO, "message in single I frame\n"); + /* send a DATA INDICATION to L3 */ + msg->l3h = msg->l2h + 3; + msgb_pull_l2h(msg); + msg->len = length; + msg->tail = msg->data + length; + rc = send_rslms_rll_l3(RSL_MT_DATA_IND, mctx, msg); + } else { + /* create rcv_buffer */ + if (!dl->rcv_buffer) { + LOGP(DLAPDM, LOGL_INFO, "message in multiple I " + "frames (first message)\n"); + dl->rcv_buffer = msgb_alloc_headroom(200+56, 56, + "LAPDm RX"); + dl->rcv_buffer->l3h = dl->rcv_buffer->data; + } + /* concat. rcv_buffer */ + if (msgb_l3len(dl->rcv_buffer) + length > 200) { + LOGP(DLAPDM, LOGL_NOTICE, "Received frame " + "overflow!\n"); + } else { + memcpy(msgb_put(dl->rcv_buffer, length), + msg->l2h + 3, length); + } + /* if the last segment was received */ + if (!(msg->l2h[2] & LAPDm_MORE)) { + LOGP(DLAPDM, LOGL_INFO, "message in multiple I " + "frames (last message)\n"); + rc = send_rslms_rll_l3(RSL_MT_DATA_IND, mctx, + dl->rcv_buffer); + dl->rcv_buffer = NULL; + } else + LOGP(DLAPDM, LOGL_INFO, "message in multiple I " + "frames (next message)\n"); + msgb_free(msg); + + } + } else + LOGP(DLAPDM, LOGL_INFO, "I frame ignored during own receiver " + "busy condition\n"); + + /* Check for P bit */ + if (LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + /* 5.5.2.1 */ + /* check if we are not in own receiver busy */ + if (!dl->own_busy) { + LOGP(DLAPDM, LOGL_INFO, "we are not busy, send RR\n"); + /* Send RR with F=1 */ + rc = lapdm_send_rr(mctx, 1); + } else { + LOGP(DLAPDM, LOGL_INFO, "we are busy, send RNR\n"); + /* Send RNR with F=1 */ + rc = lapdm_send_rnr(mctx, 1); + } + } else { + /* 5.5.2.2 */ + /* check if we are not in own receiver busy */ + if (!dl->own_busy) { + /* NOTE: V(R) is already set above */ + rc = rslms_send_i(mctx, __LINE__); + if (rc) { + LOGP(DLAPDM, LOGL_INFO, "we are not busy and " + "have no pending data, send RR\n"); + /* Send RR with F=0 */ + return lapdm_send_rr(mctx, 0); + } + /* all I or one RR is sent, we are done */ + return 0; + } else { + LOGP(DLAPDM, LOGL_INFO, "we are busy, send RNR\n"); + /* Send RNR with F=0 */ + rc = lapdm_send_rnr(mctx, 0); + } + } + + /* Send message, if possible due to acknowledged data */ + rslms_send_i(mctx, __LINE__); + + return rc; +} + +/* Receive a LAPDm message from L1 */ +static int lapdm_ph_data_ind(struct msgb *msg, struct lapdm_msg_ctx *mctx) +{ + int rc; + + /* G.2.3 EA bit set to "0" is not allowed in GSM */ + if (!LAPDm_ADDR_EA(mctx->addr)) { + LOGP(DLAPDM, LOGL_NOTICE, "EA bit 0 is not allowed in GSM\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + + if (LAPDm_CTRL_is_U(mctx->ctrl)) + rc = lapdm_rx_u(msg, mctx); + else if (LAPDm_CTRL_is_S(mctx->ctrl)) + rc = lapdm_rx_s(msg, mctx); + else if (LAPDm_CTRL_is_I(mctx->ctrl)) + rc = lapdm_rx_i(msg, mctx); + else { + LOGP(DLAPDM, LOGL_NOTICE, "unknown LAPDm format\n"); + msgb_free(msg); + rc = -EINVAL; + } + return rc; +} + +/* input into layer2 (from layer 1) */ +int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, struct l1ctl_info_dl *l1i) +{ + uint8_t cbits = l1i->chan_nr >> 3; + uint8_t sapi = l1i->link_id & 7; + struct lapdm_msg_ctx mctx; + int rc = 0; + + /* when we reach here, we have a msgb with l2h pointing to the raw + * 23byte mac block. The l1h has already been purged. */ + + mctx.dl = datalink_for_sapi(le, sapi); + mctx.chan_nr = l1i->chan_nr; + mctx.link_id = l1i->link_id; + mctx.addr = mctx.ctrl = 0; + + /* G.2.1 No action schall be taken on frames containing an unallocated + * SAPI. + */ + if (!mctx.dl) { + LOGP(DLAPDM, LOGL_NOTICE, "Received frame for unsupported " + "SAPI %d!\n", sapi); + return -EINVAL; + msgb_free(msg); + return -EIO; + } + + /* check for L1 chan_nr/link_id and determine LAPDm hdr format */ + if (cbits == 0x10 || cbits == 0x12) { + /* Format Bbis is used on BCCH and CCCH(PCH, NCH and AGCH) */ + mctx.lapdm_fmt = LAPDm_FMT_Bbis; + mctx.n201 = N201_Bbis; + } else { + if (mctx.link_id & 0x40) { + /* It was received from network on SACCH, thus + * lapdm_fmt must be B4 */ + mctx.lapdm_fmt = LAPDm_FMT_B4; + mctx.n201 = N201_B4; + LOGP(DLAPDM, LOGL_INFO, "fmt=B4\n"); + /* SACCH frames have a two-byte L1 header that + * OsmocomBB L1 doesn't strip */ + mctx.tx_power_ind = msg->l2h[0] & 0x1f; + mctx.ta_ind = msg->l2h[1]; + msgb_pull(msg, 2); + msg->l2h += 2; + } else { + mctx.lapdm_fmt = LAPDm_FMT_B; + LOGP(DLAPDM, LOGL_INFO, "fmt=B\n"); + mctx.n201 = 23; // FIXME: select correct size by chan. + } + } + + switch (mctx.lapdm_fmt) { + case LAPDm_FMT_A: + case LAPDm_FMT_B: + case LAPDm_FMT_B4: + mctx.addr = msg->l2h[0]; + if (!(mctx.addr & 0x01)) { + LOGP(DLAPDM, LOGL_ERROR, "we don't support " + "multibyte addresses (discarding)\n"); + msgb_free(msg); + return -EINVAL; + } + mctx.ctrl = msg->l2h[1]; + /* obtain SAPI from address field */ + mctx.link_id |= LAPDm_ADDR_SAPI(mctx.addr); + rc = lapdm_ph_data_ind(msg, &mctx); + break; + case LAPDm_FMT_Bter: + /* FIXME */ + msgb_free(msg); + break; + case LAPDm_FMT_Bbis: + /* directly pass up to layer3 */ + LOGP(DLAPDM, LOGL_INFO, "fmt=Bbis UI\n"); + msg->l3h = msg->l2h; + msgb_pull_l2h(msg); + rc = send_rslms_rll_l3(RSL_MT_UNIT_DATA_IND, &mctx, msg); + break; + default: + msgb_free(msg); + } + + return rc; +} + +/* L3 -> L2 / RSLMS -> LAPDm */ + +/* L3 requests establishment of data link */ +static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t chan_nr = rllh->chan_nr; + uint8_t link_id = rllh->link_id; + uint8_t sapi = rllh->link_id & 7; + struct tlv_parsed tv; + uint8_t length; + uint8_t n201 = 23; //FIXME + + /* Set chan_nr and link_id for established connection */ + memset(&dl->mctx, 0, sizeof(dl->mctx)); + dl->mctx.dl = dl; + dl->mctx.n201 = n201; + dl->mctx.chan_nr = chan_nr; + dl->mctx.link_id = link_id; + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + /* contention resolution establishment procedure */ + if (sapi != 0) { + /* According to clause 6, the contention resolution + * procedure is only permitted with SAPI value 0 */ + LOGP(DLAPDM, LOGL_ERROR, "SAPI != 0 but contention" + "resolution (discarding)\n"); + msgb_free(msg); + return send_rll_simple(RSL_MT_REL_IND, &dl->mctx); + } + /* transmit a SABM command with the P bit set to "1". The SABM + * command shall contain the layer 3 message unit */ + length = TLVP_LEN(&tv, RSL_IE_L3_INFO); + LOGP(DLAPDM, LOGL_INFO, "perform establishment with content " + "(SABM)\n"); + } else { + /* normal establishment procedure */ + length = 0; + LOGP(DLAPDM, LOGL_INFO, "perform normal establishm. (SABM)\n"); + } + + /* check if the layer3 message length exceeds N201 */ + if (length + 3 > 21) { /* FIXME: do we know the channel N201? */ + LOGP(DLAPDM, LOGL_ERROR, "frame too large: %d > N201(%d) " + "(discarding)\n", length + 3, 21); + msgb_free(msg); + return send_rll_simple(RSL_MT_REL_IND, &dl->mctx); + } + + /* Flush send-queue */ + /* Clear send-buffer */ + lapdm_dl_flush_send(dl); + + /* Discard partly received L3 message */ + if (dl->rcv_buffer) { + msgb_free(dl->rcv_buffer); + dl->rcv_buffer = NULL; + } + + /* Remove RLL header from msgb */ + msgb_pull_l2h(msg); + + /* Push LAPDm header on msgb */ + msg->l2h = msgb_push(msg, 3); + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD); + msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_SABM, 1); + msg->l2h[2] = LAPDm_LEN(length); + /* Transmit-buffer carries exactly one segment */ + memcpy(dl->tx_hist[0], msg->l2h, 3 + length); + dl->tx_length[0] = 3 + length; + /* set Vs to 0, because it is used as index when resending SABM */ + dl->V_send = 0; + + /* Set states */ + dl->own_busy = dl->peer_busy = 0; + dl->retrans_ctr = 0; + lapdm_dl_newstate(dl, LAPDm_STATE_SABM_SENT); + + /* Tramsmit and start T200 */ + bsc_schedule_timer(&dl->t200, T200); + return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, n201); +} + +/* L3 requests transfer of unnumbered information */ +static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t chan_nr = rllh->chan_nr; + uint8_t link_id = rllh->link_id; + uint8_t sapi = link_id & 7; + struct tlv_parsed tv; + int length; + uint8_t n201 = 23; //FIXME + uint8_t ta = 0, tx_power = 0; + + /* check if the layer3 message length exceeds N201 */ + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + + if (TLVP_PRESENT(&tv, RSL_IE_TIMING_ADVANCE)) { + ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE); + } + if (TLVP_PRESENT(&tv, RSL_IE_MS_POWER)) { + tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER); + } + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + LOGP(DLAPDM, LOGL_ERROR, "unit data request without message " + "error\n"); + msgb_free(msg); + return -EINVAL; + } + length = TLVP_LEN(&tv, RSL_IE_L3_INFO); + /* check if the layer3 message length exceeds N201 */ + if (length + 5 > 23) { /* FIXME: do we know the channel N201? */ + LOGP(DLAPDM, LOGL_ERROR, "frame too large: %d > N201(%d) " + "(discarding)\n", length + 5, 23); + msgb_free(msg); + return -EIO; + } + + LOGP(DLAPDM, LOGL_INFO, "sending unit data (tx_power=%d, ta=%d)\n", + tx_power, ta); + + /* Remove RLL header from msgb */ + msgb_pull_l2h(msg); + + /* Push L1 + LAPDm header on msgb */ + msg->l2h = msgb_push(msg, 2 + 3); + msg->l2h[0] = tx_power; + msg->l2h[1] = ta; + msg->l2h[2] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD); + msg->l2h[3] = LAPDm_CTRL_U(LAPDm_U_UI, 0); + msg->l2h[4] = LAPDm_LEN(length); + // FIXME: short L2 header support + + /* Tramsmit */ + return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, n201); +} + +/* L3 requests transfer of acknowledged information */ +static int rslms_rx_rll_data_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + struct tlv_parsed tv; + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + LOGP(DLAPDM, LOGL_ERROR, "data request without message " + "error\n"); + msgb_free(msg); + return -EINVAL; + } + + LOGP(DLAPDM, LOGL_INFO, "writing message to send-queue\n"); + + /* Remove the RSL/RLL header */ + msgb_pull_l2h(msg); + + /* Write data into the send queue */ + msgb_enqueue(&dl->send_queue, msg); + + /* Send message, if possible */ + rslms_send_i(&dl->mctx, __LINE__); + return 0; +} + +/* Send next I frame from queued/buffered data */ +static int rslms_send_i(struct lapdm_msg_ctx *mctx, int line) +{ + struct lapdm_datalink *dl = mctx->dl; + uint8_t chan_nr = mctx->chan_nr; + uint8_t link_id = mctx->link_id; + uint8_t sapi = link_id & 7; + int k = k_sapi[sapi]; + struct msgb *msg; + int length, left; + int rc = - 1; /* we sent nothing */ + + LOGP(DLAPDM, LOGL_INFO, "%s() called from line %d\n", __func__, line); + + next_frame: + + if (dl->peer_busy) { + LOGP(DLAPDM, LOGL_INFO, "peer busy, not sending\n"); + return rc; + } + + if (dl->state == LAPDm_STATE_TIMER_RECOV) { + LOGP(DLAPDM, LOGL_INFO, "timer recovery, not sending\n"); + return rc; + } + + /* If the send state variable V(S) is equal to V(A) plus k + * (where k is the maximum number of outstanding I frames - see + * subclause 5.8.4), the data link layer entity shall not transmit any + * new I frames, but shall retransmit an I frame as a result + * of the error recovery procedures as described in subclauses 5.5.4 and + * 5.5.7. */ + if (dl->V_send == add_mod8(dl->V_ack, k)) { + LOGP(DLAPDM, LOGL_INFO, "k frames outstanding, not sending " + "more (k=%u V(S)=%u V(A)=%u)\n", k, dl->V_send, + dl->V_ack); + return rc; + } + + /* if we have no tx_hist yet, we create it */ + if (!dl->tx_length[dl->V_send]) { + /* Get next message into send-buffer, if any */ + if (!dl->send_buffer) { + next_message: + dl->send_out = 0; + dl->send_buffer = msgb_dequeue(&dl->send_queue); + /* No more data to be sent */ + if (!dl->send_buffer) + return rc; + LOGP(DLAPDM, LOGL_INFO, "get message from " + "send-queue\n"); + } + + /* How much is left in the send-buffer? */ + left = msgb_l3len(dl->send_buffer) - dl->send_out; + /* Segment, if data exceeds N201 */ + length = left; + if (length > mctx->n201 - 3) + length = mctx->n201 - 3; + LOGP(DLAPDM, LOGL_INFO, "msg-len %d sent %d left %d N201 %d " + "length %d first byte %02x\n", + msgb_l3len(dl->send_buffer), dl->send_out, left, + mctx->n201, length, dl->send_buffer->l3h[0]); + /* If message in send-buffer is completely sent */ + if (left == 0) { + msgb_free(dl->send_buffer); + dl->send_buffer = NULL; + goto next_message; + } + + LOGP(DLAPDM, LOGL_INFO, "send I frame %sV(S)=%d\n", + (left > length) ? "segment " : "", dl->V_send); + + /* Create I frame (segment) and transmit-buffer content */ + msg = msgb_alloc_headroom(23+10, 10, "LAPDm I"); + msg->l2h = msgb_put(msg, 3 + length); + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD); + msg->l2h[1] = LAPDm_CTRL_I(dl->V_recv, dl->V_send, 0); + msg->l2h[2] = LAPDm_LEN(length); + if (left > length) + msg->l2h[2] |= LAPDm_MORE; + memcpy(msg->l2h + 3, dl->send_buffer->l3h + dl->send_out, + length); + memcpy(dl->tx_hist[dl->V_send], msg->l2h, 3 + length); + dl->tx_length[dl->V_send] = 3 + length; + /* Add length to track how much is already in the tx buffer */ + dl->send_out += length; + } else { + LOGP(DLAPDM, LOGL_INFO, "resend I frame from tx buffer " + "V(S)=%d\n", dl->V_send); + + /* Create I frame (segment) from tx_hist */ + length = dl->tx_length[dl->V_send]; + msg = msgb_alloc_headroom(23+10, 10, "LAPDm I"); + msg->l2h = msgb_put(msg, length); + memcpy(msg->l2h, dl->tx_hist[dl->V_send], length); + msg->l2h[1] = LAPDm_CTRL_I(dl->V_recv, dl->V_send, 0); + } + + /* The value of the send state variable V(S) shall be incremented by 1 + * at the end of the transmission of the I frame */ + dl->V_send = inc_mod8(dl->V_send); + + /* If timer T200 is not running at the time right before transmitting a + * frame, when the PH-READY-TO-SEND primitive is received from the + * physical layer., it shall be set. */ + if (!bsc_timer_pending(&dl->t200)) + bsc_schedule_timer(&dl->t200, T200); + + tx_ph_data_enqueue(dl, msg, chan_nr, link_id, mctx->n201); + + rc = 0; /* we sent something */ + goto next_frame; +} + +/* L3 requests suspension of data link */ +static int rslms_rx_rll_susp_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t sapi = rllh->link_id & 7; + + if (sapi != 0) { + LOGP(DLAPDM, LOGL_ERROR, "SAPI != 0 while suspending\n"); + msgb_free(msg); + return -EINVAL; + } + + LOGP(DLAPDM, LOGL_INFO, "perform suspension\n"); + + /* put back the send-buffer to the send-queue (first position) */ + if (dl->send_buffer) { + LOGP(DLAPDM, LOGL_INFO, "put frame in sendbuffer back to " + "queue\n"); + llist_add(&dl->send_buffer->list, &dl->send_queue); + dl->send_buffer = NULL; + } else + LOGP(DLAPDM, LOGL_INFO, "no frame in sendbuffer\n"); + + /* Clear transmit buffer, but keep send buffer */ + lapdm_dl_flush_tx(dl); + + msgb_free(msg); + + return send_rll_simple(RSL_MT_SUSP_CONF, &dl->mctx); +} + +/* L3 requests resume of data link */ +static int rslms_rx_rll_res_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t chan_nr = rllh->chan_nr; + uint8_t link_id = rllh->link_id; + uint8_t sapi = rllh->link_id & 7; + struct tlv_parsed tv; + uint8_t length; + uint8_t n201 = 23; //FIXME + + /* Set chan_nr and link_id for established connection */ + memset(&dl->mctx, 0, sizeof(dl->mctx)); + dl->mctx.dl = dl; + dl->mctx.n201 = n201; + dl->mctx.chan_nr = chan_nr; + dl->mctx.link_id = link_id; + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + LOGP(DLAPDM, LOGL_ERROR, "resume without message error\n"); + msgb_free(msg); + return send_rll_simple(RSL_MT_REL_IND, &dl->mctx); + } + length = TLVP_LEN(&tv, RSL_IE_L3_INFO); + + LOGP(DLAPDM, LOGL_INFO, "perform re-establishment (SABM) length=%d\n", + length); + + /* Replace message in the send-buffer (reconnect) */ + if (dl->send_buffer) + msgb_free(dl->send_buffer); + dl->send_out = 0; + if (length) { + /* Remove the RSL/RLL header */ + msgb_pull_l2h(msg); + /* Write data into the send buffer, to be sent first */ + dl->send_buffer = msg; + } + + /* Discard partly received L3 message */ + if (dl->rcv_buffer) { + msgb_free(dl->rcv_buffer); + dl->rcv_buffer = NULL; + } + + /* Create new msgb (old one is now free) */ + msg = msgb_alloc_headroom(23+10, 10, "LAPDm SABM"); + msg->l2h = msgb_put(msg, 3); + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD); + msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_SABM, 1); + msg->l2h[2] = LAPDm_LEN(0); + /* Transmit-buffer carries exactly one segment */ + memcpy(dl->tx_hist[0], msg->l2h, 3); + dl->tx_length[0] = 3; + /* set Vs to 0, because it is used as index when resending SABM */ + dl->V_send = 0; + + /* Set states */ + dl->own_busy = dl->peer_busy = 0; + dl->retrans_ctr = 0; + lapdm_dl_newstate(dl, LAPDm_STATE_SABM_SENT); + + /* Tramsmit and start T200 */ + bsc_schedule_timer(&dl->t200, T200); + return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, n201); +} + +/* L3 requests release of data link */ +static int rslms_rx_rll_rel_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t chan_nr = rllh->chan_nr; + uint8_t link_id = rllh->link_id; + uint8_t sapi = rllh->link_id & 7; + uint8_t mode = 0; + + /* get release mode */ + if (rllh->data[0] == RSL_IE_RELEASE_MODE) + mode = rllh->data[1] & 1; + + /* local release */ + if (mode) { + LOGP(DLAPDM, LOGL_INFO, "perform local release\n"); + msgb_free(msg); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* enter idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + /* flush buffers */ + lapdm_dl_flush_tx(dl); + lapdm_dl_flush_send(dl); + /* send notification to L3 */ + return send_rll_simple(RSL_MT_REL_CONF, &dl->mctx); + } + + /* in case we are already disconnecting */ + if (dl->state == LAPDm_STATE_DISC_SENT) + return -EBUSY; + + LOGP(DLAPDM, LOGL_INFO, "perform normal release (DISC)\n"); + + /* Pull rllh */ + msgb_pull(msg, msg->tail - msg->l2h); + + /* Push LAPDm header on msgb */ + msg->l2h = msgb_push(msg, 3); + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD); + msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_DISC, 1); + msg->l2h[2] = LAPDm_LEN(0); + /* Transmit-buffer carries exactly one segment */ + memcpy(dl->tx_hist[0], msg->l2h, 3); + dl->tx_length[0] = 3; + + /* Set states */ + dl->own_busy = dl->peer_busy = 0; + dl->retrans_ctr = 0; + lapdm_dl_newstate(dl, LAPDm_STATE_DISC_SENT); + + /* Tramsmit and start T200 */ + bsc_schedule_timer(&dl->t200, T200); + return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, dl->mctx.n201); +} + +/* L3 requests release in idle state */ +static int rslms_rx_rll_rel_req_idle(struct msgb *msg, struct lapdm_datalink *dl) +{ + msgb_free(msg); + + /* send notification to L3 */ + return send_rll_simple(RSL_MT_REL_CONF, &dl->mctx); +} + +/* L3 requests channel in idle state */ +static int rslms_rx_chan_rqd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct abis_rsl_cchan_hdr *cch = msgb_l2(msg); + int rc; + + if (msgb_l2len(msg) < sizeof(*cch) + 4 + 2 + 2) { + LOGP(DRSL, LOGL_ERROR, "Message too short for CHAN RQD!\n"); + return -EINVAL; + } + if (cch->data[0] != RSL_IE_REQ_REFERENCE) { + LOGP(DRSL, LOGL_ERROR, "Missing REQ REFERENCE IE\n"); + return -EINVAL; + } + if (cch->data[4] != RSL_IE_ACCESS_DELAY) { + LOGP(DRSL, LOGL_ERROR, "Missing ACCESS_DELAY IE\n"); + return -EINVAL; + } + if (cch->data[6] != RSL_IE_MS_POWER) { + LOGP(DRSL, LOGL_ERROR, "Missing MS POWER IE\n"); + return -EINVAL; + } + + /* TA = 0 - delay */ + rc = l1ctl_tx_param_req(ms, 0 - cch->data[5], cch->data[7]); + + rc = l1ctl_tx_rach_req(ms, cch->data[1], + ((cch->data[2] & 0x7f) << 8) | cch->data[3], cch->data[2] >> 7); + + msgb_free(msg); + + return rc; +} + +/* L1 confirms channel request */ +int l2_ph_chan_conf(struct msgb *msg, struct osmocom_ms *ms, + struct l1ctl_info_dl *dl) +{ + struct abis_rsl_cchan_hdr *ch; + struct gsm_time tm; + struct gsm48_req_ref *ref; + + gsm_fn2gsmtime(&tm, htonl(dl->frame_nr)); + + msgb_pull_l2h(msg); + msg->l2h = msgb_push(msg, sizeof(*ch) + sizeof(*ref)); + ch = (struct abis_rsl_cchan_hdr *)msg->l2h; + rsl_init_cchan_hdr(ch, RSL_MT_CHAN_CONF); + ch->chan_nr = RSL_CHAN_RACH; + ch->data[0] = RSL_IE_REQ_REFERENCE; + ref = (struct gsm48_req_ref *) (ch->data + 1); + ref->t1 = tm.t1; + ref->t2 = tm.t2; + ref->t3_low = tm.t3 & 0x7; + ref->t3_high = tm.t3 >> 3; + + return rslms_sendmsg(msg, ms); +} + +/* Names for Radio Link Layer Management */ +static const struct value_string rsl_msg_names[] = { + { RSL_MT_DATA_REQ, "RSL_MT_DATA_REQ" }, + { RSL_MT_DATA_IND, "RSL_MT_DATA_IND" }, + { RSL_MT_ERROR_IND, "RSL_MT_ERROR_IND" }, + { RSL_MT_EST_REQ, "RSL_MT_EST_REQ" }, + { RSL_MT_EST_CONF, "RSL_MT_EST_CONF" }, + { RSL_MT_EST_IND, "RSL_MT_EST_IND" }, + { RSL_MT_EST_IND, "RSL_MT_REL_REQ" }, + { RSL_MT_REL_REQ, "RSL_MT_REL_REQ" }, + { RSL_MT_REL_CONF, "RSL_MT_REL_CONF" }, + { RSL_MT_REL_IND, "RSL_MT_REL_IND" }, + { RSL_MT_UNIT_DATA_REQ, "RSL_MT_UNIT_DATA_REQ" }, + { RSL_MT_UNIT_DATA_IND, "RSL_MT_UNIT_DATA_IND" }, + { RSL_MT_SUSP_REQ, "RSL_MT_SUSP_REQ" }, + { RSL_MT_SUSP_CONF, "RSL_MT_SUSP_CONF" }, + { RSL_MT_RES_REQ, "RSL_MT_RES_REQ" }, + { RSL_MT_RECON_REQ, "RSL_MT_RECON_REQ" }, + { RSL_MT_CHAN_RQD, "RSL_MT_CHAN_RQD" }, + { RSL_MT_CHAN_CONF, "RSL_MT_CHAN_CONF" }, + { 0, NULL } +}; + +const char *get_rsl_name(int value) +{ + return get_value_string(rsl_msg_names, value); +} + +const char *lapdm_state_names[] = { + "LAPDm_STATE_NULL", + "LAPDm_STATE_IDLE", + "LAPDm_STATE_SABM_SENT", + "LAPDm_STATE_MF_EST", + "LAPDm_STATE_TIMER_RECOV", + "LAPDm_STATE_DISC_SENT", +}; + +/* statefull handling for RSLms RLL messages from L3 */ +static struct l2downstate { + uint32_t states; + int type; + int (*rout) (struct msgb *msg, struct lapdm_datalink *dl); +} l2downstatelist[] = { + /* create and send UI command */ + {ALL_STATES, + RSL_MT_UNIT_DATA_REQ, rslms_rx_rll_udata_req}, + + /* create and send SABM command */ + {SBIT(LAPDm_STATE_IDLE), + RSL_MT_EST_REQ, rslms_rx_rll_est_req}, + + /* create and send I command */ + {SBIT(LAPDm_STATE_MF_EST) | + SBIT(LAPDm_STATE_TIMER_RECOV), + RSL_MT_DATA_REQ, rslms_rx_rll_data_req}, + + /* suspend datalink */ + {SBIT(LAPDm_STATE_MF_EST) | + SBIT(LAPDm_STATE_TIMER_RECOV), + RSL_MT_SUSP_REQ, rslms_rx_rll_susp_req}, + + /* create and send SABM command (resume) */ + {SBIT(LAPDm_STATE_MF_EST) | + SBIT(LAPDm_STATE_TIMER_RECOV), + RSL_MT_RES_REQ, rslms_rx_rll_res_req}, + + /* create and send SABM command (reconnect) */ + {SBIT(LAPDm_STATE_IDLE) | + SBIT(LAPDm_STATE_MF_EST) | + SBIT(LAPDm_STATE_TIMER_RECOV), + RSL_MT_RECON_REQ, rslms_rx_rll_res_req}, + + /* create and send DISC command */ + {SBIT(LAPDm_STATE_SABM_SENT) | + SBIT(LAPDm_STATE_MF_EST) | + SBIT(LAPDm_STATE_TIMER_RECOV) | + SBIT(LAPDm_STATE_DISC_SENT), + RSL_MT_REL_REQ, rslms_rx_rll_rel_req}, + + /* release in idle state */ + {SBIT(LAPDm_STATE_IDLE), + RSL_MT_REL_REQ, rslms_rx_rll_rel_req_idle}, +}; + +#define L2DOWNSLLEN \ + (sizeof(l2downstatelist) / sizeof(struct l2downstate)) + +/* incoming RSLms RLL message from L3 */ +static int rslms_rx_rll(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + int msg_type = rllh->c.msg_type; + uint8_t sapi = rllh->link_id & 7; + struct lapdm_entity *le; + struct lapdm_datalink *dl; + int i, supported = 0; + int rc = 0; + + if (msgb_l2len(msg) < sizeof(*rllh)) { + LOGP(DRSL, LOGL_ERROR, "Message too short for RLL hdr!\n"); + return -EINVAL; + } + + if (rllh->link_id & 0x40) + le = &ms->l2_entity.lapdm_acch; + else + le = &ms->l2_entity.lapdm_dcch; + + /* G.2.1 No action schall be taken on frames containing an unallocated + * SAPI. + */ + dl = datalink_for_sapi(le, sapi); + if (!dl) { + LOGP(DRSL, LOGL_ERROR, "No instance for SAPI %d!\n", sapi); + return -EINVAL; + } + + LOGP(DRSL, LOGL_INFO, "(ms %s) RLL Message '%s' received in state %s\n", + ms->name, get_rsl_name(msg_type), lapdm_state_names[dl->state]); + + /* find function for current state and message */ + for (i = 0; i < L2DOWNSLLEN; i++) { + if (msg_type == l2downstatelist[i].type) + supported = 1; + if ((msg_type == l2downstatelist[i].type) + && ((1 << dl->state) & l2downstatelist[i].states)) + break; + } + if (!supported) { + LOGP(DRSL, LOGL_NOTICE, "Message unsupported.\n"); + msgb_free(msg); + return 0; + } + if (i == L2DOWNSLLEN) { + LOGP(DRSL, LOGL_NOTICE, "Message unhandled at this state.\n"); + msgb_free(msg); + return 0; + } + + rc = l2downstatelist[i].rout(msg, dl); + + return rc; +} + +/* incoming RSLms COMMON CHANNEL message from L3 */ +static int rslms_rx_com_chan(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_cchan_hdr *cch = msgb_l2(msg); + int msg_type = cch->c.msg_type; + int rc = 0; + + if (msgb_l2len(msg) < sizeof(*cch)) { + LOGP(DRSL, LOGL_ERROR, "Message too short for COM CHAN hdr!\n"); + return -EINVAL; + } + + switch (msg_type) { + case RSL_MT_CHAN_RQD: + /* create and send RACH request */ + rc = rslms_rx_chan_rqd(ms, msg); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "Unknown COMMON CHANNEL msg %d!\n", + msg_type); + msgb_free(msg); + return 0; + } + + return rc; +} + +/* input into layer2 (from layer 3) */ +int rslms_recvmsg(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + int rc = 0; + + if (msgb_l2len(msg) < sizeof(*rslh)) { + LOGP(DRSL, LOGL_ERROR, "Message too short RSL hdr!\n"); + return -EINVAL; + } + + switch (rslh->msg_discr & 0xfe) { + case ABIS_RSL_MDISC_RLL: + rc = rslms_rx_rll(msg, ms); + break; + case ABIS_RSL_MDISC_COM_CHAN: + rc = rslms_rx_com_chan(msg, ms); + break; + default: + LOGP(DRSL, LOGL_ERROR, "unknown RSLms message " + "discriminator 0x%02x", rslh->msg_discr); + msgb_free(msg); + return -EINVAL; + } + + return rc; +} + +/* input function that L2 calls when sending messages up to L3 */ +int rslms_sendmsg(struct msgb *msg, struct osmocom_ms *ms) +{ + if (!ms->l2_entity.msg_handler) { + msgb_free(msg); + return -EIO; + } + + /* call the layer2 message handler that is registered */ + return ms->l2_entity.msg_handler(msg, ms); +} + +/* register message handler for messages that are sent from L2->L3 */ +int osmol2_register_handler(struct osmocom_ms *ms, osmol2_cb_t cb) +{ + ms->l2_entity.msg_handler = cb; + + return 0; +} diff --git a/src/host/layer23/src/common/logging.c b/src/host/layer23/src/common/logging.c new file mode 100644 index 00000000..cdd3c35a --- /dev/null +++ b/src/host/layer23/src/common/logging.c @@ -0,0 +1,130 @@ +/* Logging/Debug support of the layer2/3 stack */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <osmocore/utils.h> +#include <osmocore/logging.h> +#include <osmocom/bb/common/logging.h> + +static const struct log_info_cat default_categories[] = { + [DRSL] = { + .name = "DRSL", + .description = "Radio Signalling Link (MS)", + .color = "\033[1;35m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DCS] = { + .name = "DCS", + .description = "Cell selection", + .color = "\033[34m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DPLMN] = { + .name = "DPLMN", + .description = "PLMN selection", + .color = "\033[32m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DRR] = { + .name = "DRR", + .description = "Radio Resource", + .color = "\033[1;34m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DMM] = { + .name = "DMM", + .description = "Mobility Management", + .color = "\033[1;32m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DCC] = { + .name = "DCC", + .description = "Call Control", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DSMS] = { + .name = "DSMS", + .description = "Short Message Service", + .color = "\033[1;37m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DMNCC] = { + .name = "DMNCC", + .description = "Mobile Network Call Control", + .color = "\033[1;37m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DMEAS] = { + .name = "DMEAS", + .description = "MEasurement Reporting", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DPAG] = { + .name = "DPAG", + .description = "Paging", + .color = "\033[33m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DLAPDM] = { + .name = "DLAPDM", + .description = "LAPDm Layer2", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DL1C] = { + .name = "DL1C", + .description = "Layer 1 Control", + .color = "\033[1;31m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DSAP] = { + .name = "DSAP", + .description = "SAP Control", + .color = "\033[1;31m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DSUM] = { + .name = "DSUM", + .description = "Summary of Process", + .color = "\033[1;37m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DSIM] = { + .name = "DSIM", + .description = "SIM client", + .color = "\033[0;35m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DGPS] = { + .name = "DGPS", + .description = "GPS", + .color = "\033[1;35m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, +}; + +const struct log_info log_info = { + .filter_fn = NULL, + .cat = default_categories, + .num_cat = ARRAY_SIZE(default_categories), +}; + diff --git a/src/host/layer23/src/common/main.c b/src/host/layer23/src/common/main.c new file mode 100644 index 00000000..cb9564a4 --- /dev/null +++ b/src/host/layer23/src/common/main.c @@ -0,0 +1,285 @@ +/* Main method of the layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/l1l2_interface.h> +#include <osmocom/bb/common/sap_interface.h> +#include <osmocom/bb/misc/layer3.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/l23_app.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/linuxlist.h> +#include <osmocore/gsmtap_util.h> +#include <osmocore/utils.h> + +#include <arpa/inet.h> + +#define _GNU_SOURCE +#include <getopt.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> + +struct log_target *stderr_target; + +void *l23_ctx = NULL; +static char *layer2_socket_path = "/tmp/osmocom_l2"; +static char *sap_socket_path = "/tmp/osmocom_sap"; +struct llist_head ms_list; +static struct osmocom_ms *ms = NULL; +static uint32_t gsmtap_ip = 0; +unsigned short vty_port = 4247; +int (*l23_app_work) (struct osmocom_ms *ms) = NULL; +int (*l23_app_exit) (struct osmocom_ms *ms) = NULL; +int quit = 0; + +const char *openbsc_copyright = + "%s" + "%s\n" + "License GPLv2+: GNU GPL version 2 or later " + "<http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n\n"; + +static void print_usage(const char *app) +{ + printf("Usage: %s\n", app); +} + +static void print_help() +{ + int options = 0xff; + struct l23_app_info *app = l23_app_info(); + + if (app && app->cfg_supported != 0) + options = app->cfg_supported(); + + printf(" Some help...\n"); + printf(" -h --help this text\n"); + printf(" -s --socket /tmp/osmocom_l2. Path to the unix " + "domain socket (l2)\n"); + + if (options & L23_OPT_SAP) + printf(" -S --sap /tmp/osmocom_sap. Path to the " + "unix domain socket (BTSAP)\n"); + + if (options & L23_OPT_ARFCN) + printf(" -a --arfcn NR The ARFCN to be used for layer2.\n"); + + if (options & L23_OPT_TAP) + printf(" -i --gsmtap-ip The destination IP used for GSMTAP.\n"); + + if (options & L23_OPT_VTY) + printf(" -v --vty-port The VTY port number to telnet " + "to. (default %u)\n", vty_port); + + if (options & L23_OPT_DBG) + printf(" -d --debug Change debug flags.\n"); + + if (app && app->cfg_print_help) + app->cfg_print_help(); +} + +static void build_config(char **opt, struct option **option) +{ + struct l23_app_info *app; + struct option *app_opp = NULL; + int app_len = 0, len; + + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"socket", 1, 0, 's'}, + {"sap", 1, 0, 'S'}, + {"arfcn", 1, 0, 'a'}, + {"gsmtap-ip", 1, 0, 'i'}, + {"vty-port", 1, 0, 'v'}, + {"debug", 1, 0, 'd'}, + }; + + + app = l23_app_info(); + *opt = talloc_asprintf(l23_ctx, "hs:S:a:i:v:d:%s", + app && app->getopt_string ? app->getopt_string : ""); + + len = ARRAY_SIZE(long_options); + if (app && app->cfg_getopt_opt) + app_len = app->cfg_getopt_opt(&app_opp); + + *option = talloc_zero_array(l23_ctx, struct option, len + app_len + 1); + memcpy(*option, long_options, sizeof(long_options)); + memcpy(*option + len, app_opp, app_len * sizeof(struct option)); +} + +static void handle_options(int argc, char **argv) +{ + struct sockaddr_in gsmtap; + struct l23_app_info *app = l23_app_info(); + struct option *long_options; + char *opt; + + build_config(&opt, &long_options); + + while (1) { + int option_index = 0, c; + + c = getopt_long(argc, argv, opt, + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(argv[0]); + print_help(); + exit(0); + break; + case 's': + layer2_socket_path = talloc_strdup(l23_ctx, optarg); + break; + case 'S': + sap_socket_path = talloc_strdup(l23_ctx, optarg); + break; + case 'a': + ms->test_arfcn = atoi(optarg); + break; + case 'i': + if (!inet_aton(optarg, &gsmtap.sin_addr)) { + perror("inet_aton"); + exit(2); + } + gsmtap_ip = ntohl(gsmtap.sin_addr.s_addr); + break; + case 'v': + vty_port = atoi(optarg); + break; + case 'd': + log_parse_category_mask(stderr_target, optarg); + break; + default: + if (app && app->cfg_handle_opt) + app->cfg_handle_opt(c, optarg); + break; + } + } + + talloc_free(opt); + talloc_free(long_options); +} + +void sighandler(int sigset) +{ + int rc = 0; + + if (sigset == SIGHUP || sigset == SIGPIPE) + return; + + fprintf(stderr, "Signal %d recevied.\n", sigset); + if (l23_app_exit) + rc = l23_app_exit(ms); + + if (rc != -EBUSY) + exit (0); +} + +static void print_copyright() +{ + struct l23_app_info *app; + app = l23_app_info(); + printf(openbsc_copyright, + app && app->copyright ? app->copyright : "", + app && app->contribution ? app->contribution : ""); +} + +int main(int argc, char **argv) +{ + int rc; + + INIT_LLIST_HEAD(&ms_list); + log_init(&log_info); + stderr_target = log_target_create_stderr(); + log_add_target(stderr_target); + log_set_all_filter(stderr_target, 1); + + l23_ctx = talloc_named_const(NULL, 1, "layer2 context"); + + ms = talloc_zero(l23_ctx, struct osmocom_ms); + if (!ms) { + fprintf(stderr, "Failed to allocate MS\n"); + exit(1); + } + + print_copyright(); + + llist_add_tail(&ms->entity, &ms_list); + + sprintf(ms->name, "1"); + + ms->test_arfcn = 871; + + handle_options(argc, argv); + + rc = layer2_open(ms, layer2_socket_path); + if (rc < 0) { + fprintf(stderr, "Failed during layer2_open()\n"); + exit(1); + } + + rc = sap_open(ms, sap_socket_path); + if (rc < 0) + fprintf(stderr, "Failed during sap_open(), no SIM reader\n"); + + lapdm_init(&ms->l2_entity.lapdm_dcch, ms); + lapdm_init(&ms->l2_entity.lapdm_acch, ms); + + rc = l23_app_init(ms); + if (rc < 0) + exit(1); + + if (gsmtap_ip) { + rc = gsmtap_init(gsmtap_ip); + if (rc < 0) { + fprintf(stderr, "Failed during gsmtap_init()\n"); + exit(1); + } + } + + signal(SIGINT, sighandler); + signal(SIGHUP, sighandler); + signal(SIGTERM, sighandler); + signal(SIGPIPE, sighandler); + + while (!quit) { + if (l23_app_work) + l23_app_work(ms); + bsc_select_main(0); + } + + return 0; +} diff --git a/src/host/layer23/src/common/networks.c b/src/host/layer23/src/common/networks.c new file mode 100644 index 00000000..f35dcb15 --- /dev/null +++ b/src/host/layer23/src/common/networks.c @@ -0,0 +1,1983 @@ +#include <string.h> +#include <stdint.h> +#include <stdio.h> + +#include <osmocom/bb/common/networks.h> + +/* list of networks */ + +struct gsm_networks gsm_networks[] = { + { 0x001, -1, "Test" }, + { 0x001, 0x01f, "Test" }, + { 0x412, -1, "Afghanistan" }, + { 0x412, 0x01f, "AWCC" }, + { 0x412, 0x20f, "Roshan" }, + { 0x412, 0x30f, "New1" }, + { 0x412, 0x40f, "Areeba" }, + { 0x412, 0x50f, "Etisalat" }, /* ? */ + { 0x412, 0x88f, "Afghan Telecom" }, + { 0x276, -1, "Albania" }, + { 0x276, 0x01f, "AMC" }, + { 0x276, 0x02f, "Vodafone" }, + { 0x276, 0x03f, "Eagle Mobile" }, + { 0x276, 0x04f, "Mobile 4 AL" }, + { 0x603, -1, "Algeria" }, + { 0x603, 0x01f, "Algerie Telecom" }, + { 0x603, 0x02f, "Orascom Telecom Algerie" }, + { 0x603, 0x03f, "Nedjma" }, /* ? */ + { 0x213, -1, "Andorra" }, + { 0x213, 0x03f, "Mobiland" }, + { 0x631, -1, "Angola" }, + { 0x631, 0x02f, "UNITEL" }, + { 0x365, -1, "Anguilla" }, + { 0x365, 0x010, "Weblinks Limited" }, + { 0x365, 0x840, "Cable & Wireless" }, /* ? */ + { 0x344, -1, "Antigua and Barbuda" }, + { 0x344, 0x030, "APUA PCS" }, + { 0x338, 0x050, "Digicel" }, /* ? */ + { 0x344, 0x920, "Cable & Wireless (Antigua)" }, + { 0x344, 0x930, "AT&T Wireless (Antigua)" }, + { 0x722, -1, "Argentina" }, + { 0x722, 0x010, "Companie de Radiocomunicatciones Moviles S.A." }, + { 0x722, 0x020, "Nextel Argentina srl" }, + { 0x722, 0x070, "Telefonica Communicationes Personales S.A." }, + { 0x722, 0x310, "CTI PCS S.A" }, + { 0x722, 0x320, "Compania de Telefonos del Interior Norte S.A." }, + { 0x722, 0x330, "Companie de Telefonos del Interior S.A." }, + { 0x722, 0x340, "Personal" }, /* ? */ + { 0x722, 0x341, "Telecom Personal S.A." }, + { 0x722, 0x350, "Hutchinson (PORT HABLE)" }, /* ? */ + { 0x283, -1, "Armenia" }, + { 0x283, 0x01f, "Beeline" }, + { 0x283, 0x05f, "VivaCell-MTS" }, + { 0x283, 0x10f, "Orange" }, + { 0x363, -1, "Aruba" }, + { 0x363, 0x01f, "SETAR" }, + { 0x363, 0x02f, "Digicel" }, /* ? */ + { 0x505, -1, "Australia" }, + { 0x505, 0x01f, "Telstra" }, + { 0x505, 0x02f, "Optus" }, + { 0x505, 0x03f, "Vodafone" }, + { 0x505, 0x04f, "Department of Defence" }, + { 0x505, 0x05f, "Ozitel" }, + { 0x505, 0x06f, "Hutchison 3G"}, + { 0x505, 0x07f, "Vodafone" }, + { 0x505, 0x08f, "One.Tel" }, + { 0x505, 0x09f, "Airnet" }, + { 0x505, 0x10f, "Norfolk Telecom" }, + { 0x505, 0x11f, "Telstra" }, + { 0x505, 0x12f, "3" }, + { 0x505, 0x13f, "Railcorp" }, + { 0x505, 0x14f, "AAPT" }, + { 0x505, 0x15f, "3GIS" }, + { 0x505, 0x16f, "Victorian Rail Track" }, + { 0x505, 0x17f, "Vivid Wireless Pty Ltd" }, + { 0x505, 0x18f, "Pactel International Pty Ltd" }, + { 0x505, 0x19f, "Lycamobile Pty Ltd" }, + { 0x505, 0x21f, "SOUL" }, /* ? */ + { 0x505, 0x24f, "Advanced Communications Technologies Pty. Ltd." }, + { 0x505, 0x38f, "Crazy John's" }, /* ? */ + { 0x505, 0x71f, "Telstra" }, + { 0x505, 0x72f, "Telstra" }, + { 0x505, 0x88f, "Localstar Holding Pty. Ltd." }, + { 0x505, 0x90f, "Optus" }, + { 0x505, 0x99f, "One.Tel" }, + { 0x232, -1, "Austria" }, + { 0x232, 0x01f, "A1" }, + { 0x232, 0x02f, "A1" }, + { 0x232, 0x03f, "T-Mobile" }, + { 0x232, 0x04f, "T-Mobile" }, + { 0x232, 0x05f, "Orange" }, + { 0x232, 0x06f, "Orange" }, + { 0x232, 0x07f, "T-Mobile (tele.ring)" }, + { 0x232, 0x09f, "Mobilkom Austria" }, + { 0x232, 0x10f, "Hutchison 3G Austria" }, + { 0x232, 0x11f, "Mobilkom Austria" }, + { 0x232, 0x12f, "Orange Austria" }, + { 0x232, 0x14f, "Hutchison 3G Austria" }, + { 0x232, 0x15f, "Barablu Mobile Austria" }, + { 0x232, 0x16f, "3" }, /* ? */ + { 0x232, 0x91f, "OBB - Infrastruktur Bau AG" }, + { 0x400, -1, "Azerbaijan" }, + { 0x400, 0x01f, "Azercell" }, + { 0x400, 0x02f, "Bakcell" }, + { 0x400, 0x03f, "Catel JV" }, + { 0x400, 0x04f, "Azerphone LLC" }, + { 0x364, -1, "Bahamas" }, + { 0x364, 0x390, "BaTelCo" }, + { 0x426, -1, "Bahrain" }, + { 0x426, 0x01f, "BHR Mobile Plus" }, + { 0x426, 0x02f, "zain BH" }, /* ? */ + { 0x426, 0x04f, "VIVA" }, /* ? */ + { 0x470, -1, "Bangladesh" }, + { 0x470, 0x01f, "Grameenphone" }, + { 0x470, 0x02f, "Aktel" }, + { 0x470, 0x03f, "Mobile 2000" }, + { 0x470, 0x04f, "TeleTalk" }, /* ? */ + { 0x470, 0x05f, "Citycell" }, /* ? */ + { 0x470, 0x06f, "Warid" }, /* ? */ + { 0x470, 0x07f, "WTBL" }, /* ? */ + { 0x342, -1, "Barbados" }, + { 0x342, 0x600, "Cable & Wireless (Barbados) Ltd." }, + { 0x342, 0x750, "Digicel" }, /* ? */ + { 0x342, 0x820, "Sunbeach Communications" }, + { 0x257, -1, "Belarus" }, + { 0x257, 0x01f, "MCD Velcom" }, + { 0x257, 0x02f, "MTS" }, + { 0x257, 0x04f, "life:)" }, /* ? */ + { 0x257, 0x03f, "DIALLOG" }, /* ? */ + { 0x206, -1, "Belgium" }, + { 0x206, 0x01f, "Proximus" }, + { 0x206, 0x02f, "SNCB GSM-R" }, /* ? */ + { 0x206, 0x10f, "Mobistar" }, + { 0x206, 0x20f, "BASE" }, + { 0x702, -1, "Belize" }, + { 0x702, 0x67f, "Belize Telemedia" }, + { 0x702, 0x68f, "International Telecommunications Ltd." }, + { 0x702, 0x00f, "Smart" }, /* ? */ + { 0x616, -1, "Benin" }, + { 0x616, 0x01f, "Libercom" }, + { 0x616, 0x02f, "Telecel" }, + { 0x616, 0x03f, "Spacetel Benin" }, + { 0x616, 0x04f, "BBCOM" }, /* ? */ + { 0x616, 0x05f, "Glo" }, /* ? */ + { 0x350, -1, "Bermuda" }, + { 0x350, 0x01f, "Digicel Bermuda" }, /* ? */ + { 0x350, 0x02f, "Mobility" }, /* ? */ + { 0x338, 0x050, "Digicel Bermuda" }, /* ? */ + { 0x310, 0x00f, "Cellular One" }, /* ? */ + { 0x402, -1, "Bhutan" }, + { 0x402, 0x11f, "Bhutan Telecom Ltd" }, + { 0x402, 0x77f, "B-Mobile" }, + { 0x736, -1, "Bolivia" }, + { 0x736, 0x01f, "Nuevatel" }, + { 0x736, 0x02f, "Entel" }, + { 0x736, 0x03f, "Telecel" }, + { 0x218, -1, "Bosnia and Herzegovina" }, + { 0x218, 0x03f, "HT-ERONET" }, + { 0x218, 0x05f, "MOBI'S" }, + { 0x218, 0x90f, "GSMBIH" }, + { 0x652, -1, "Botswana" }, + { 0x652, 0x01f, "Mascom" }, + { 0x652, 0x02f, "Orange" }, + { 0x652, 0x04f, "BTC Mobile" }, + { 0x724, -1, "Brazil" }, + { 0x724, 0x00f, "Telet" }, + { 0x724, 0x01f, "CRT Cellular" }, + { 0x724, 0x02f, "TIM" }, + { 0x724, 0x03f, "TIM" }, + { 0x724, 0x04f, "TIM" }, + { 0x724, 0x05f, "Claro" }, + { 0x724, 0x06f, "Vivo" }, + { 0x724, 0x07f, "CTBC Celular" }, + { 0x724, 0x08f, "TIM" }, + { 0x724, 0x10f, "Vivo" }, + { 0x724, 0x11f, "Vivo" }, + { 0x724, 0x15f, "Sercomtel" }, + { 0x724, 0x16f, "Oi / Brasil Telecom" }, + { 0x724, 0x23f, "Vivo" }, + { 0x724, 0x24f, "Oi / Amazonia Celular" }, + { 0x724, 0x31f, "Oi" }, + { 0x724, 0x32f, "CTBC Celular" }, + { 0x724, 0x33f, "CTBC Celular" }, + { 0x724, 0x34f, "CTBC Celular" }, + { 0x724, 0x35f, "TIM" }, + { 0x724, 0x37f, "aeiou" }, + { 0x724, 0x39f, "TIM" }, + { 0x724, 0x41f, "TIM" }, + { 0x724, 0x43f, "TIM" }, + { 0x724, 0x45f, "TIM" }, + { 0x724, 0x47f, "TIM" }, + { 0x724, 0x48f, "TIM" }, + { 0x724, 0x51f, "TIM" }, + { 0x724, 0x53f, "TIM" }, + { 0x724, 0x55f, "TIM" }, + { 0x724, 0x57f, "TIM" }, + { 0x724, 0x59f, "TIM" }, + { 0x724, 0x00f, "Nextel" }, + { 0x348, -1, "British Virgin Islands" }, + { 0x348, 0x170, "Cable & Wireless" }, + { 0x348, 0x370, "BVI Cable TV Ltd" }, + { 0x348, 0x570, "CCT Boatphone" }, + { 0x348, 0x770, "Digicel (BVI) Ltd" }, + { 0x528, -1, "Brunei" }, + { 0x528, 0x01f, "Jabatan Telekom" }, /* ? */ + { 0x528, 0x02f, "B-Mobile" }, /* ? */ + { 0x528, 0x11f, "DSTCom" }, + { 0x284, -1, "Bulgaria" }, + { 0x284, 0x01f, "M-Tel" }, + { 0x284, 0x03f, "Vivacom" }, /* ? */ + { 0x284, 0x05f, "GLOBUL" }, + { 0x613, -1, "Burkina Faso" }, + { 0x613, 0x01f, "Onatel" }, /* ? */ + { 0x613, 0x02f, "Celtel / Zain" }, + { 0x613, 0x03f, "Telecel Faso" }, + { 0x642, -1, "Burundi" }, + { 0x642, 0x01f, "Econet / Spacetel" }, + { 0x642, 0x02f, "Africell" }, + { 0x642, 0x03f, "Onamob" }, + { 0x642, 0x07f, "Lacell" }, + { 0x642, 0x08f, "Hits" }, + { 0x642, 0x82f, "U.COM / Onatel" }, + { 0x456, -1, "Cambodia" }, + { 0x456, 0x01f, "Mobitel" }, + { 0x456, 0x02f, "hello" }, + { 0x456, 0x03f, "S Telecom" }, + { 0x456, 0x04f, "Cadcomms / qb" }, + { 0x456, 0x05f, "Star-Cell" }, + { 0x456, 0x06f, "Smart" }, + { 0x456, 0x08f, "Viettel" }, + { 0x456, 0x18f, "Mfone" }, +// { 0x456, ?, "Excell" }, /* ? */ + { 0x456, 0x09f, "Beeline" }, /* ? */ + { 0x456, 0x08f, "Metfone" }, /* ? */ + { 0x624, -1, "Cameroon" }, + { 0x624, 0x01f, "MTN Cameroon" }, + { 0x624, 0x02f, "Orange" }, + { 0x302, -1, "Canada" }, + { 0x302, 0x220, "Telus" }, + { 0x302, 0x221, "Telus" }, + { 0x302, 0x290, "Airtel Wireless" }, + { 0x302, 0x350, "FIRST" }, + { 0x302, 0x360, "MiKe" }, + { 0x302, 0x361, "Telus" }, + { 0x302, 0x370, "Fido" }, + { 0x302, 0x380, "DMTS" }, + { 0x302, 0x490, "WIND Mobile" }, + { 0x302, 0x500, "Videotron" }, + { 0x302, 0x510, "Videotron" }, + { 0x302, 0x610, "Bell" }, + { 0x302, 0x620, "ICE Wireless" }, + { 0x302, 0x640, "Bell" }, + { 0x302, 0x651, "Bell" }, + { 0x302, 0x652, "BC Tel Mobility (Telus)" }, + { 0x302, 0x653, "Telus" }, + { 0x302, 0x655, "MTS" }, + { 0x302, 0x656, "TBay" }, + { 0x302, 0x657, "Telus" }, + { 0x302, 0x680, "SaskTel" }, + { 0x302, 0x701, "MB Tel Mobility" }, + { 0x302, 0x702, "MT&T Mobility (Aliant)" }, + { 0x302, 0x703, "New Tel Mobility (Aliant)" }, + { 0x302, 0x710, "Globalstar" }, + { 0x302, 0x720, "Rogers Wireless" }, + { 0x302, 0x780, "SaskTel" }, + { 0x302, 0x880, "Bell / Telus" }, + { 0x625, -1, "Cape Verde" }, + { 0x625, 0x01f, "CVMOVEL" }, + { 0x625, 0x02f, "T+" }, + { 0x346, -1, "Cayman Islands" }, + { 0x346, 0x140, "Cable & Wireless" }, + { 0x338, 0x050, "Digicel" }, /* ? */ + { 0x623, -1, "Central African Republic" }, + { 0x623, 0x01f, "CTP" }, + { 0x623, 0x02f, "TC" }, + { 0x623, 0x03f, "Celca / Socatel / Orange" }, + { 0x623, 0x04f, "Nationlink" }, /* ? */ + { 0x622, -1, "Chad" }, + { 0x622, 0x01f, "Celtel / Zain" }, + { 0x622, 0x02f, "Tchad Mobile" }, + { 0x622, 0x03f, "TIGO - Millicom" }, /* ? */ + { 0x622, 0x02f, "TAWALI" }, /* ? */ + { 0x622, 0x04f, "Salam" }, /* ? */ + { 0x730, -1, "Chile" }, + { 0x730, 0x01f, "Entel" }, + { 0x730, 0x02f, "movistar" }, + { 0x730, 0x03f, "Smartcom / Claro" }, + { 0x730, 0x04f, "Centennial Cayman Corp / Nextel" }, + { 0x730, 0x05f, "Multikom S.A." }, + { 0x730, 0x06f, "Blue Two Chile S.A." }, + { 0x730, 0x07f, "Telefonica" }, + { 0x730, 0x10f, "Entel" }, + { 0x730, 0x99f, "WILL" }, /* ? */ + { 0x460, -1, "China" }, + { 0x460, 0x00f, "China Mobile" }, + { 0x460, 0x01f, "China Unicom" }, + { 0x460, 0x02f, "China Mobile" }, + { 0x460, 0x03f, "China Unicom CMDA" }, + { 0x460, 0x04f, "China Satellite Global Star Network" }, + { 0x460, 0x05f, "China Telecom" }, /* ? */ + { 0x460, 0x06f, "China Unicom" }, /* ? */ + { 0x460, 0x20f, "China TIETONG" }, /* ? */ + { 0x732, -1, "Colombia" }, + { 0x732, 0x001, "Colombia Telecomunicaciones S.A." }, + { 0x732, 0x002, "Edatel" }, + { 0x732, 0x020, "Emtelsa" }, + { 0x732, 0x099, "Emcali" }, + { 0x732, 0x101, "Comcel" }, + { 0x732, 0x102, "Bellsouth / movistar" }, + { 0x732, 0x103, "Colombia Movil / Tigo" }, + { 0x732, 0x111, "Colombia Movil / Tigo" }, + { 0x732, 0x123, "movistar" }, + { 0x732, 0x12f, "movistar" }, /* ? */ + { 0x732, 0x130, "Avantel" }, + { 0x654, -1, "Comoros" }, + { 0x654, 0x01f, "HURI - SNPT" }, + { 0x629, -1, "Republic of the Congo" }, + { 0x629, 0x01f, "Celtel / Zain" }, + { 0x629, 0x10f, "Libertis Telecom" }, +// { 0x629, ?, "Warid Telecom" }, + { 0x548, -1, "Cook Islands" }, + { 0x548, 0x01f, "Telecom Cook" }, + { 0x712, -1, "Costa Rica" }, + { 0x712, 0x01f, "ICE" }, + { 0x712, 0x02f, "ICE" }, /* ? */ + { 0x712, 0x03f, "ICE" }, /* ? */ + { 0x219, -1, "Croatia" }, + { 0x219, 0x01f, "T-Mobile" }, + { 0x219, 0x02f, "Tele2" }, + { 0x219, 0x10f, "VIPnet" }, + { 0x368, -1, "Cuba" }, + { 0x368, 0x01f, "ETECSA" }, + { 0x280, -1, "Cyprus" }, + { 0x280, 0x01f, "Cytamobile-Vodafone" }, + { 0x280, 0x10f, "Scanacom / MTN" }, + { 0x230, -1, "Czech Republic" }, + { 0x230, 0x01f, "T-Mobile" }, + { 0x230, 0x02f, "O2" }, + { 0x230, 0x03f, "Vodafone" }, + { 0x230, 0x04f, "Mobilkom / U:fon" }, + { 0x230, 0x98f, "SZDC s.o." }, + { 0x230, 0x99f, "Vodafone" }, + { 0x630, -1, "Democratic Republic of the Congo" }, + { 0x630, 0x01f, "Vodacom" }, + { 0x630, 0x02f, "Zain" }, /* ? */ + { 0x630, 0x04f, "Cellco" }, /* ? */ + { 0x630, 0x05f, "Supercell" }, + { 0x630, 0x86f, "CCT" }, + { 0x630, 0x89f, "SAIT Telecom" }, /* ? */ +// { 0x630, ?, "Africell" }, + { 0x238, -1, "Denmark" }, + { 0x238, 0x01f, "TDC" }, + { 0x238, 0x02f, "Sonofon / Telenor" }, + { 0x238, 0x03f, "MIGway A/S" }, + { 0x238, 0x05f, "ApS KBUS" }, + { 0x238, 0x06f, "Hi3G" }, + { 0x238, 0x07f, "Lycamobile / Barablu Mobile" }, + { 0x238, 0x09f, "Dansk Beredskabskommunikation A/S" }, /* ? */ + { 0x238, 0x10f, "TDC" }, + { 0x238, 0x11f, "Dansk Beredskabskommunikation A/S" }, /* ? */ + { 0x238, 0x12f, "Lycamobile Denmark Ltd" }, + { 0x238, 0x20f, "Telia" }, + { 0x238, 0x30f, "Telia" }, + { 0x238, 0x40f, "Ericsson Danmark A/S" }, /* ? */ + { 0x238, 0x77f, "Tele2 / Telenor" }, + { 0x638, -1, "Djibouti" }, + { 0x638, 0x01f, "Evatis" }, + { 0x366, -1, "Dominica" }, + { 0x366, 0x020, "Digicel" }, /* ? */ + { 0x366, 0x110, "Cable & Wireless" }, /* ? */ + { 0x370, -1, "Dominican Republic" }, + { 0x370, 0x01f, "Orange" }, + { 0x370, 0x02f, "Verizon / Claro" }, + { 0x370, 0x03f, "Tricom" }, + { 0x370, 0x04f, "CentennialDominicana / Viva" }, + { 0x514, -1, "East Timor" }, + { 0x514, 0x02f, "Timor Telecom" }, /* ? */ + { 0x740, -1, "Ecuador" }, + { 0x740, 0x00f, "Otecel / Bellsouth / Movistar" }, + { 0x740, 0x01f, "Porta GSM" }, + { 0x740, 0x02f, "Telecsa / Alegro" }, + { 0x602, -1, "Egypt" }, + { 0x602, 0x01f, "Mobinil" }, + { 0x602, 0x02f, "Vodafone" }, + { 0x602, 0x03f, "Etisalat" }, + { 0x706, -1, "El Salvador" }, + { 0x706, 0x01f, "CTE Telecom Personal" }, + { 0x706, 0x02f, "digicel" }, + { 0x706, 0x03f, "Telemovil EL Salvador" }, + { 0x706, 0x04f, "movistar" }, /* ? */ + { 0x706, 0x10f, "Claro" }, /* ? */ + { 0x627, -1, "Equatorial Guinea" }, + { 0x627, 0x01f, "Orange GQ" }, + { 0x627, 0x03f, "Hits GQ" }, + { 0x657, -1, "Eritrea" }, + { 0x657, 0x01f, "Eritel" }, /* ? */ + { 0x248, -1, "Estonia" }, + { 0x248, 0x01f, "EMT" }, + { 0x248, 0x02f, "RLE / Elisa" }, + { 0x248, 0x03f, "Tele 2" }, + { 0x248, 0x04f, "OY Top Connect" }, + { 0x248, 0x05f, "AS Bravocom Mobiil" }, + { 0x248, 0x06f, "Pro Group Holding / ViaTel" }, + { 0x248, 0x07f, "Televorgu AS" }, + { 0x248, 0x71f, "Siseministeerium" }, + { 0x636, -1, "Ethiopia" }, + { 0x636, 0x01f, "ETMTN" }, + { 0x750, -1, "Falkland Islands (Malvinas)" }, + { 0x750, 0x001, "Touch" }, + { 0x288, -1, "Faroe Islands" }, + { 0x288, 0x01f, "Faroese Telecom" }, + { 0x288, 0x02f, "Kall / Vodafone" }, + { 0x274, 0x02f, "P/F Kall" }, + { 0x542, -1, "Fiji" }, + { 0x542, 0x01f, "Vodafone" }, + { 0x542, 0x02f, "Digicel" }, + { 0x542, 0x03f, "Telecom Fiji" }, + { 0x244, -1, "Finland" }, + { 0x244, 0x03f, "DNA" }, /* ? */ + { 0x244, 0x04f, "Finnet" }, + { 0x244, 0x05f, "Elisa" }, + { 0x244, 0x07f, "Nokia" }, + { 0x244, 0x08f, "Unknown" }, + { 0x244, 0x09f, "Finnet Group" }, + { 0x244, 0x10f, "TDC Oy" }, + { 0x244, 0x12f, "Finnet Networks / DNA" }, + { 0x244, 0x14f, "AMT" }, + { 0x244, 0x16f, "Oy Finland Tele2" }, + { 0x244, 0x21f, "Saunalahti" }, + { 0x244, 0x29f, "Scnl Truphone" }, /* ? */ + { 0x244, 0x91f, "Sonera" }, + { 0x208, -1, "France" }, + { 0x208, 0x00f, "Orange" }, /* ? */ + { 0x208, 0x01f, "Orange" }, + { 0x208, 0x02f, "Orange" }, + { 0x208, 0x05f, "Globalstar Europe" }, + { 0x208, 0x06f, "Globalstar Europe" }, + { 0x208, 0x07f, "Globalstar Europe" }, + { 0x208, 0x10f, "SFR" }, + { 0x208, 0x11f, "SFR" }, + { 0x208, 0x13f, "SFR" }, /* ? */ + { 0x208, 0x20f, "Bouygues" }, + { 0x208, 0x21f, "Bouygues" }, + { 0x208, 0x22f, "Transatel Mobile" }, + { 0x208, 0x88f, "Bouygues" }, + { 0x628, -1, "Gabon" }, + { 0x628, 0x01f, "Libertis" }, + { 0x628, 0x02f, "Moov (Telecel) Gabon S.A." }, + { 0x628, 0x03f, "Celtel / Zain" }, + { 0x628, 0x04f, "USAN Gabon" }, + { 0x607, -1, "Gambia" }, + { 0x607, 0x01f, "Gamcel" }, + { 0x607, 0x02f, "Africel" }, + { 0x607, 0x03f, "Comium" }, + { 0x607, 0x04f, "QCell" }, /* ? */ + { 0x282, -1, "Georgia" }, + { 0x282, 0x01f, "Geocell" }, + { 0x282, 0x02f, "MagtiCom" }, + { 0x282, 0x03f, "Iberiatel" }, + { 0x282, 0x04f, "Beeline" }, + { 0x282, 0x05f, "Silknet JSC" }, + { 0x289, 0x67f, "Aquafon" }, /* ? */ + { 0x289, 0x88f, "A-Mobile" }, /* ? */ + { 0x262, -1, "Germany" }, + { 0x262, 0x01f, "T-Mobile" }, + { 0x262, 0x02f, "Vodafone" }, + { 0x262, 0x03f, "E-Plus" }, + { 0x262, 0x04f, "Vodafone" }, + { 0x262, 0x05f, "E-Plus" }, + { 0x262, 0x06f, "T-Mobile" }, + { 0x262, 0x07f, "O2" }, + { 0x262, 0x08f, "O2" }, + { 0x262, 0x09f, "Vodafone" }, + { 0x262, 0x10f, "DB Systel GSM-R" }, + { 0x262, 0x11f, "O2" }, + { 0x262, 0x12f, "Dolphin Telecom" }, + { 0x262, 0x13f, "Mobilcom Multimedia" }, + { 0x262, 0x14f, "Group 3G UMTS" }, + { 0x262, 0x15f, "Airdata" }, + { 0x262, 0x16f, "Vistream" }, /* ? */ + { 0x262, 0x42f, "OpenBSC" }, /* ? */ + { 0x262, 0x60f, "DB Telematik" }, + { 0x262, 0x76f, "Siemens AG" }, + { 0x262, 0x77f, "E-Plus" }, + { 0x262, 0x901, "Debitel" }, /* ? */ + { 0x620, -1, "Ghana" }, + { 0x620, 0x01f, "Spacefon / MTN" }, + { 0x620, 0x02f, "Ghana Telecom Mobile / Vodafone" }, + { 0x620, 0x03f, "Mobiltel / tiGO" }, + { 0x620, 0x04f, "Kasapa / Hutchison Telecom" }, + { 0x620, 0x06f, "Zain" }, /* ? */ + { 0x620, 0x10f, "Netafriques" }, /* ? */ + { 0x266, -1, "Gibraltar" }, + { 0x266, 0x01f, "GibTel" }, + { 0x266, 0x06f, "CTS Mobile" }, + { 0x266, 0x09f, "Cloud9 Mobile Communications" }, + { 0x202, -1, "Greece" }, + { 0x202, 0x01f, "Cosmote" }, + { 0x202, 0x05f, "Vodafone" }, + { 0x202, 0x09f, "Infoquest / Wind" }, + { 0x202, 0x10f, "Wind" }, + { 0x290, -1, "Greenland" }, + { 0x290, 0x01f, "TELE Greenland A/S" }, + { 0x352, -1, "Grenada" }, + { 0x352, 0x030, "Digicel" }, + { 0x352, 0x110, "Cable & Wireless" }, + { 0x340, -1, "Guadeloupe" }, + { 0x340, 0x01f, "Orange" }, + { 0x340, 0x02f, "Outremer" }, + { 0x340, 0x03f, "Telcell" }, + { 0x340, 0x08f, "MIO GSM" }, + { 0x340, 0x10f, "Guadeloupe Telephone Mobile" }, + { 0x340, 0x20f, "Digicel" }, + { 0x310, -1, "United States of America" }, + { 0x310, 0x010, "Verizon Wireless" }, + { 0x310, 0x012, "Verizon Wireless" }, + { 0x310, 0x013, "Verizon Wireless" }, + { 0x310, 0x016, "Cricket Communications" }, + { 0x310, 0x017, "North Sight Communications Inc." }, + { 0x310, 0x020, "Union Telephone Company" }, + { 0x310, 0x030, "Centennial Communications" }, + { 0x310, 0x035, "ETEX Communications dba ETEX Wireless" }, + { 0x310, 0x040, "MTA Communications dba MTA Wireless" }, + { 0x310, 0x050, "ACS Wireless Inc." }, + { 0x310, 0x060, "Consolidated Telecom" }, + { 0x310, 0x070, "Cingular Wireless" }, + { 0x310, 0x080, "Corr Wireless Communications LLC" }, + { 0x310, 0x090, "Cingular Wireless" }, + { 0x310, 0x100, "New Mexicu RSA 4 East Ltd. Partnership" }, + { 0x310, 0x110, "Pacific Telecom Inc." }, + { 0x310, 0x130, "Carolina West Wireless" }, + { 0x310, 0x140, "GTA Wireless LLC" }, + { 0x310, 0x150, "Cingular Wireless" }, + { 0x310, 0x160, "T-Mobile USA" }, + { 0x310, 0x170, "Cingular Wireless" }, + { 0x310, 0x180, "West Central Wireless" }, + { 0x310, 0x190, "Alaska Wireless Communications LLC" }, + { 0x310, 0x200, "T-Mobile USA" }, + { 0x310, 0x210, "T-Mobile USA" }, + { 0x310, 0x220, "T-Mobile USA" }, + { 0x310, 0x230, "T-Mobile USA" }, + { 0x310, 0x240, "T-Mobile USA" }, + { 0x310, 0x250, "T-Mobile USA" }, + { 0x310, 0x260, "T-Mobile USA" }, + { 0x310, 0x270, "T-Mobile USA" }, + { 0x310, 0x280, "Contennial Puerto Rio License Corp." }, + { 0x310, 0x290, "Nep Cellcorp Inc." }, + { 0x310, 0x300, "Blanca Telephone Company" }, + { 0x310, 0x310, "T-Mobile USA" }, + { 0x310, 0x320, "Simth Bagley Inc, dba Cellular One" }, + { 0x310, 0x340, "High Plains Midwest LLC, dba Wetlink Communications" }, + { 0x310, 0x350, "Mohave Cellular L.P." }, + { 0x310, 0x360, "Cellular Network Partnership dba Pioneer Cellular" }, + { 0x310, 0x370, "Guamcell Cellular and Paging" }, + { 0x310, 0x380, "New Cingular Wireless PCS, LLC" }, + { 0x310, 0x390, "TX-11 Acquisition LLC" }, + { 0x310, 0x400, "Wave Runner LLC" }, + { 0x310, 0x410, "Cingular Wireless" }, + { 0x310, 0x420, "Cincinnati Bell Wireless LLC" }, + { 0x310, 0x430, "Alaska Digital LLC" }, + { 0x310, 0x440, "Numerex Corp" }, + { 0x310, 0x450, "North East Cellular Inc" }, + { 0x310, 0x460, "TMP Corporation" }, + { 0x310, 0x470, "nTELOS Communications Inc" }, + { 0x310, 0x480, "Choice Phone LLC" }, + { 0x310, 0x490, "T-Mobile USA" }, + { 0x310, 0x500, "Public Service Cellular, Inc." }, + { 0x310, 0x520, "Transactions Network Services" }, + { 0x310, 0x530, "Iowa Wireless Services LLC" }, + { 0x310, 0x540, "Oklahoma Western Telephone Company" }, + { 0x310, 0x550, "Wireless Solutions International" }, + { 0x310, 0x560, "Cingular Wireless" }, + { 0x310, 0x570, "MTPCS LLC" }, + { 0x310, 0x580, "Inland Celluar Telephone Company" }, + { 0x310, 0x590, "Western Wireless Corporation" }, + { 0x310, 0x600, "New Cell Inc. dba Cellcom" }, + { 0x310, 0x610, "Elkhart Telephone Co. Inc. dba Epic Touch Co." }, + { 0x310, 0x620, "Coleman County Telecommunications Inc. (Trans Texas PCS)" }, + { 0x310, 0x640, "Airadigm Communications" }, + { 0x310, 0x650, "Jasper Wireless Inc." }, + { 0x310, 0x660, "T-Mobile USA" }, + { 0x310, 0x670, "AT&T Mobility Vanguard Services" }, + { 0x310, 0x680, "Cingular Wireless" }, + { 0x310, 0x690, "Keystane Wireless LLC" }, + { 0x310, 0x700, "Cross Valiant Cellular Partnership" }, + { 0x310, 0x710, "Arctic Slope Telephone Association Cooperative" }, + { 0x310, 0x720, "Wireless Solutions International Inc." }, + { 0x310, 0x730, "US Cellular" }, + { 0x310, 0x740, "Convey Communications Inc" }, + { 0x310, 0x750, "East Kentucky Network LLC dba Appalachian Wireless" }, + { 0x310, 0x760, "Lynch 3G Communcations Corporation" }, + { 0x310, 0x770, "Iowa Wireless Services LLC dba I Wireless" }, + { 0x310, 0x780, "Connect Net Inc" }, + { 0x310, 0x790, "PinPoint Communications Inc."}, + { 0x310, 0x800, "T-Mobile USA" }, + { 0x310, 0x810, "LCFR LLC" }, + { 0x310, 0x820, "South Canaan Cellular Communications Co. LP" }, + { 0x310, 0x830, "Caprock Cellular Ltd. Partnership" }, + { 0x310, 0x840, "Telecom North America Mobile Inc" }, + { 0x310, 0x850, "Aeris Communications Inc." }, + { 0x310, 0x860, "TX RSA 15B2, LP dba Five Star Wireless" }, + { 0x310, 0x870, "Kaplan Telephone Company, Inc" }, + { 0x310, 0x890, "Rural Cellular Corporation" }, + { 0x310, 0x900, "Cable & Communications Corporation dba Mid-Rivers Wireless" }, + { 0x310, 0x910, "Verizon Wireless" }, + { 0x310, 0x930, "Copper Valley Wireless" }, + { 0x310, 0x940, "Iris Wireless LLC" }, + { 0x310, 0x950, "Texas RSA 1 dba XIT Wireless" }, + { 0x310, 0x960, "UBET Wireless" }, + { 0x310, 0x970, "Globalstar USA" }, + { 0x310, 0x980, "Texas RSA 7B3 dba Peoples Wireless Services" }, + { 0x310, 0x99, "Worldcall Interconnect" }, + + { 0x704, -1, "Guatemala" }, + { 0x704, 0x01f, "Claro" }, + { 0x704, 0x02f, "Comcel / Tigo" }, + { 0x704, 0x03f, "movistar" }, +// { 0x704, ?, "digicel" }, + { 0x234, -1, "Guernsey" }, + { 0x234, 0x55f, "Sure Mobile" }, + { 0x234, 0x50f, "Wave Telecom" }, + { 0x234, 0x03f, "Airtel Vodafone" }, + { 0x611, -1, "Guinea" }, + { 0x611, 0x01f, "Orange / Spacetel" }, + { 0x611, 0x02f, "Sotelgui / Lagui" }, + { 0x611, 0x03f, "Telecel Guinee" }, /* ? */ + { 0x611, 0x04f, "MTN" }, /* ? */ + { 0x611, 0x05f, "Cellcom Guinee" }, + { 0x632, -1, "Guinea-Bissau" }, + { 0x632, 0x01f, "Guinetel" }, + { 0x632, 0x02f, "Spacetel / Areeba" }, + { 0x632, 0x03f, "Orange" }, + { 0x738, -1, "Guyana" }, + { 0x738, 0x01f, "Digicel" }, + { 0x738, 0x02f, "GT&T Cellink Plus" }, /* ? */ + { 0x372, -1, "Haiti" }, + { 0x372, 0x01f, "Comcel / Voila" }, + { 0x338, 0x050, "Digicel" }, + { 0x338, 0x03f, "Rectel" }, + { 0x708, -1, "Honduras" }, + { 0x708, 0x01f, "Claro" }, + { 0x708, 0x02f, "Celtel / Tigo" }, + { 0x708, 0x30f, "Hondutel" }, /* ? */ + { 0x708, 0x40f, "DIGICEL" }, + { 0x454, -1, "Hong Kong" }, + { 0x454, 0x00f, "1O1O and One2Free" }, + { 0x454, 0x01f, "CITIC Telecom 1616" }, + { 0x454, 0x02f, "CSL Limited" }, + { 0x454, 0x03f, "3 (3G)" }, + { 0x454, 0x04f, "3 DualBand (2G)" }, + { 0x454, 0x05f, "3 CDMA" }, + { 0x454, 0x06f, "SmarTone-Vodafone" }, + { 0x454, 0x07f, "China Unicom (Hong Kong) Limited" }, + { 0x454, 0x08f, "Trident" }, + { 0x454, 0x09f, "China Motion Telecom" }, + { 0x454, 0x10f, "New World Mobility" }, + { 0x454, 0x11f, "China-Hongkong Telecom" }, + { 0x454, 0x12f, "CMCC HK" }, + { 0x454, 0x14f, "Hutchison Telecom" }, + { 0x454, 0x15f, "SmarTone Mobile Communications Limited" }, + { 0x454, 0x16f, "PCCW Mobile (2G)" }, + { 0x454, 0x17f, "SmarTone Mobile Communications Limited" }, + { 0x454, 0x18f, "CSL Limited" }, + { 0x454, 0x19f, "Sunday3G" }, + { 0x454, 0x19f, "PCCW Mobile (3G)" }, + { 0x454, 0x29f, "PCCW Mobile (CDMA)" }, + { 0x216, -1, "Hungary" }, + { 0x216, 0x01f, "Pannon GSM" }, + { 0x216, 0x30f, "Westel 900" }, + { 0x216, 0x70f, "Vodafone" }, + { 0x274, -1, "Iceland" }, + { 0x274, 0x01f, "Siminn" }, + { 0x274, 0x02f, "Vodafone" }, + { 0x274, 0x03f, "Vodafone" }, + { 0x274, 0x04f, "IMC Viking" }, + { 0x274, 0x06f, "N?ll n?u ehf" }, /* ? */ + { 0x274, 0x07f, "IceCell" }, + { 0x274, 0x08f, "On-waves" }, + { 0x274, 0x11f, "Nova" }, + /* FIXME: update the list from here below */ + { 0x404, -1, "India" }, + { 0x404, 0x01f, "Vodafone IN" }, + { 0x404, 0x02f, "AirTel" }, + { 0x404, 0x04f, "IDEA" }, + { 0x404, 0x05f, "Vodafone IN" }, + { 0x404, 0x07f, "IDEA" }, + { 0x404, 0x09f, "Reliance" }, + { 0x404, 0x10f, "AirTel" }, + { 0x404, 0x11f, "Vodafone IN" }, + { 0x404, 0x12f, "IDEA" }, + { 0x404, 0x13f, "Vodafone IN" }, + { 0x404, 0x14f, "IDEA" }, + { 0x404, 0x15f, "Vodafone IN" }, + { 0x404, 0x17f, "AIRCEL" }, + { 0x404, 0x19f, "IDEA" }, + { 0x404, 0x20f, "Vodafone IN" }, + { 0x404, 0x21f, "Loop Mobile" }, + { 0x404, 0x22f, "IDEA" }, + { 0x404, 0x24f, "IDEA" }, + { 0x404, 0x27f, "Vodafone IN" }, + { 0x404, 0x28f, "AIRCEL" }, + { 0x404, 0x29f, "AIRCEL" }, + { 0x404, 0x30f, "Vodafone IN" }, + { 0x404, 0x31f, "AirTel" }, + { 0x404, 0x34f, "CellOne" }, + { 0x404, 0x36f, "Reliance" }, + { 0x404, 0x37f, "Aircel" }, + { 0x404, 0x38f, "CellOne" }, + { 0x404, 0x41f, "Aircel" }, + { 0x404, 0x42f, "Aircel" }, + { 0x404, 0x44f, "IDEA" }, + { 0x404, 0x45f, "Airtel" }, + { 0x404, 0x51f, "CellOne" }, + { 0x404, 0x52f, "Reliance" }, + { 0x404, 0x53f, "CellOne" }, + { 0x404, 0x54f, "CellOne" }, + { 0x404, 0x55f, "CellOne" }, + { 0x404, 0x56f, "IDEA" }, + { 0x404, 0x57f, "CellOne" }, + { 0x404, 0x58f, "CellOne" }, + { 0x404, 0x59f, "CellOne" }, + { 0x404, 0x60f, "Vodafone IN" }, + { 0x404, 0x62f, "CellOne" }, + { 0x404, 0x64f, "CellOne" }, + { 0x404, 0x66f, "CellOne" }, + { 0x404, 0x67f, "Reliance GSM" }, + { 0x404, 0x68f, "DOLPHIN" }, + { 0x404, 0x69f, "DOLPHIN" }, + { 0x404, 0x72f, "CellOne" }, + { 0x404, 0x74f, "CellOne" }, + { 0x404, 0x76f, "CellOne" }, + { 0x404, 0x78f, "Idea Cellular Ltd" }, + { 0x404, 0x80f, "BSNL MOBILE" }, + { 0x404, 0x81f, "CellOne" }, + { 0x404, 0x82f, "Idea" }, + { 0x404, 0x83f, "Reliance Smart GSM" }, + { 0x404, 0x84f, "Vodafone IN" }, + { 0x404, 0x85f, "Reliance" }, + { 0x404, 0x86f, "Vodafone IN" }, + { 0x404, 0x90f, "AirTel" }, + { 0x404, 0x91f, "AIRCEL" }, + { 0x404, 0x92f, "AirTel" }, + { 0x404, 0x93f, "AirTel" }, + { 0x404, 0x96f, "AirTel" }, + { 0x405, 0x05f, "Reliance" }, + { 0x405, 0x10f, "Reliance" }, + { 0x405, 0x13f, "Reliance" }, + { 0x405, 0x025, "TATA DOCOMO" }, + { 0x405, 0x026, "TATA DOCOMO" }, + { 0x405, 0x027, "TATA DOCOMO" }, + { 0x405, 0x029, "TATA DOCOMO" }, + { 0x405, 0x030, "TATA DOCOMO" }, + { 0x405, 0x031, "TATA DOCOMO" }, + { 0x405, 0x032, "TATA DOCOMO" }, + { 0x405, 0x034, "TATA DOCOMO" }, + { 0x405, 0x035, "TATA DOCOMO" }, + { 0x405, 0x036, "TATA DOCOMO" }, + { 0x405, 0x037, "TATA DOCOMO" }, + { 0x405, 0x038, "TATA DOCOMO" }, + { 0x405, 0x039, "TATA DOCOMO" }, + { 0x405, 0x041, "TATA DOCOMO" }, + { 0x405, 0x042, "TATA DOCOMO" }, + { 0x405, 0x043, "TATA DOCOMO" }, + { 0x405, 0x044, "TATA DOCOMO" }, + { 0x405, 0x045, "TATA DOCOMO" }, + { 0x405, 0x046, "TATA DOCOMO" }, + { 0x405, 0x047, "TATA DOCOMO" }, + { 0x405, 0x51f, "AirTel" }, + { 0x405, 0x52f, "AirTel" }, + { 0x405, 0x54f, "AirTel" }, + { 0x405, 0x56f, "AirTel" }, + { 0x405, 0x66f, "Vodafone IN" }, + { 0x405, 0x70f, "IDEA" }, + { 0x405, 0x750, "Vodafone IN" }, + { 0x405, 0x751, "Vodafone IN" }, + { 0x405, 0x752, "Vodafone IN" }, + { 0x405, 0x753, "Vodafone IN" }, + { 0x405, 0x754, "Vodafone IN" }, + { 0x405, 0x755, "Vodafone IN" }, + { 0x405, 0x756, "Vodafone IN" }, + { 0x405, 0x799, "IDEA" }, + { 0x405, 0x800, "AIRCEL" }, + { 0x405, 0x801, "AIRCEL" }, + { 0x405, 0x802, "AIRCEL" }, + { 0x405, 0x803, "AIRCEL" }, + { 0x405, 0x804, "AIRCEL" }, + { 0x405, 0x805, "AIRCEL" }, + { 0x405, 0x806, "AIRCEL" }, + { 0x405, 0x807, "AIRCEL" }, + { 0x405, 0x808, "AIRCEL" }, + { 0x405, 0x809, "AIRCEL" }, + { 0x405, 0x810, "AIRCEL" }, + { 0x405, 0x811, "AIRCEL" }, + { 0x405, 0x812, "AIRCEL" }, + { 0x405, 0x819, "Uninor" }, + { 0x405, 0x818, "[Uninor]" }, + { 0x405, 0x820, "Uninor" }, + { 0x405, 0x821, "Uninor" }, + { 0x405, 0x822, "Uninor" }, + { 0x405, 0x880, "Uninor" }, + { 0x405, 0x824, "Videocon Datacom" }, + { 0x405, 0x834, "Videocon Datacom" }, + { 0x405, 0x844, "Uninor" }, + { 0x405, 0x845, "IDEA" }, + { 0x405, 0x848, "IDEA" }, + { 0x405, 0x850, "IDEA" }, + { 0x405, 0x855, "Loop Mobile" }, + { 0x405, 0x864, "Loop Mobile" }, + { 0x405, 0x865, "Loop Mobile" }, + { 0x405, 0x875, "Uninor" }, + { 0x405, 0x881, "S Tel" }, + { 0x405, 0x912, "Etisalat DB" }, + { 0x405, 0x913, "Etisalat DB" }, + { 0x405, 0x917, "Etisalat DB" }, + { 0x405, 0x929, "Uninor" }, + { 0x510, -1, "Indonesia" }, + { 0x510, 0x00f, "PSN" }, + { 0x510, 0x01f, "INDOSAT" }, + { 0x510, 0x03f, "StarOne" }, + { 0x510, 0x07f, "TelkomFlexi" }, + { 0x510, 0x08f, "AXIS" }, + { 0x510, 0x09f, "SMART" }, + { 0x510, 0x10f, "Telkomsel" }, + { 0x510, 0x11f, "XL" }, + { 0x510, 0x20f, "TELKOMMobile" }, + { 0x510, 0x21f, "IM3" }, + { 0x510, 0x27f, "Ceria" }, + { 0x510, 0x28f, "Fren/Hepi" }, + { 0x510, 0x89f, "3" }, + { 0x510, 0x99f, "Esia " }, + { 0x432, -1, "Iran" }, + { 0x432, 0x11f, "MCI" }, + { 0x432, 0x14f, "TKC" }, + { 0x432, 0x19f, "MTCE" }, + { 0x432, 0x32f, "Taliya" }, + { 0x432, 0x35f, "Irancell" }, + { 0x418, -1, "Iraq" }, + { 0x418, 0x20f, "Zain IQ" }, + { 0x418, 0x30f, "Zain IQ" }, + { 0x418, 0x05f, "Asia Cell" }, + { 0x418, 0x40f, "Korek" }, + { 0x418, 0x08f, "SanaTel" }, +// { 0x418, ?, "IRAQNA" }, + { 0x272, -1, "Ireland" }, + { 0x272, 0x01f, "Vodafone" }, + { 0x272, 0x02f, "O2" }, + { 0x272, 0x03f, "Meteor" }, + { 0x272, 0x04f, "Access Telecom" }, + { 0x272, 0x05f, "3" }, + { 0x272, 0x07f, "Eircom" }, + { 0x272, 0x09f, "Clever Communications" }, + { 0x234, -1, "Isle of Man" }, + { 0x234, 0x58f, "Pronto GSM" }, + { 0x425, -1, "Israel" }, + { 0x425, 0x01f, "Orange" }, + { 0x425, 0x02f, "Cellcom" }, + { 0x425, 0x03f, "Pelephone" }, + { 0x425, 0x77f, "Mirs" }, + { 0x222, -1, "Italy" }, + { 0x222, 0x01f, "TIM" }, + { 0x222, 0x02f, "Elsacom" }, + { 0x222, 0x10f, "Vodafone" }, + { 0x222, 0x30f, "RFI" }, + { 0x222, 0x77f, "IPSE 2000" }, + { 0x222, 0x88f, "Wind" }, + { 0x222, 0x98f, "Blu" }, + { 0x222, 0x99f, "3 Italia" }, + { 0x612, -1, "Ivory Coast" }, + { 0x612, 0x01f, "Cora de Comstar" }, + { 0x612, 0x02f, "Moov" }, + { 0x612, 0x03f, "Orange" }, + { 0x612, 0x04f, "KoZ" }, + { 0x612, 0x05f, "MTN" }, + { 0x612, 0x06f, "ORICEL" }, + { 0x338, -1, "Jamaica" }, + { 0x338, 0x020, "LIME (formerly known as Cable & Wireless)" }, + { 0x338, 0x050, "Digicel" }, + { 0x338, 0x070, "Claro" }, + { 0x338, 0x180, "LIME (formerly known as Cable & Wireless)" }, + { 0x440, -1, "Japan" }, + { 0x440, 0x00f, "eMobile" }, + { 0x440, 0x01f, "NTT docomo" }, + { 0x440, 0x02f, "NTT docomo" }, + { 0x440, 0x03f, "NTT docomo" }, + { 0x440, 0x04f, "SoftBank" }, + { 0x440, 0x06f, "SoftBank" }, + { 0x440, 0x07f, "KDDI" }, + { 0x440, 0x08f, "KDDI" }, + { 0x440, 0x09f, "NTT docomo" }, + { 0x440, 0x10f, "NTT docomo" }, + { 0x440, 0x11f, "NTT docomo" }, + { 0x440, 0x12f, "NTT docomo" }, + { 0x440, 0x13f, "NTT docomo" }, + { 0x440, 0x14f, "NTT docomo" }, + { 0x440, 0x15f, "NTT docomo" }, + { 0x440, 0x16f, "NTT docomo" }, + { 0x440, 0x17f, "NTT docomo" }, + { 0x440, 0x18f, "NTT docomo" }, + { 0x440, 0x19f, "NTT docomo" }, + { 0x440, 0x20f, "SoftBank" }, + { 0x440, 0x21f, "NTT docomo" }, + { 0x440, 0x22f, "NTT docomo" }, + { 0x440, 0x23f, "DoCoMo" }, + { 0x440, 0x24f, "DoCoMo" }, + { 0x440, 0x25f, "DoCoMo" }, + { 0x440, 0x26f, "DoCoMo" }, + { 0x440, 0x27f, "DoCoMo" }, + { 0x440, 0x28f, "DoCoMo" }, + { 0x440, 0x29f, "DoCoMo" }, + { 0x440, 0x30f, "DoCoMo" }, + { 0x440, 0x31f, "DoCoMo" }, + { 0x440, 0x32f, "DoCoMo" }, + { 0x440, 0x33f, "DoCoMo" }, + { 0x440, 0x34f, "DoCoMo" }, + { 0x440, 0x35f, "DoCoMo" }, + { 0x440, 0x36f, "DoCoMo" }, + { 0x440, 0x37f, "DoCoMo" }, + { 0x440, 0x38f, "DoCoMo" }, + { 0x440, 0x39f, "DoCoMo" }, + { 0x440, 0x40f, "SoftBank" }, + { 0x440, 0x41f, "SoftBank" }, + { 0x440, 0x42f, "SoftBank" }, + { 0x440, 0x43f, "SoftBank" }, + { 0x440, 0x44f, "SoftBank" }, + { 0x440, 0x45f, "SoftBank" }, + { 0x440, 0x46f, "SoftBank" }, + { 0x440, 0x47f, "SoftBank" }, + { 0x440, 0x48f, "SoftBank" }, + { 0x440, 0x49f, "DoCoMo" }, + { 0x440, 0x50f, "KDDI" }, + { 0x440, 0x51f, "KDDI" }, + { 0x440, 0x52f, "KDDI" }, + { 0x440, 0x53f, "KDDI" }, + { 0x440, 0x54f, "KDDI" }, + { 0x440, 0x55f, "KDDI" }, + { 0x440, 0x56f, "KDDI" }, + { 0x440, 0x58f, "DoCoMo" }, + { 0x440, 0x60f, "DoCoMo" }, + { 0x440, 0x61f, "DoCoMo" }, + { 0x440, 0x62f, "DoCoMo" }, + { 0x440, 0x63f, "DoCoMo" }, + { 0x440, 0x64f, "DoCoMo" }, + { 0x440, 0x65f, "DoCoMo" }, + { 0x440, 0x66f, "DoCoMo" }, + { 0x440, 0x67f, "DoCoMo" }, + { 0x440, 0x68f, "DoCoMo" }, + { 0x440, 0x69f, "DoCoMo" }, + { 0x440, 0x70f, "au" }, + { 0x440, 0x71f, "KDDI" }, + { 0x440, 0x72f, "KDDI" }, + { 0x440, 0x73f, "KDDI" }, + { 0x440, 0x74f, "KDDI" }, + { 0x440, 0x75f, "KDDI" }, + { 0x440, 0x76f, "KDDI" }, + { 0x440, 0x77f, "KDDI" }, + { 0x440, 0x78f, "Okinawa Cellular Telephone" }, + { 0x440, 0x79f, "KDDI" }, + { 0x440, 0x80f, "TU-KA" }, + { 0x440, 0x81f, "TU-KA" }, + { 0x440, 0x82f, "TU-KA" }, + { 0x440, 0x83f, "TU-KA" }, + { 0x440, 0x84f, "TU-KA" }, + { 0x440, 0x85f, "TU-KA" }, + { 0x440, 0x86f, "TU-KA" }, + { 0x440, 0x87f, "DoCoMo" }, + { 0x440, 0x88f, "KDDI" }, + { 0x440, 0x89f, "KDDI" }, + { 0x440, 0x90f, "SoftBank" }, + { 0x440, 0x92f, "SoftBank" }, + { 0x440, 0x93f, "SoftBank" }, + { 0x440, 0x94f, "SoftBank" }, + { 0x440, 0x95f, "SoftBank" }, + { 0x440, 0x96f, "SoftBank" }, + { 0x440, 0x97f, "SoftBank" }, + { 0x440, 0x98f, "SoftBank" }, + { 0x440, 0x99f, "DoCoMo" }, + { 0x234, -1, "Jersey" }, + { 0x234, 0x50f, "JT-Wave" }, + { 0x234, 0x55f, "Sure Mobile" }, + { 0x234, 0x03f, "Airtel Vodafone" }, + { 0x416, -1, "Jordan" }, + { 0x416, 0x01f, "zain JO" }, + { 0x416, 0x02f, "XPress Telecom" }, + { 0x416, 0x03f, "Umniah" }, + { 0x416, 0x77f, "Orange" }, + { 0x401, -1, "Kazakhstan" }, + { 0x401, 0x01f, "Beeline" }, + { 0x401, 0x02f, "Kcell" }, + { 0x401, 0x07f, "Dalacom" }, + { 0x401, 0x77f, "Mobile Telecom Service" }, + { 0x639, -1, "Kenya" }, + { 0x639, 0x02f, "Safaricom" }, + { 0x639, 0x03f, "Zain" }, + { 0x639, 0x07f, "Orange Kenya" }, + { 0x545, -1, "Kiribati" }, + { 0x545, 0x09f, "Kiribati Frigate" }, + { 0x467, -1, "North Korea" }, + { 0x467, 0x193, "SUN NET" }, + { 0x450, -1, "South Korea" }, + { 0x450, 0x02f, "KT" }, + { 0x450, 0x03f, "Power 017" }, + { 0x450, 0x04f, "KT" }, + { 0x450, 0x05f, "SKT" }, + { 0x450, 0x06f, "LGT" }, + { 0x450, 0x08f, "KT SHOW" }, + { 0x212, -1, "Kosovo" }, + { 0x212, 0x01f, "Vala" }, + { 0x293, 0x41f, "iPKO" }, + { 0x293, 0x41f, "D3 Mobile" }, + { 0x212, 0x01f, "Z Mobile" }, + { 0x419, -1, "Kuwait" }, + { 0x419, 0x02f, "zain KW" }, + { 0x419, 0x03f, "Wataniya" }, + { 0x419, 0x04f, "Viva" }, + { 0x437, -1, "Kyrgyzstan" }, + { 0x437, 0x01f, "Beeline" }, + { 0x437, 0x05f, "MegaCom" }, + { 0x437, 0x09f, "O!" }, + { 0x457, -1, "Laos" }, + { 0x457, 0x01f, "LaoTel" }, + { 0x457, 0x02f, "ETL" }, + { 0x457, 0x03f, "Unitel" }, + { 0x457, 0x08f, "Tigo" }, + { 0x247, -1, "Latvia" }, + { 0x247, 0x01f, "LMT" }, + { 0x247, 0x02f, "Tele2" }, + { 0x247, 0x03f, "TRIATEL" }, + { 0x247, 0x05f, "Bite" }, + { 0x247, 0x06f, "Rigatta" }, + { 0x247, 0x07f, "MTS" }, + { 0x247, 0x08f, "IZZI" }, + { 0x247, 0x09f, "Camel Mobile" }, + { 0x415, -1, "Lebanon" }, + { 0x415, 0x01f, "Alfa" }, + { 0x415, 0x03f, "MTC-Touch" }, + { 0x651, -1, "Lesotho" }, + { 0x651, 0x01f, "Vodacom" }, + { 0x651, 0x02f, "Econet Ezin-cel" }, + { 0x618, -1, "Liberia" }, + { 0x618, 0x01f, "Lonestar Cell" }, + { 0x618, 0x04f, "Comium" }, + { 0x618, 0x20f, "LIBTELCO" }, + { 0x606, -1, "Libya" }, + { 0x606, 0x00f, "Libyana" }, + { 0x606, 0x01f, "Madar" }, + { 0x295, -1, "Liechtenstein" }, + { 0x295, 0x01f, "Swisscom" }, + { 0x295, 0x02f, "Orange" }, + { 0x295, 0x05f, "FL1" }, + { 0x295, 0x77f, "Tele 2" }, + { 0x246, -1, "Lithuania" }, + { 0x246, 0x01f, "Omnitel" }, + { 0x246, 0x02f, "BITE" }, + { 0x246, 0x03f, "Tele 2" }, + { 0x270, -1, "Luxembourg" }, + { 0x270, 0x01f, "LuxGSM" }, + { 0x270, 0x77f, "Tango" }, + { 0x270, 0x99f, "Orange" }, + { 0x455, -1, "Macau" }, + { 0x455, 0x00f, "SmarTone" }, + { 0x455, 0x01f, "CTM" }, + { 0x455, 0x02f, "China Telecom" }, + { 0x455, 0x03f, "3" }, + { 0x455, 0x04f, "CTM" }, + { 0x455, 0x05f, "3" }, + { 0x294, -1, "Republic of Macedonia" }, + { 0x294, 0x01f, "T-Mobile MK" }, + { 0x294, 0x02f, "ONE" }, + { 0x294, 0x03f, "Vip MK" }, + { 0x646, -1, "Madagascar" }, + { 0x646, 0x01f, "Zain" }, + { 0x646, 0x02f, "Orange" }, + { 0x646, 0x03f, "Sacel" }, + { 0x646, 0x04f, "Telma" }, + { 0x650, -1, "Malawi" }, + { 0x650, 0x01f, "TNM" }, + { 0x650, 0x10f, "Zain" }, + { 0x502, -1, "Malaysia" }, + { 0x502, 0x12f, "Maxis" }, + { 0x502, 0x13f, "Celcom" }, + { 0x502, 0x16f, "DiGi" }, + { 0x502, 0x17f, "Maxis" }, + { 0x502, 0x18f, "U Mobile" }, + { 0x502, 0x19f, "Celcom" }, + { 0x472, -1, "Maldives" }, + { 0x472, 0x01f, "Dhiraagu" }, + { 0x472, 0x02f, "Wataniya" }, + { 0x610, -1, "Mali" }, + { 0x610, 0x01f, "Malitel" }, + { 0x610, 0x02f, "Orange" }, + { 0x278, -1, "Malta" }, + { 0x278, 0x01f, "Vodafone" }, + { 0x278, 0x21f, "GO" }, + { 0x278, 0x77f, "Melita" }, + { 0x000, -1, "Marshall Islands" }, +// { 0x000, ?, "?" }, + { 0x340, -1, "Martinique" }, + { 0x340, 0x01f, "Orange" }, + { 0x340, 0x02f, "Outremer" }, + { 0x340, 0x20f, "Digicel" }, + { 0x609, -1, "Mauritania" }, + { 0x609, 0x01f, "Mattel" }, + { 0x609, 0x10f, "Mauritel" }, + { 0x617, -1, "Mauritius" }, + { 0x617, 0x01f, "Orange" }, + { 0x617, 0x02f, "MTML" }, + { 0x617, 0x10f, "Emtel" }, + { 0x334, -1, "Mexico" }, + { 0x334, 0x01f, "Nextel" }, + { 0x334, 0x02f, "Telcel" }, + { 0x334, 0x03f, "movistar" }, + { 0x334, 0x04f, "Iusacell / Unefon" }, + { 0x550, -1, "Federated States of Micronesia" }, + { 0x550, 0x01f, "FSM Telecom" }, + { 0x259, -1, "Moldova" }, + { 0x259, 0x01f, "Orange" }, + { 0x259, 0x02f, "Moldcell" }, + { 0x259, 0x03f, "IDC" }, + { 0x259, 0x03f, "Unit?" }, + { 0x259, 0x04f, "Eventis" }, + { 0x259, 0x05f, "Unit?" }, + { 0x212, -1, "Monaco" }, + { 0x212, 0x01f, "Office des Telephones" }, + { 0x428, -1, "Mongolia" }, + { 0x428, 0x99f, "MobiCom" }, + { 0x428, 0x88f, "Unitel" }, + { 0x428, 0x91f, "Skytel" }, + { 0x428, 0x98f, "G.Mobile" }, + { 0x297, -1, "Montenegro" }, + { 0x297, 0x01f, "Telenor" }, + { 0x297, 0x02f, "T-Mobile" }, + { 0x297, 0x03f, "m:tel CG" }, + { 0x604, -1, "Morocco" }, + { 0x604, 0x00f, "Moditel" }, + { 0x604, 0x01f, "IAM" }, + { 0x604, 0x02f, "INWI" }, + { 0x605, 0x03f, "yassine" }, + { 0x643, -1, "Mozambique" }, + { 0x643, 0x01f, "mCel" }, + { 0x643, 0x04f, "Vodacom" }, + { 0x414, -1, "Myanmar" }, + { 0x414, 0x01f, "MPT" }, + { 0x649, -1, "Namibia" }, + { 0x649, 0x01f, "MTC" }, + { 0x649, 0x02f, "switch" }, + { 0x649, 0x03f, "Leo" }, + { 0x536, -1, "Nauru" }, + { 0x429, -1, "Nepal" }, + { 0x429, 0x01f, "Namaste / NT Mobile" }, + { 0x429, 0x02f, "Ncell" }, + { 0x429, 0x03f, "Sky/C-Phone" }, + { 0x204, -1, "Netherlands" }, + { 0x204, 0x01f, "OneFoon" }, + { 0x204, 0x02f, "Tele2" }, + { 0x204, 0x03f, "Blyk" }, + { 0x204, 0x04f, "Vodafone" }, + { 0x204, 0x05f, "Elephant Talk" }, + { 0x204, 0x06f, "Barablu Mobile" }, + { 0x204, 0x07f, "Teleena" }, + { 0x204, 0x08f, "KPN" }, + { 0x204, 0x09f, "Lycamobile" }, + { 0x204, 0x10f, "KPN" }, + { 0x204, 0x12f, "Telfort" }, + { 0x204, 0x14f, "6Gmobile" }, + { 0x204, 0x16f, "T-Mobile" }, + { 0x204, 0x18f, "Telfort" }, + { 0x204, 0x20f, "Orange Nederland" }, + { 0x204, 0x21f, "NS Railinfrabeheer B.V." }, + { 0x204, 0x67f, "RadioAccess" }, + { 0x204, 0x69f, "KPN Mobile" }, + { 0x362, -1, "Netherlands Antilles" }, + { 0x362, 0x51f, "Telcell" }, + { 0x362, 0x69f, "Digicel" }, + { 0x362, 0x91f, "UTS" }, + { 0x362, 0x00f, "East Caribbean Cellular" }, + { 0x362, 0x00f, "Antiliano Por N.V." }, + { 0x362, 0x95f, "MIO" }, + { 0x362, 0x94f, "Bay?s" }, + { 0x546, -1, "New Caledonia" }, + { 0x546, 0x01f, "Mobilis" }, + { 0x530, -1, "New Zealand" }, + { 0x530, 0x00f, "Telecom" }, + { 0x530, 0x01f, "Vodafone" }, + { 0x530, 0x02f, "Telecom" }, + { 0x530, 0x03f, "Woosh" }, + { 0x530, 0x04f, "TelstraClear" }, + { 0x530, 0x05f, "XT Mobile Network" }, + { 0x530, 0x12f, "360" }, + { 0x530, 0x24f, "2degrees" }, + { 0x710, -1, "Nicaragua" }, + { 0x710, 0x21f, "Claro" }, + { 0x710, 0x30f, "movistar" }, + { 0x710, 0x73f, "SERCOM" }, + { 0x614, -1, "Niger" }, + { 0x614, 0x01f, "SahelCom" }, + { 0x614, 0x02f, "Zain" }, + { 0x614, 0x03f, "Telecel" }, + { 0x614, 0x04f, "Orange" }, + { 0x621, -1, "Nigeria" }, + { 0x621, 0x20f, "Zain" }, + { 0x621, 0x30f, "MTN" }, + { 0x621, 0x40f, "M-Tel" }, + { 0x621, 0x50f, "Glo" }, + { 0x621, 0x60f, "Etisalat" }, + { 0x242, -1, "Norway" }, + { 0x242, 0x01f, "Telenor" }, + { 0x242, 0x02f, "NetCom" }, + { 0x242, 0x03f, "Teletopia" }, + { 0x242, 0x04f, "Tele2" }, + { 0x242, 0x05f, "Network Norway" }, + { 0x242, 0x06f, "Ice" }, + { 0x242, 0x07f, "Ventelo" }, + { 0x242, 0x08f, "TDC Mobil AS" }, + { 0x242, 0x09f, "Barablu Mobile Norway Ltd" }, + { 0x242, 0x20f, "Jernbaneverket AS" }, + { 0x422, -1, "Oman" }, + { 0x422, 0x02f, "Oman Mobile" }, + { 0x422, 0x03f, "Nawras" }, + { 0x410, -1, "Pakistan" }, + { 0x410, 0x01f, "Mobilink" }, + { 0x410, 0x03f, "Ufone" }, + { 0x410, 0x04f, "Zong" }, + { 0x410, 0x06f, "Telenor" }, + { 0x410, 0x07f, "Warid" }, + { 0x552, -1, "Palau" }, + { 0x552, 0x01f, "PNCC" }, + { 0x552, 0x80f, "Palau Mobile" }, + { 0x423, -1, "Palestinian Authority" }, + { 0x423, 0x05f, "Jawwal" }, + { 0x423, 0x06f, "Wataniya" }, + { 0x714, -1, "Panama" }, + { 0x714, 0x01f, "Cable & Wireless" }, + { 0x714, 0x02f, "movistar" }, + { 0x714, 0x04f, "Digicel" }, + { 0x714, 0x03f, "Claro" }, + { 0x537, -1, "Papua New Guinea" }, + { 0x537, 0x01f, "B-Mobile" }, + { 0x537, 0x03f, "Digicel" }, + { 0x744, -1, "Paraguay" }, + { 0x744, 0x01f, "VOX" }, + { 0x744, 0x02f, "Claro" }, + { 0x744, 0x04f, "Tigo" }, + { 0x744, 0x05f, "Personal" }, + { 0x716, -1, "Peru" }, + { 0x716, 0x06f, "movistar" }, + { 0x716, 0x10f, "Claro" }, + { 0x716, 0x17f, "NEXTEL" }, + { 0x515, -1, "Philippines" }, + { 0x515, 0x01f, "Islacom" }, + { 0x515, 0x02f, "Globe" }, + { 0x515, 0x03f, "Smart" }, + { 0x515, 0x05f, "Sun" }, + { 0x515, 0x11f, "PLDT via ACeS Philippines" }, + { 0x515, 0x18f, "Cure" }, + { 0x515, 0x88f, "Nextel" }, + { 0x260, -1, "Poland" }, + { 0x260, 0x01f, "Plus" }, + { 0x260, 0x02f, "Era" }, + { 0x260, 0x03f, "Orange" }, + { 0x260, 0x04f, "Netia S.A." }, + { 0x260, 0x05f, "Polska Telefonia Kom?rkowa Centertel Sp. z o.o." }, + { 0x260, 0x06f, "Play" }, + { 0x260, 0x07f, "Netia" }, + { 0x260, 0x08f, "E-Telko Sp. z o.o." }, + { 0x260, 0x09f, "Telekomunikacja Kolejowa Sp. z o.o." }, + { 0x260, 0x10f, "Sferia" }, + { 0x260, 0x11f, "Nordisk Polska" }, + { 0x260, 0x12f, "Cyfrowy Polsat" }, + { 0x260, 0x13f, "Sferia" }, + { 0x260, 0x14f, "Sferia" }, + { 0x260, 0x15f, "CenterNet" }, + { 0x260, 0x16f, "Mobyland" }, + { 0x260, 0x17f, "Aero2" }, + { 0x268, -1, "Portugal" }, + { 0x268, 0x01f, "Vodafone" }, + { 0x268, 0x03f, "Optimus" }, + { 0x268, 0x06f, "TMN" }, + { 0x268, 0x21f, "Zapp" }, + { 0x330, -1, "Puerto Rico" }, + { 0x330, 0x11f, "Claro" }, + { 0x427, -1, "Qatar" }, + { 0x427, 0x01f, "Qatarnet" }, + { 0x427, 0x02f, "Vodafone Qatar" }, + { 0x647, -1, "R&?union" }, + { 0x647, 0x00f, "Orange" }, + { 0x647, 0x02f, "Outremer" }, + { 0x647, 0x10f, "SFR Reunion" }, + { 0x226, -1, "Romania" }, + { 0x226, 0x01f, "Vodafone" }, + { 0x226, 0x02f, "Romtelecom" }, + { 0x226, 0x03f, "Cosmote" }, + { 0x226, 0x04f, "Cosmote" }, + { 0x226, 0x05f, "Digi.Mobil" }, + { 0x226, 0x06f, "Cosmote" }, + { 0x226, 0x10f, "Orange" }, + { 0x250, -1, "Russian Federation" }, + { 0x250, 0x01f, "MTS" }, + { 0x250, 0x02f, "MegaFon" }, + { 0x250, 0x03f, "NCC" }, + { 0x250, 0x04f, "Sibchallenge" }, + { 0x250, 0x05f, "ETK" }, + { 0x250, 0x06f, "Skylink" }, + { 0x250, 0x07f, "SMARTS" }, + { 0x250, 0x09f, "Skylink" }, + { 0x250, 0x10f, "DTC" }, + { 0x250, 0x11f, "Orensot" }, + { 0x250, 0x12f, "Baykalwestcom" }, + { 0x250, 0x12f, "Akos" }, + { 0x250, 0x13f, "KUGSM" }, + { 0x250, 0x15f, "SMARTS" }, + { 0x250, 0x16f, "NTC" }, + { 0x250, 0x17f, "Utel" }, + { 0x250, 0x19f, "INDIGO" }, + { 0x250, 0x20f, "Tele2" }, + { 0x250, 0x23f, "Mobicom - Novosibirsk" }, + { 0x250, 0x28f, "Beeline" }, + { 0x250, 0x35f, "MOTIV" }, + { 0x250, 0x38f, "Tambov GSM" }, + { 0x250, 0x39f, "Utel" }, + { 0x250, 0x44f, "Stavtelesot / North Caucasian GSM" }, + { 0x250, 0x92f, "Primtelefon" }, + { 0x250, 0x93f, "Telecom XXI" }, + { 0x250, 0x99f, "Beeline" }, +// { 0x250, ?, "SkyLink/MTS/the Moscow Cellular communication" }, + { 0x635, -1, "Rwanda" }, + { 0x635, 0x10f, "MTN" }, + { 0x635, 0x13f, "Tigo" }, + { 0x356, -1, "Saint Kitts and Nevis" }, + { 0x356, 0x050, "Digicel" }, + { 0x356, 0x110, "Cable & Wireless" }, + { 0x358, -1, "Saint Lucia" }, + { 0x358, 0x050, "Digicel" }, + { 0x358, 0x110, "Cable & Wireless" }, + { 0x308, -1, "Saint Pierre and Miquelon" }, + { 0x308, 0x01f, "Ameris" }, + { 0x360, -1, "Saint Vincent and the Grenadines" }, + { 0x360, 0x070, "Digicel" }, + { 0x360, 0x100, "Cingular Wireless" }, + { 0x360, 0x110, "Cable & Wireless" }, + { 0x549, -1, "Samoa" }, + { 0x549, 0x01f, "Digicel" }, + { 0x549, 0x27f, "SamoaTel" }, + { 0x292, -1, "San Marino" }, + { 0x292, 0x01f, "PRIMA" }, + { 0x626, -1, "Sao Tome and Principe" }, + { 0x626, 0x01f, "CSTmovel" }, + { 0x420, -1, "Saudi Arabia" }, + { 0x420, 0x01f, "Al Jawal" }, + { 0x420, 0x03f, "Mobily" }, + { 0x420, 0x07f, "EAE" }, + { 0x420, 0x04f, "Zain SA" }, + { 0x608, -1, "Senegal" }, + { 0x608, 0x01f, "Orange (telecommunications)" }, + { 0x608, 0x02f, "Tigo" }, + { 0x608, 0x03f, "Expresso" }, + { 0x220, -1, "Serbia" }, + { 0x220, 0x01f, "Telenor" }, + { 0x220, 0x03f, "mt:s" }, + { 0x220, 0x05f, "VIP" }, + { 0x633, -1, "Seychelles" }, + { 0x633, 0x01f, "Cable & Wireless" }, + { 0x633, 0x02f, "Mediatech International" }, + { 0x633, 0x10f, "Airtel" }, + { 0x619, -1, "Sierra Leone" }, + { 0x619, 0x01f, "Zain" }, + { 0x619, 0x02f, "Millicom" }, + { 0x619, 0x03f, "Datatel" }, + { 0x619, 0x04f, "Comium" }, + { 0x619, 0x05f, "Africell" }, + { 0x619, 0x25f, "Mobitel" }, +// { 0x619, ?, "LeoneCel" }, + { 0x525, -1, "Singapore" }, + { 0x525, 0x01f, "SingTel" }, + { 0x525, 0x02f, "SingTel-G18" }, + { 0x525, 0x03f, "M1" }, + { 0x525, 0x05f, "StarHub" }, + { 0x525, 0x12f, "Digital Trunked Radio Network" }, + { 0x231, -1, "Slovakia" }, + { 0x231, 0x01f, "Orange" }, + { 0x231, 0x02f, "T-Mobile" }, + { 0x231, 0x03f, "Unient Communications" }, + { 0x231, 0x04f, "T-Mobile" }, + { 0x231, 0x05f, "Mobile Entertainment Company" }, + { 0x231, 0x06f, "O2" }, + { 0x231, 0x99f, "?SR" }, + { 0x293, -1, "Slovenia" }, + { 0x293, 0x40f, "Si.mobil" }, + { 0x293, 0x41f, "Mobitel" }, + { 0x293, 0x64f, "T-2" }, + { 0x293, 0x70f, "Tu?mobil" }, + { 0x540, -1, "Solomon Islands" }, + { 0x637, -1, "Somalia" }, + { 0x637, 0x01f, "Telesom" }, + { 0x637, 0x04f, "Somafone" }, + { 0x637, 0x10f, "Nationlink" }, + { 0x637, 0x25f, "Hormuud" }, + { 0x637, 0x30f, "Golis" }, + { 0x637, 0x82f, "Telcom" }, + { 0x655, -1, "South Africa" }, + { 0x655, 0x01f, "Vodacom" }, + { 0x655, 0x06f, "Sentech" }, + { 0x655, 0x07f, "Cell C" }, + { 0x655, 0x10f, "MTN" }, + { 0x655, 0x11f, "SAPS Gauteng" }, + { 0x655, 0x13f, "Neotel" }, + { 0x655, 0x21f, "Cape Town Metropolitan Council" }, + { 0x655, 0x30f, "Bokamoso Consortium" }, + { 0x655, 0x31f, "Karabo Telecoms (Pty) Ltd." }, + { 0x655, 0x32f, "Ilizwi Telecommunications" }, + { 0x655, 0x33f, "Thinta Thinta Telecommunications" }, + { 0x655, 0x02f, "Telkom" }, + { 0x214, -1, "Spain" }, + { 0x214, 0x01f, "Vodafone" }, + { 0x214, 0x03f, "Orange" }, + { 0x214, 0x04f, "Yoigo" }, + { 0x214, 0x05f, "TME" }, + { 0x214, 0x06f, "Vodafone" }, + { 0x214, 0x07f, "movistar" }, + { 0x214, 0x08f, "Euskaltel" }, + { 0x214, 0x09f, "Orange" }, + { 0x214, 0x15f, "BT" }, + { 0x214, 0x16f, "TeleCable" }, + { 0x214, 0x17f, "M?bil R" }, + { 0x214, 0x18f, "ONO" }, + { 0x214, 0x19f, "Simyo" }, + { 0x214, 0x21f, "Jazztel" }, + { 0x214, 0x22f, "DigiMobil" }, + { 0x214, 0x23f, "Barablu" }, + { 0x413, -1, "Sri Lanka" }, + { 0x413, 0x01f, "Mobitel" }, + { 0x413, 0x02f, "Dialog" }, + { 0x413, 0x03f, "Etisalat" }, + { 0x413, 0x05f, "Airtel" }, + { 0x413, 0x08f, "Hutch" }, + { 0x413, 0x00f, "RTEC Mobile" }, + { 0x634, -1, "Sudan" }, + { 0x634, 0x01f, "Zain SD" }, + { 0x634, 0x02f, "MTN" }, + { 0x634, 0x05f, "Vivacell" }, + { 0x746, -1, "Suriname" }, + { 0x746, 0x05f, "Telesur" }, + { 0x653, -1, "Swaziland" }, + { 0x653, 0x10f, "Swazi MTN" }, + { 0x240, -1, "Sweden" }, + { 0x240, 0x01f, "Telia" }, + { 0x240, 0x02f, "3" }, + { 0x240, 0x03f, "Ice.net" }, + { 0x240, 0x04f, "3G Infrastructure Services" }, + { 0x240, 0x05f, "Sweden 3G" }, + { 0x240, 0x06f, "Telenor" }, + { 0x240, 0x07f, "Tele2" }, + { 0x240, 0x08f, "Telenor" }, + { 0x240, 0x09f, "djuice" }, + { 0x240, 0x10f, "Spring Mobil" }, + { 0x240, 0x11f, "Lindholmen Science Park" }, + { 0x240, 0x12f, "Barablu Mobile Scandinavia" }, + { 0x240, 0x13f, "Ventelo Sverige" }, + { 0x240, 0x14f, "TDC Mobil" }, + { 0x240, 0x15f, "Wireless Maingate Nordic" }, + { 0x240, 0x16f, "42IT" }, + { 0x240, 0x17f, "G?talandsn?tet" }, + { 0x240, 0x20f, "Wireless Maingate Message Services" }, + { 0x240, 0x21f, "MobiSir" }, + { 0x240, 0x25f, "DigiTelMobile" }, + { 0x228, -1, "Switzerland" }, + { 0x228, 0x01f, "Swisscom" }, + { 0x228, 0x02f, "Sunrise" }, + { 0x228, 0x03f, "Orange" }, + { 0x228, 0x05f, "Togewanet AG (Comfone)" }, + { 0x228, 0x06f, "SBB AG" }, + { 0x228, 0x07f, "IN&Phone" }, + { 0x228, 0x08f, "Tele2" }, + { 0x228, 0x50f, "3G Mobile AG" }, + { 0x228, 0x51f, "BebbiCell AG" }, + { 0x417, -1, "Syria" }, + { 0x417, 0x01f, "Syriatel" }, + { 0x417, 0x02f, "MTN" }, + { 0x466, -1, "Taiwan" }, + { 0x466, 0x01f, "FarEasTone" }, + { 0x466, 0x02f, "APTG" }, + { 0x466, 0x06f, "Tuntex" }, + { 0x466, 0x11f, "Chunghwa LDM" }, + { 0x466, 0x88f, "KG Telecom" }, + { 0x466, 0x89f, "VIBO" }, + { 0x466, 0x92f, "Chungwa" }, + { 0x466, 0x93f, "MobiTai" }, + { 0x466, 0x97f, "Taiwan Mobile" }, + { 0x466, 0x99f, "TransAsia" }, + { 0x436, -1, "Tajikistan" }, + { 0x436, 0x01f, "Tcell" }, + { 0x436, 0x02f, "Indigo" }, + { 0x436, 0x03f, "MLT" }, + { 0x436, 0x04f, "Babilon-M" }, + { 0x436, 0x05f, "Beeline" }, + { 0x640, -1, "Tanzania" }, + { 0x640, 0x06f, "SasaTel" }, + { 0x640, 0x02f, "tiGO" }, + { 0x640, 0x03f, "Zantel" }, + { 0x640, 0x04f, "Vodacom" }, + { 0x640, 0x05f, "Zain" }, + { 0x520, -1, "Thailand" }, + { 0x520, 0x00f, "Hutch" }, + { 0x520, 0x01f, "AIS" }, + { 0x520, 0x02f, "CAT CDMA" }, + { 0x520, 0x10f, "?" }, + { 0x520, 0x15f, "Thai Mobile" }, + { 0x520, 0x15f, "TOT 3G" }, + { 0x520, 0x18f, "dtac" }, + { 0x520, 0x23f, "AIS GSM 1800" }, + { 0x520, 0x99f, "True Move" }, + { 0x520, 0x00f, "WE PCT" }, + { 0x615, -1, "Togo" }, + { 0x615, 0x01f, "Togo Cell" }, + { 0x615, 0x03f, "Moov" }, + { 0x539, -1, "Tonga" }, + { 0x539, 0x01f, "Tonga Communications Corporation" }, + { 0x539, 0x43f, "Shoreline Communication" }, + { 0x539, 0x88f, "Digicel" }, + { 0x374, -1, "Trinidad and Tobago" }, + { 0x374, 0x12f, "bmobile" }, + { 0x374, 0x13f, "Digicel" }, + { 0x605, -1, "Tunisia" }, + { 0x605, 0x01f, "Orange" }, + { 0x605, 0x02f, "Tunicell" }, + { 0x605, 0x03f, "Tunisiana" }, + { 0x286, -1, "Turkey" }, + { 0x286, 0x01f, "Turkcell" }, + { 0x286, 0x02f, "Vodafone" }, + { 0x286, 0x03f, "Avea" }, + { 0x286, 0x04f, "Aycell" }, + { 0x438, -1, "Turkmenistan" }, + { 0x438, 0x01f, "MTS" }, + { 0x438, 0x02f, "TM-Cell" }, + { 0x376, -1, "Turks and Caicos Islands" }, + { 0x376, 0x350, "C&W" }, + { 0x376, 0x352, "Islandcom" }, + { 0x338, 0x05f, "Digicel" }, + { 0x553, -1, "Tuvalu" }, + { 0x553, 0x01f, "TTC" }, + { 0x641, -1, "Uganda" }, + { 0x641, 0x01f, "Zain" }, + { 0x641, 0x10f, "MTN" }, + { 0x641, 0x11f, "Uganda Telecom Ltd." }, + { 0x641, 0x22f, "Warid Telecom" }, + { 0x641, 0x14f, "Orange" }, + { 0x255, -1, "Ukraine" }, + { 0x255, 0x01f, "MTS" }, + { 0x255, 0x02f, "Beeline" }, + { 0x255, 0x03f, "Kyivstar" }, + { 0x255, 0x04f, "IT" }, + { 0x255, 0x05f, "Golden Telecom" }, + { 0x255, 0x06f, "life:)" }, + { 0x255, 0x07f, "Ukrtelecom" }, + { 0x255, 0x21f, "PEOPLEnet" }, + { 0x255, 0x23f, "CDMA Ukraine" }, + { 0x424, -1, "United Arab Emirates" }, + { 0x424, 0x02f, "Etisalat" }, + { 0x424, 0x03f, "du" }, + { 0x234, -1, "United Kingdom" }, + { 0x234, 0x00f, "BT" }, + { 0x234, 0x01f, "UK01" }, + { 0x234, 0x02f, "O2" }, + { 0x234, 0x03f, "Airtel-Vodafone" }, + { 0x234, 0x04f, "FMS Solutions Ltd" }, + { 0x234, 0x07f, "Cable and Wireless UK" }, + { 0x234, 0x08f, "OnePhone Ltd" }, + { 0x234, 0x10f, "O2" }, + { 0x234, 0x11f, "O2" }, + { 0x234, 0x12f, "Railtrack" }, + { 0x234, 0x14f, "Hay Systems Ltd" }, + { 0x234, 0x15f, "Vodafone" }, + { 0x234, 0x16f, "Opal Telecom Ltd" }, + { 0x234, 0x18f, "Cloud9" }, + { 0x234, 0x19f, "Teleware" }, + { 0x234, 0x20f, "3" }, + { 0x234, 0x22f, "RoutoMessaging" }, + { 0x234, 0x25f, "Truphone" }, + { 0x234, 0x30f, "T-Mobile" }, + { 0x234, 0x31f, "Virgin" }, + { 0x234, 0x32f, "Virgin" }, + { 0x234, 0x33f, "Orange" }, + { 0x234, 0x34f, "Orange" }, + { 0x234, 0x50f, "JT-Wave" }, + { 0x234, 0x55f, "Cable & Wireless Guernsey / Sure Mobile (Jersey)" }, + { 0x234, 0x58f, "Manx Telecom" }, + { 0x234, 0x75f, "Inquam" }, + { 0x234, 0x77f, "BT" }, + { 0x200, -1, "United States of America" }, + { 0x200, 0x053, "Virgin Mobile US" }, + { 0x200, 0x054, "Alltel US" }, + { 0x200, 0x066, "U.S. Cellular" }, + /* 0x310 taken from Annex to ITU Operational Bulletin No. 958 – 15.VI.2010 */ + { 0x310, 0x00f, "nTelos" }, + { 0x310, 0x000, "Mid-Tex Cellular" }, + { 0x310, 0x004, "Verizon" }, + { 0x310, 0x010, "MCI" }, + { 0x310, 0x012, "Verizon" }, + { 0x310, 0x013, "MobileTel" }, + { 0x310, 0x014, "Testing" }, + { 0x310, 0x016, "Cricket Communications" }, + { 0x310, 0x017, "North Sight Communications Inc." }, + { 0x310, 0x020, "Union Telephone Company" }, + { 0x310, 0x026, "T-Mobile" }, + { 0x310, 0x030, "Centennial" }, + { 0x310, 0x034, "Airpeak" }, + { 0x310, 0x038, "AT&T" }, + { 0x310, 0x040, "Concho" }, + { 0x310, 0x046, "SIMMETRY" }, + { 0x310, 0x060, "Consolidated Telcom" }, + { 0x310, 0x070, "Highland Cellular" }, + { 0x310, 0x080, "Corr" }, + { 0x310, 0x090, "AT&T" }, + { 0x310, 0x100, "Plateau Wireless" }, + { 0x310, 0x110, "PTI Pacifica" }, + { 0x310, 0x120, "Sprint" }, + { 0x310, 0x150, "AT&T" }, + { 0x310, 0x160, "T-Mobile" }, + { 0x310, 0x170, "T-Mobile" }, + { 0x310, 0x180, "West Central" }, + { 0x310, 0x190, "Dutch Harbor" }, + { 0x310, 0x200, "T-Mobile" }, + { 0x310, 0x210, "T-Mobile" }, + { 0x310, 0x220, "T-Mobile" }, + { 0x310, 0x230, "T-Mobile" }, + { 0x310, 0x240, "T-Mobile" }, + { 0x310, 0x250, "T-Mobile" }, + { 0x310, 0x260, "T-Mobile" }, + { 0x310, 0x270, "T-Mobile" }, + { 0x310, 0x280, "T-Mobile" }, + { 0x310, 0x290, "T-Mobile" }, + { 0x310, 0x300, "iSmart Mobile" }, + { 0x310, 0x310, "T-Mobile" }, + { 0x310, 0x311, "Farmers Wireless" }, + { 0x310, 0x320, "Cellular One" }, + { 0x310, 0x330, "T-Mobile" }, + { 0x310, 0x340, "Westlink" }, + { 0x310, 0x350, "Carolina Phone" }, + { 0x310, 0x380, "AT&T Mobility" }, + { 0x310, 0x390, "Cellular One of East Texas" }, + { 0x310, 0x400, "i CAN_GSM" }, + { 0x310, 0x410, "AT&T" }, + { 0x310, 0x420, "Cincinnati Bell" }, + { 0x310, 0x430, "Alaska Digitel" }, + { 0x310, 0x440, "Cellular One" }, + { 0x310, 0x450, "Viaero" }, + { 0x310, 0x460, "Simmetry" }, + { 0x310, 0x480, "Choice Phone" }, + { 0x310, 0x490, "T-Mobile" }, + { 0x310, 0x500, "Alltel" }, + { 0x310, 0x510, "Airtel" }, + { 0x310, 0x520, "VeriSign" }, + { 0x310, 0x530, "West Virginia Wireless" }, + { 0x310, 0x540, "Oklahoma Western" }, + { 0x310, 0x560, "AT&T" }, + { 0x310, 0x570, "Cellular One" }, + { 0x310, 0x580, "T-Mobile" }, + { 0x310, 0x590, "Alltel" }, + { 0x310, 0x610, "Epic Touch" }, + { 0x310, 0x620, "Coleman County Telecom" }, + { 0x310, 0x630, "AmeriLink PCS" }, + { 0x310, 0x640, "Airadigm" }, + { 0x310, 0x650, "Jasper" }, + { 0x310, 0x660, "T-Mobile" }, + { 0x310, 0x670, "Northstar" }, + { 0x310, 0x680, "AT&T" }, + { 0x310, 0x690, "Conestoga" }, + { 0x310, 0x730, "SeaMobile" }, + { 0x310, 0x740, "Convey" }, + { 0x310, 0x760, "Panhandle" }, + { 0x310, 0x770, "i wireless" }, + { 0x310, 0x780, "Airlink PCS" }, + { 0x310, 0x790, "PinPoint" }, + { 0x310, 0x800, "T-Mobile" }, + { 0x310, 0x830, "Caprock" }, + { 0x310, 0x850, "Aeris" }, + { 0x310, 0x870, "PACE" }, + { 0x310, 0x880, "Advantage" }, + { 0x310, 0x890, "Unicel" }, + { 0x310, 0x900, "Mid-Rivers Wireless" }, + { 0x310, 0x910, "First Cellular" }, + { 0x310, 0x940, "Iris Wireless LLC" }, + { 0x310, 0x950, "XIT Wireless" }, + { 0x310, 0x960, "Plateau Wireless" }, + { 0x310, 0x970, "Globalstar" }, + { 0x310, 0x980, "AT&T Mobility" }, + { 0x310, 0x990, "AT&T Mobility" }, + { 0x311, 0x000, "Mid-Tex Cellular" }, + { 0x311, 0x010, "Chariton Valley" }, + { 0x311, 0x020, "Missouri RSA 5 Partnership" }, + { 0x311, 0x030, "Indigo Wireless" }, + { 0x311, 0x040, "Commnet Wireless" }, + { 0x311, 0x050, "Wikes Cellular" }, + { 0x311, 0x060, "Farmers Cellular" }, + { 0x311, 0x070, "Easterbrooke" }, + { 0x311, 0x080, "Pine Cellular" }, + { 0x311, 0x090, "Long Lines Wireless" }, + { 0x311, 0x100, "High Plains Wireless" }, + { 0x311, 0x110, "High Plains Wireless" }, + { 0x311, 0x120, "Choice Phone" }, + { 0x311, 0x130, "Cell One Amarillo" }, + { 0x311, 0x140, "Sprocket" }, + { 0x311, 0x150, "Wilkes Cellular" }, + { 0x311, 0x160, "Endless Mountains Wireless" }, + { 0x311, 0x170, "PetroCom" }, + { 0x311, 0x180, "Cingular Wireless" }, + { 0x311, 0x190, "Cellular Properties" }, + { 0x311, 0x210, "Farmers Cellular" }, + { 0x316, 0x010, "Nextel" }, + { 0x316, 0x011, "Southern Communications Services" }, + { 0x748, -1, "Uruguay" }, + { 0x748, 0x00f, "Ancel" }, + { 0x748, 0x01f, "Ancel" }, + { 0x748, 0x07f, "Movistar" }, + { 0x748, 0x10f, "Claro" }, + { 0x434, -1, "Uzbekistan" }, + { 0x434, 0x01f, "Buztel" }, + { 0x434, 0x02f, "Uzmacom" }, + { 0x434, 0x04f, "Beeline" }, + { 0x434, 0x05f, "Ucell" }, + { 0x434, 0x06f, "Perfectum Mobile" }, + { 0x434, 0x07f, "MTS" }, + { 0x541, -1, "Vanuatu" }, + { 0x541, 0x01f, "SMILE" }, + { 0x225, -1, "Vatican" }, + { 0x734, -1, "Venezuela" }, + { 0x734, 0x01f, "Digitel" }, + { 0x734, 0x02f, "Digitel" }, + { 0x734, 0x03f, "Digitel" }, + { 0x734, 0x04f, "movistar" }, + { 0x734, 0x06f, "Movilnet" }, + { 0x452, -1, "Vietnam" }, + { 0x452, 0x01f, "MobiFone" }, + { 0x452, 0x02f, "Vinaphone" }, + { 0x452, 0x03f, "S-Fone" }, + { 0x452, 0x04f, "Viettel Mobile" }, + { 0x452, 0x05f, "Vietnamobile" }, + { 0x452, 0x06f, "E-Mobile" }, + { 0x452, 0x07f, "Beeline VN" }, + { 0x421, -1, "Yemen" }, + { 0x421, 0x01f, "SabaFon" }, + { 0x421, 0x02f, "MTN" }, + { 0x421, 0x03f, "Yemen Mobile" }, + { 0x421, 0x04f, "HiTS-UNITEL" }, + { 0x645, -1, "Zambia" }, + { 0x645, 0x01f, "Zain" }, + { 0x645, 0x02f, "MTN" }, + { 0x645, 0x03f, "ZAMTEL" }, + { 0x648, -1, "Zimbabwe" }, + { 0x648, 0x01f, "Net*One" }, + { 0x648, 0x03f, "Telecel" }, + { 0x648, 0x04f, "Econet" }, + { 0x901, -1, "International" }, + { 0x901, 0x01f, "ICO" }, + { 0x901, 0x02f, "Sense Communications International" }, + { 0x901, 0x03f, "Iridium" }, + { 0x901, 0x04f, "Globalstar" }, + { 0x901, 0x05f, "Thuraya RMSS Network" }, + { 0x901, 0x06f, "Thuraya Satellite Telecommunications Company" }, + { 0x901, 0x07f, "Ellipso" }, + { 0x901, 0x08f, "" }, + { 0x901, 0x09f, "Tele1 Europe" }, + { 0x901, 0x10f, "ACeS" }, + { 0x901, 0x11f, "Inmarsat" }, + { 0x901, 0x12f, "MCP" }, + { 0x901, 0x13f, "GSM.AQ" }, + { 0x901, 0x14f, "AeroMobile AS" }, + { 0x901, 0x15f, "OnAir Switzerland Sarl" }, + { 0x901, 0x16f, "Jasper Systems" }, + { 0x901, 0x17f, "Navitas" }, + { 0x901, 0x18f, "Cellular @Sea" }, + { 0x901, 0x19f, "Vodafone Malta Maritime" }, + { 0x901, 0x21f, "Seanet" }, + { 0x901, 0x24f, "iNum" }, + { 0x901, 0x29f, "Telenor" }, + { 0, 0, NULL } +}; + +/* GSM 03.22 Annex A */ +int gsm_match_mcc(uint16_t mcc, char *imsi) +{ + uint16_t sim_mcc; + + sim_mcc = ((imsi[0] - '0') << 8) + + ((imsi[1] - '0') << 4) + + imsi[2] - '0'; + + return (mcc == sim_mcc); +} + +/* GSM 03.22 Annex A */ +int gsm_match_mnc(uint16_t mcc, uint8_t mnc, char *imsi) +{ + uint16_t sim_mnc; + + /* 1. SIM-MCC = BCCH-MCC */ + if (!gsm_match_mcc(mcc, imsi)) + return 0; + + /* 2. 3rd digit of BCCH-MNC is not 0xf */ + if ((mnc & 0x00f) != 0x00f) { + /* 3. 3 digit SIM-MNC = BCCH-MNC */ + sim_mnc = ((imsi[3] - '0') << 8) + + ((imsi[4] - '0') << 4) + + imsi[5] - '0'; + + return (mnc == sim_mnc); + } + + /* 4. BCCH-MCC in the range 310-316 */ + if (mcc >= 310 && mcc <= 316) { + /* 5. 3rd diit of SIM-MNC is 0 */ + if (imsi[5] != 0) + return 0; + } + + /* 6. 1st 2 digits of SIM-MNC and BCCH-MNC match */ + sim_mnc = ((imsi[3] - '0') << 8) + + ((imsi[4] - '0') << 4) + + 0x00f; + + return (mnc == sim_mnc); +} + +const char *gsm_print_mcc(uint16_t mcc) +{ + static char string[5] = "000"; + + snprintf(string, 4, "%03x", mcc); + return string; +} + +const char *gsm_print_mnc(uint16_t mnc) +{ + static char string[7]; + + /* invalid format: return hex value */ + if ((mnc & 0xf000) + || (mnc & 0x0f00) > 0x0900 + || (mnc & 0x00f0) > 0x0090 + || ((mnc & 0x000f) > 0x0009 && (mnc & 0x000f) < 0x000f)) { + snprintf(string, 6, "0x%03x", mnc); + return string; + } + + /* two digits */ + if ((mnc & 0x000f) == 0x000f) { + snprintf(string, 6, "%02x", mnc >> 4); + return string; + } + + /* three digits */ + snprintf(string, 6, "%03x", mnc); + return string; +} + +const uint16_t gsm_input_mcc(char *string) +{ + uint16_t mcc; + + if (strlen(string) != 3) + return 0; + if (string[0] < '0' || string [0] > '9' + || string[1] < '0' || string [1] > '9' + || string[2] < '0' || string [2] > '9') + return 0; + + mcc = ((string[0] - '0') << 8) + | ((string[1] - '0') << 4) + | ((string[2] - '0')); + + return mcc; +} + +const uint16_t gsm_input_mnc(char *string) +{ + uint16_t mnc = 0; + + if (strlen(string) == 2) { + if (string[0] < '0' || string [0] > '9' + || string[1] < '0' || string [1] > '9') + return 0; + + mnc = ((string[0] - '0') << 8) + | ((string[1] - '0') << 4) + | 0x00f; + } else + if (strlen(string) == 3) { + if (string[0] < '0' || string [0] > '9' + || string[1] < '0' || string [1] > '9' + || string[2] < '0' || string [2] > '9') + return 0; + + mnc = ((string[0] - '0') << 8) + | ((string[1] - '0') << 4) + | ((string[2] - '0')); + } + + return mnc; +} + +const char *gsm_get_mcc(uint16_t mcc) +{ + int i; + + for (i = 0; gsm_networks[i].name; i++) + if (gsm_networks[i].mnc < 0 && gsm_networks[i].mcc == mcc) + return gsm_networks[i].name; + + return gsm_print_mcc(mcc); +} + +const char *gsm_get_mnc(uint16_t mcc, uint16_t mnc) +{ + int i; + + for (i = 0; gsm_networks[i].name; i++) + if (gsm_networks[i].mcc == mcc && gsm_networks[i].mnc == mnc) + return gsm_networks[i].name; + + return gsm_print_mnc(mnc); +} + +/* get MCC from IMSI */ +const char *gsm_imsi_mcc(char *imsi) +{ + int i, found = 0; + uint16_t mcc; + + mcc = ((imsi[0] - '0') << 8) + | ((imsi[1] - '0') << 4) + | ((imsi[2] - '0')); + + for (i = 0; gsm_networks[i].name; i++) { + if (gsm_networks[i].mcc == mcc) { + found = 1; + break; + } + } + if (found == 0) + return "Unknown"; + + return gsm_networks[i].name; +} + +/* get MNC from IMSI */ +const char *gsm_imsi_mnc(char *imsi) +{ + int i, found = 0, position = 0; + uint16_t mcc, mnc2, mnc3; + + mcc = ((imsi[0] - '0') << 8) + | ((imsi[1] - '0') << 4) + | ((imsi[2] - '0')); + mnc2 = ((imsi[3] - '0') << 8) + + ((imsi[4] - '0') << 4) + + 0x00f; + mnc3 = ((imsi[3] - '0') << 8) + + ((imsi[4] - '0') << 4) + + imsi[5] - '0'; + + for (i = 0; gsm_networks[i].name; i++) { + if (gsm_networks[i].mcc != mcc) + continue; + if ((gsm_networks[i].mnc & 0x00f) == 0x00f) { + if (mnc2 == gsm_networks[i].mnc) { + found++; + position = i; + } + } else { + if (mnc3 == gsm_networks[i].mnc) { + found++; + position = i; + } + } + } + + if (found == 0) + return "Unknown"; + if (found > 1) + return "Ambiguous"; + return gsm_networks[position].name; +} + + diff --git a/src/host/layer23/src/common/sap_interface.c b/src/host/layer23/src/common/sap_interface.c new file mode 100644 index 00000000..d384c9eb --- /dev/null +++ b/src/host/layer23/src/common/sap_interface.c @@ -0,0 +1,189 @@ +/* BTSAP socket interface of layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/sap_interface.h> + +#include <osmocore/utils.h> + +#include <sys/socket.h> +#include <sys/un.h> + +#include <arpa/inet.h> + +#define _GNU_SOURCE +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +#define GSM_SAP_LENGTH 300 +#define GSM_SAP_HEADROOM 32 + +static int sap_read(struct bsc_fd *fd) +{ + struct msgb *msg; + u_int16_t len; + int rc; + struct osmocom_ms *ms = (struct osmocom_ms *) fd->data; + + msg = msgb_alloc_headroom(GSM_SAP_LENGTH+GSM_SAP_HEADROOM, GSM_SAP_HEADROOM, "Layer2"); + if (!msg) { + LOGP(DSAP, LOGL_ERROR, "Failed to allocate msg.\n"); + return -ENOMEM; + } + + rc = read(fd->fd, &len, sizeof(len)); + if (rc < sizeof(len)) { + fprintf(stderr, "SAP socket failed\n"); + msgb_free(msg); + if (rc >= 0) + rc = -EIO; + sap_close(ms); + return rc; + } + + len = ntohs(len); + if (len > GSM_SAP_LENGTH) { + LOGP(DSAP, LOGL_ERROR, "Length is too big: %u\n", len); + msgb_free(msg); + return -EINVAL; + } + + + msg->l1h = msgb_put(msg, len); + rc = read(fd->fd, msg->l1h, msgb_l1len(msg)); + if (rc != msgb_l1len(msg)) { + LOGP(DSAP, LOGL_ERROR, "Can not read data: len=%d rc=%d " + "errno=%d\n", len, rc, errno); + msgb_free(msg); + return rc; + } + + if (ms->sap_entity.msg_handler) + ms->sap_entity.msg_handler(msg, ms); + + return 0; +} + +static int sap_write(struct bsc_fd *fd, struct msgb *msg) +{ + int rc; + + if (fd->fd <= 0) + return -EINVAL; + + rc = write(fd->fd, msg->data, msg->len); + if (rc != msg->len) { + LOGP(DSAP, LOGL_ERROR, "Failed to write data: rc: %d\n", rc); + return rc; + } + + return 0; +} + +int sap_open(struct osmocom_ms *ms, const char *socket_path) +{ + int rc; + struct sockaddr_un local; + + ms->sap_wq.bfd.fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (ms->sap_wq.bfd.fd < 0) { + fprintf(stderr, "Failed to create unix domain socket.\n"); + return ms->sap_wq.bfd.fd; + } + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, socket_path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + + rc = connect(ms->sap_wq.bfd.fd, (struct sockaddr *) &local, + sizeof(local.sun_family) + strlen(local.sun_path)); + if (rc < 0) { + fprintf(stderr, "Failed to connect to '%s'.\n", local.sun_path); + close(ms->sap_wq.bfd.fd); + return rc; + } + + write_queue_init(&ms->sap_wq, 100); + ms->sap_wq.bfd.data = ms; + ms->sap_wq.bfd.when = BSC_FD_READ; + ms->sap_wq.read_cb = sap_read; + ms->sap_wq.write_cb = sap_write; + + rc = bsc_register_fd(&ms->sap_wq.bfd); + if (rc != 0) { + fprintf(stderr, "Failed to register fd.\n"); + return rc; + } + + return 0; +} + +int sap_close(struct osmocom_ms *ms) +{ + if (ms->sap_wq.bfd.fd <= 0) + return -EINVAL; + + close(ms->sap_wq.bfd.fd); + ms->sap_wq.bfd.fd = -1; + bsc_unregister_fd(&ms->sap_wq.bfd); + + return 0; +} + +int osmosap_send(struct osmocom_ms *ms, struct msgb *msg) +{ + uint16_t *len; + + if (ms->sap_wq.bfd.fd <= 0) + return -EINVAL; + + DEBUGP(DSAP, "Sending: '%s'\n", hexdump(msg->data, msg->len)); + + if (msg->l1h != msg->data) + LOGP(DSAP, LOGL_ERROR, "Message SAP header != Message Data\n"); + + /* prepend 16bit length before sending */ + len = (uint16_t *) msgb_push(msg, sizeof(*len)); + *len = htons(msg->len - sizeof(*len)); + + if (write_queue_enqueue(&ms->sap_wq, msg) != 0) { + LOGP(DSAP, LOGL_ERROR, "Failed to enqueue msg.\n"); + msgb_free(msg); + return -1; + } + + return 0; +} + +/* register message handler for messages that are sent from L2->L3 */ +int osmosap_register_handler(struct osmocom_ms *ms, osmosap_cb_t cb) +{ + ms->sap_entity.msg_handler = cb; + + return 0; +} + diff --git a/src/host/layer23/src/common/sim.c b/src/host/layer23/src/common/sim.c new file mode 100644 index 00000000..3ef3cf84 --- /dev/null +++ b/src/host/layer23/src/common/sim.c @@ -0,0 +1,1236 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <arpa/inet.h> +#include <osmocore/talloc.h> +#include <osmocore/utils.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1ctl.h> + +extern void *l23_ctx; +static int sim_process_job(struct osmocom_ms *ms); + +/* + * support + */ + +uint32_t new_handle = 1; + +static struct gsm1111_df_name { + uint16_t file; + const char *name; +} gsm1111_df_name[] = { + { 0x3f00, "MF" }, + { 0x7f20, "DFgsm" }, + { 0x7f10, "DFtelecom" }, + { 0x7f22, "DFis-41" }, + { 0x7f23, "DFfp-cts" }, + { 0x5f50, "DFgraphics" }, + { 0x5f30, "DFiridium" }, + { 0x5f31, "DFglobst" }, + { 0x5f32, "DFico" }, + { 0x5f33, "DFaces" }, + { 0x5f40, "DFeia/tia-553" }, + { 0x5f60, "DFcts" }, + { 0x5f70, "DFsolsa" }, + { 0x5f3c, "DFmexe" }, + { 0, NULL } +}; + +static const char *get_df_name(uint16_t fid) +{ + int i; + static char text[7]; + + for (i = 0; gsm1111_df_name[i].file; i++) + if (gsm1111_df_name[i].file == fid) + break; + if (gsm1111_df_name[i].file) + return gsm1111_df_name[i].name; + + sprintf(text, "0x%04x", fid); + return text; +} + +static struct gsm_sim_handler *sim_get_handler(struct gsm_sim *sim, + uint32_t handle) +{ + struct gsm_sim_handler *handler; + + llist_for_each_entry(handler, &sim->handlers, entry) + if (handler->handle == handle) + return handler; + + return NULL; +} + +/* + * messages + */ + +static const struct value_string sim_job_names[] = { + { SIM_JOB_READ_BINARY, "SIM_JOB_READ_BINARY" }, + { SIM_JOB_UPDATE_BINARY, "SIM_JOB_UPDATE_BINARY" }, + { SIM_JOB_READ_RECORD, "SIM_JOB_READ_RECORD" }, + { SIM_JOB_UPDATE_RECORD, "SIM_JOB_UPDATE_RECORD" }, + { SIM_JOB_SEEK_RECORD, "SIM_JOB_SEEK_RECORD" }, + { SIM_JOB_INCREASE, "SIM_JOB_INCREASE" }, + { SIM_JOB_INVALIDATE, "SIM_JOB_INVALIDATE" }, + { SIM_JOB_REHABILITATE, "SIM_JOB_REHABILITATE" }, + { SIM_JOB_RUN_GSM_ALGO, "SIM_JOB_RUN_GSM_ALGO" }, + { SIM_JOB_PIN1_UNLOCK, "SIM_JOB_PIN1_UNLOCK" }, + { SIM_JOB_PIN1_CHANGE, "SIM_JOB_PIN1_CHANGE" }, + { SIM_JOB_PIN1_DISABLE, "SIM_JOB_PIN1_DISABLE" }, + { SIM_JOB_PIN1_ENABLE, "SIM_JOB_PIN1_ENABLE" }, + { SIM_JOB_PIN1_UNBLOCK, "SIM_JOB_PIN1_UNBLOCK" }, + { SIM_JOB_PIN2_UNLOCK, "SIM_JOB_PIN2_UNLOCK" }, + { SIM_JOB_PIN2_CHANGE, "SIM_JOB_PIN2_CHANGE" }, + { SIM_JOB_PIN2_UNBLOCK, "SIM_JOB_PIN2_UNBLOCK" }, + { SIM_JOB_OK, "SIM_JOB_OK" }, + { SIM_JOB_ERROR, "SIM_JOB_ERROR" }, + { 0, NULL } +}; + +static const char *get_job_name(int value) +{ + return get_value_string(sim_job_names, value); +} + +/* allocate sim client message (upper layer) */ +struct msgb *gsm_sim_msgb_alloc(uint32_t handle, uint8_t job_type) +{ + struct msgb *msg; + struct sim_hdr *nsh; + + msg = msgb_alloc_headroom(SIM_ALLOC_SIZE+SIM_ALLOC_HEADROOM, + SIM_ALLOC_HEADROOM, "SIM"); + if (!msg) + return NULL; + + nsh = (struct sim_hdr *) msgb_put(msg, sizeof(*nsh)); + nsh->handle = handle; + nsh->job_type = job_type; + + return msg; +} + +/* reply to job, after it is done. reuse the msgb in the job */ +void gsm_sim_reply(struct osmocom_ms *ms, uint8_t result_type, uint8_t *result, + uint16_t result_len) +{ + struct gsm_sim *sim = &ms->sim; + struct msgb *msg = sim->job_msg; + struct sim_hdr *sh; + uint8_t *payload; + uint16_t payload_len; + struct gsm_sim_handler *handler; + + LOGP(DSIM, LOGL_INFO, "sending result to callback function " + "(type=%d)\n", result_type); + + /* if no handler, or no callback, just free the job */ + sh = (struct sim_hdr *)msg->data; + handler = sim_get_handler(sim, sh->handle); + if (!handler || !handler->cb) { + LOGP(DSIM, LOGL_INFO, "no callback or no handler, " + "dropping result\n"); + msgb_free(sim->job_msg); + sim->job_msg = NULL; + sim->job_state = SIM_JST_IDLE; + return; + } + + payload = msg->data + sizeof(*sh); + payload_len = msg->len - sizeof(*sh); + + /* remove data */ + msg->tail -= payload_len; + msg->len -= payload_len; + + /* add reply data */ + sh->job_type = result_type; + if (result_len) + memcpy(msgb_put(msg, result_len), result, result_len); + + /* callback */ + sim->job_state = SIM_JST_IDLE; + sim->job_msg = NULL; + handler->cb(ms, msg); +} + +/* send APDU to card reader */ +static int sim_apdu_send(struct osmocom_ms *ms, uint8_t *data, uint16_t length) +{ + LOGP(DSIM, LOGL_INFO, "sending APDU (class 0x%02x, ins 0x%02x)\n", + data[0], data[1]); + l1ctl_tx_sim_req(ms, data, length); + return 0; +} + +/* dequeue messages (RSL-SAP) */ +int gsm_sim_job_dequeue(struct osmocom_ms *ms) +{ + struct gsm_sim *sim = &ms->sim; + struct sim_hdr *sh; + struct msgb *msg; + struct gsm_sim_handler *handler; + + /* already have a job */ + if (sim->job_msg) + return 0; + + /* get next job */ + while ((msg = msgb_dequeue(&sim->jobs))) { + /* resolve handler */ + sh = (struct sim_hdr *) msg->data; + LOGP(DSIM, LOGL_INFO, "got new job: %s (handle=%08x)\n", + get_job_name(sh->job_type), sh->handle); + handler = sim_get_handler(sim, sh->handle); + if (!handler) { + LOGP(DSIM, LOGL_INFO, "no handler, ignoring job\n"); + /* does not exist anymore */ + msgb_free(msg); + continue; + } + + /* init job */ + sim->job_state = SIM_JST_IDLE; + sim->job_msg = msg; + sim->job_handle = sh->handle; + + /* process current job, message is freed there */ + sim_process_job(ms); + return 1; /* work done */ + } + + return 0; +} + + +/* + * SIM commands + */ + +/* 9.2.1 */ +static int gsm1111_tx_select(struct osmocom_ms *ms, uint16_t fid) +{ + uint8_t buffer[5 + 2]; + + LOGP(DSIM, LOGL_INFO, "SELECT (file=0x%04x)\n", fid); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_SELECT; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 2; + buffer[5] = fid >> 8; + buffer[6] = fid; + + return sim_apdu_send(ms, buffer, 5 + 2); +} + +#if 0 +/* 9.2.2 */ +static int gsm1111_tx_status(struct osmocom_ms *ms) +{ + uint8_t buffer[5]; + + LOGP(DSIM, LOGL_INFO, "STATUS\n"); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_STATUS; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0; + + return sim_apdu_send(ms, buffer, 5); +} +#endif + +/* 9.2.3 */ +static int gsm1111_tx_read_binary(struct osmocom_ms *ms, uint16_t offset, + uint8_t length) +{ + uint8_t buffer[5]; + + LOGP(DSIM, LOGL_INFO, "READ BINARY (offset=%d len=%d)\n", offset, + length); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_READ_BINARY; + buffer[2] = offset >> 8; + buffer[3] = offset; + buffer[4] = length; + + return sim_apdu_send(ms, buffer, 5); +} + +/* 9.2.4 */ +static int gsm1111_tx_update_binary(struct osmocom_ms *ms, uint16_t offset, + uint8_t *data, uint8_t length) +{ + uint8_t buffer[5 + length]; + + LOGP(DSIM, LOGL_INFO, "UPDATE BINARY (offset=%d len=%d)\n", offset, + length); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_UPDATE_BINARY; + buffer[2] = offset >> 8; + buffer[3] = offset; + buffer[4] = length; + memcpy(buffer + 5, data, length); + + return sim_apdu_send(ms, buffer, 5 + length); +} + +/* 9.2.5 */ +static int gsm1111_tx_read_record(struct osmocom_ms *ms, uint8_t rec_no, + uint8_t mode, uint8_t length) +{ + uint8_t buffer[5]; + + LOGP(DSIM, LOGL_INFO, "READ RECORD (rec_no=%d mode=%d len=%d)\n", + rec_no, mode, length); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_READ_RECORD; + buffer[2] = rec_no; + buffer[3] = mode; + buffer[4] = length; + + return sim_apdu_send(ms, buffer, 5); +} + +/* 9.2.6 */ +static int gsm1111_tx_update_record(struct osmocom_ms *ms, uint8_t rec_no, + uint8_t mode, uint8_t *data, uint8_t length) +{ + uint8_t buffer[5 + length]; + + LOGP(DSIM, LOGL_INFO, "UPDATE RECORD (rec_no=%d mode=%d len=%d)\n", + rec_no, mode, length); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_UPDATE_RECORD; + buffer[2] = rec_no; + buffer[3] = mode; + buffer[4] = length; + memcpy(buffer + 5, data, length); + + return sim_apdu_send(ms, buffer, 5 + length); +} + +/* 9.2.7 */ +static int gsm1111_tx_seek(struct osmocom_ms *ms, uint8_t type_mode, + uint8_t *pattern, uint8_t length) +{ + uint8_t buffer[5 + length]; + uint8_t type = type_mode >> 4; + uint8_t mode = type_mode & 0x0f; + + LOGP(DSIM, LOGL_INFO, "SEEK (type=%d mode=%d len=%d)\n", type, mode, + length); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_SEEK; + buffer[2] = 0x00; + buffer[3] = type_mode; + buffer[4] = length; + memcpy(buffer + 5, pattern, length); + + return sim_apdu_send(ms, buffer, 5 + length); +} + +/* 9.2.8 */ +static int gsm1111_tx_increase(struct osmocom_ms *ms, uint32_t value) +{ + uint8_t buffer[5 + 3]; + + LOGP(DSIM, LOGL_INFO, "INCREASE (value=%d)\n", value); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_INCREASE; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 3; + buffer[5] = value >> 16; + buffer[6] = value >> 8; + buffer[7] = value; + + return sim_apdu_send(ms, buffer, 5 + 3); +} + +/* 9.2.9 */ +static int gsm1111_tx_verify_chv(struct osmocom_ms *ms, uint8_t chv_no, + uint8_t *chv, uint8_t length) +{ + uint8_t buffer[5 + 8]; + int i; + + LOGP(DSIM, LOGL_INFO, "VERIFY CHV (CHV%d)\n", chv_no); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_VERIFY_CHV; + buffer[2] = 0x00; + buffer[3] = chv_no; + buffer[4] = 8; + for (i = 0; i < 8; i++) { + if (i < length) + buffer[5 + i] = chv[i]; + else + buffer[5 + i] = 0xff; + } + + return sim_apdu_send(ms, buffer, 5 + 8); +} + +/* 9.2.10 */ +static int gsm1111_tx_change_chv(struct osmocom_ms *ms, uint8_t chv_no, + uint8_t *chv_old, uint8_t length_old, uint8_t *chv_new, + uint8_t length_new) +{ + uint8_t buffer[5 + 16]; + int i; + + LOGP(DSIM, LOGL_INFO, "CHANGE CHV (CHV%d)\n", chv_no); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_CHANGE_CHV; + buffer[2] = 0x00; + buffer[3] = chv_no; + buffer[4] = 16; + for (i = 0; i < 8; i++) { + if (i < length_old) + buffer[5 + i] = chv_old[i]; + else + buffer[5 + i] = 0xff; + if (i < length_new) + buffer[13 + i] = chv_new[i]; + else + buffer[13 + i] = 0xff; + } + + return sim_apdu_send(ms, buffer, 5 + 16); +} + +/* 9.2.11 */ +static int gsm1111_tx_disable_chv(struct osmocom_ms *ms, uint8_t *chv, + uint8_t length) +{ + uint8_t buffer[5 + 8]; + int i; + + LOGP(DSIM, LOGL_INFO, "DISABLE CHV (CHV1)\n"); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_DISABLE_CHV; + buffer[2] = 0x00; + buffer[3] = 0x01; + buffer[4] = 8; + for (i = 0; i < 8; i++) { + if (i < length) + buffer[5 + i] = chv[i]; + else + buffer[5 + i] = 0xff; + } + + return sim_apdu_send(ms, buffer, 5 + 8); +} + +/* 9.2.12 */ +static int gsm1111_tx_enable_chv(struct osmocom_ms *ms, uint8_t *chv, + uint8_t length) +{ + uint8_t buffer[5 + 8]; + int i; + + LOGP(DSIM, LOGL_INFO, "ENABLE CHV (CHV1)\n"); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_ENABLE_CHV; + buffer[2] = 0x00; + buffer[3] = 0x01; + buffer[4] = 8; + for (i = 0; i < 8; i++) { + if (i < length) + buffer[5 + i] = chv[i]; + else + buffer[5 + i] = 0xff; + } + + return sim_apdu_send(ms, buffer, 5 + 8); +} + +/* 9.2.13 */ +static int gsm1111_tx_unblock_chv(struct osmocom_ms *ms, uint8_t chv_no, + uint8_t *chv_unblk, uint8_t length_unblk, uint8_t *chv_new, + uint8_t length_new) +{ + uint8_t buffer[5 + 16]; + int i; + + LOGP(DSIM, LOGL_INFO, "UNBLOCK CHV (CHV%d)\n", (chv_no == 2) ? 2 : 1); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_UNBLOCK_CHV; + buffer[2] = 0x00; + buffer[3] = (chv_no == 1) ? 0 : chv_no; + buffer[4] = 16; + for (i = 0; i < 8; i++) { + if (i < length_unblk) + buffer[5 + i] = chv_unblk[i]; + else + buffer[5 + i] = 0xff; + if (i < length_new) + buffer[13 + i] = chv_new[i]; + else + buffer[13 + i] = 0xff; + } + + return sim_apdu_send(ms, buffer, 5 + 16); +} + +/* 9.2.14 */ +static int gsm1111_tx_invalidate(struct osmocom_ms *ms) +{ + uint8_t buffer[5]; + + LOGP(DSIM, LOGL_INFO, "INVALIDATE\n"); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_INVALIDATE; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0; + + return sim_apdu_send(ms, buffer, 5); +} + +/* 9.2.15 */ +static int gsm1111_tx_rehabilitate(struct osmocom_ms *ms) +{ + uint8_t buffer[5]; + + LOGP(DSIM, LOGL_INFO, "REHABILITATE\n"); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_REHABLILITATE; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0; + + return sim_apdu_send(ms, buffer, 5); +} + +/* 9.2.16 */ +static int gsm1111_tx_run_gsm_algo(struct osmocom_ms *ms, uint8_t *rand) +{ + uint8_t buffer[5 + 16]; + + LOGP(DSIM, LOGL_INFO, "RUN GSM ALGORITHM\n"); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_RUN_GSM_ALGO; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 16; + memcpy(buffer + 5, rand, 16); + + return sim_apdu_send(ms, buffer, 5 + 16); +} + +#if 0 +/* 9.2.17 */ +static int gsm1111_tx_sleep(struct osmocom_ms *ms) +{ + uint8_t buffer[5]; + + LOGP(DSIM, LOGL_INFO, "\n"); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_SLEEP; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0; + + return sim_apdu_send(ms, buffer, 5); +} +#endif + +/* 9.2.18 */ +static int gsm1111_tx_get_response(struct osmocom_ms *ms, uint8_t length) +{ + uint8_t buffer[5]; + + LOGP(DSIM, LOGL_INFO, "GET RESPONSE (len=%d)\n", length); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_GET_RESPONSE; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = length; + + return sim_apdu_send(ms, buffer, 5); +} + +#if 0 +/* 9.2.19 */ +static int gsm1111_tx_terminal_profile(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + uint8_t buffer[5 + length]; + + LOGP(DSIM, LOGL_INFO, "TERMINAL PROFILE (len=%d)\n", length); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_TERMINAL_PROFILE; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = length; + memcpy(buffer + 5, data, length); + + return sim_apdu_send(ms, buffer, 5 + length); +} + +/* 9.2.20 */ +static int gsm1111_tx_envelope(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + uint8_t buffer[5 + length]; + + LOGP(DSIM, LOGL_INFO, "ENVELOPE (len=%d)\n", length); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_ENVELOPE; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = length; + memcpy(buffer + 5, data, length); + + return sim_apdu_send(ms, buffer, 5 + length); +} + +/* 9.2.21 */ +static int gsm1111_tx_fetch(struct osmocom_ms *ms, uint8_t length) +{ + uint8_t buffer[5]; + + LOGP(DSIM, LOGL_INFO, "FETCH (len=%d)\n", length); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_FETCH; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = length; + + return sim_apdu_send(ms, buffer, 5); +} + +/* 9.2.22 */ +static int gsm1111_tx_terminal_response(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + uint8_t buffer[5 + length]; + + LOGP(DSIM, LOGL_INFO, "TERMINAL RESPONSE (len=%d)\n", length); + buffer[0] = GSM1111_CLASS_GSM; + buffer[1] = GSM1111_INST_TERMINAL_RESPONSE; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = length; + memcpy(buffer + 5, data, length); + + return sim_apdu_send(ms, buffer, 5 + length); +} +#endif + +/* + * SIM state machine + */ + +/* process job */ +static int sim_process_job(struct osmocom_ms *ms) +{ + struct gsm_sim *sim = &ms->sim; + uint8_t *payload, *payload2; + uint16_t payload_len, payload_len2; + struct sim_hdr *sh; + uint8_t cause; + int i; + + /* no current */ + if (!sim->job_msg) + return 0; + + sh = (struct sim_hdr *)sim->job_msg->data; + payload = sim->job_msg->data + sizeof(*sh); + payload_len = sim->job_msg->len - sizeof(*sh); + + /* do reset before sim reading */ + if (!sim->reset) { + sim->reset = 1; + // FIXME: send reset command to L1 + } + + /* navigate to right DF */ + switch (sh->job_type) { + case SIM_JOB_READ_BINARY: + case SIM_JOB_UPDATE_BINARY: + case SIM_JOB_READ_RECORD: + case SIM_JOB_UPDATE_RECORD: + case SIM_JOB_SEEK_RECORD: + case SIM_JOB_INCREASE: + case SIM_JOB_INVALIDATE: + case SIM_JOB_REHABILITATE: + case SIM_JOB_RUN_GSM_ALGO: + /* check MF / DF */ + i = 0; + while (sh->path[i] && sim->path[i]) { + if (sh->path[i] != sh->path[i]) + break; + i++; + } + /* if path in message is shorter or if paths are different */ + if (sim->path[i]) { + LOGP(DSIM, LOGL_INFO, "go MF\n"); + sim->job_state = SIM_JST_SELECT_MFDF; + /* go MF */ + sim->path[0] = 0; + return gsm1111_tx_select(ms, 0x3f00); + } + /* if path in message is longer */ + if (sh->path[i]) { + LOGP(DSIM, LOGL_INFO, "requested path is longer, go " + "child %s\n", get_df_name(sh->path[i])); + sim->job_state = SIM_JST_SELECT_MFDF; + /* select child */ + sim->path[i] = sh->path[i]; + sim->path[i + 1] = 0; + return gsm1111_tx_select(ms, sh->path[i]); + } + /* if paths are equal, continue */ + } + + /* set state and trigger SIM process */ + switch (sh->job_type) { + case SIM_JOB_READ_BINARY: + case SIM_JOB_UPDATE_BINARY: + case SIM_JOB_READ_RECORD: + case SIM_JOB_UPDATE_RECORD: + case SIM_JOB_SEEK_RECORD: + case SIM_JOB_INCREASE: + case SIM_JOB_INVALIDATE: + case SIM_JOB_REHABILITATE: + sim->job_state = SIM_JST_SELECT_EF; + sim->file = sh->file; + return gsm1111_tx_select(ms, sh->file); + case SIM_JOB_RUN_GSM_ALGO: + if (payload_len != 16) { + LOGP(DSIM, LOGL_ERROR, "random not 16 bytes\n"); + break; + } + sim->job_state = SIM_JST_RUN_GSM_ALGO; + return gsm1111_tx_run_gsm_algo(ms, payload); + case SIM_JOB_PIN1_UNLOCK: + payload_len = strlen((char *)payload); + if (payload_len < 4 || payload_len > 8) { + LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n"); + break; + } + sim->job_state = SIM_JST_PIN1_UNLOCK; + return gsm1111_tx_verify_chv(ms, 0x01, payload, payload_len); + case SIM_JOB_PIN2_UNLOCK: + payload_len = strlen((char *)payload); + if (payload_len < 4 || payload_len > 8) { + LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n"); + break; + } + sim->job_state = SIM_JST_PIN2_UNLOCK; + return gsm1111_tx_verify_chv(ms, 0x02, payload, payload_len); + case SIM_JOB_PIN1_CHANGE: + payload_len = strlen((char *)payload); + payload2 = payload + payload_len + 1; + payload_len2 = strlen((char *)payload2); + if (payload_len < 4 || payload_len > 8) { + LOGP(DSIM, LOGL_ERROR, "key1 not in range 4..8\n"); + break; + } + if (payload_len2 < 4 || payload_len2 > 8) { + LOGP(DSIM, LOGL_ERROR, "key2 not in range 4..8\n"); + break; + } + sim->job_state = SIM_JST_PIN1_CHANGE; + return gsm1111_tx_change_chv(ms, 0x01, payload, payload_len, + payload2, payload_len2); + case SIM_JOB_PIN2_CHANGE: + payload_len = strlen((char *)payload); + payload2 = payload + payload_len + 1; + payload_len2 = strlen((char *)payload2); + if (payload_len < 4 || payload_len > 8) { + LOGP(DSIM, LOGL_ERROR, "key1 not in range 4..8\n"); + break; + } + if (payload_len2 < 4 || payload_len2 > 8) { + LOGP(DSIM, LOGL_ERROR, "key2 not in range 4..8\n"); + break; + } + sim->job_state = SIM_JST_PIN2_CHANGE; + return gsm1111_tx_change_chv(ms, 0x02, payload, payload_len, + payload2, payload_len2); + case SIM_JOB_PIN1_DISABLE: + payload_len = strlen((char *)payload); + if (payload_len < 4 || payload_len > 8) { + LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n"); + break; + } + sim->job_state = SIM_JST_PIN1_DISABLE; + return gsm1111_tx_disable_chv(ms, payload, payload_len); + case SIM_JOB_PIN1_ENABLE: + payload_len = strlen((char *)payload); + if (payload_len < 4 || payload_len > 8) { + LOGP(DSIM, LOGL_ERROR, "key not in range 4..8\n"); + break; + } + sim->job_state = SIM_JST_PIN1_ENABLE; + return gsm1111_tx_enable_chv(ms, payload, payload_len); + case SIM_JOB_PIN1_UNBLOCK: + payload_len = strlen((char *)payload); + payload2 = payload + payload_len + 1; + payload_len2 = strlen((char *)payload2); + if (payload_len != 8) { + LOGP(DSIM, LOGL_ERROR, "key1 not 8 digits\n"); + break; + } + if (payload_len2 < 4 || payload_len2 > 8) { + LOGP(DSIM, LOGL_ERROR, "key2 not in range 4..8\n"); + break; + } + sim->job_state = SIM_JST_PIN1_UNBLOCK; + /* NOTE: CHV1 is coded 0x00 here */ + return gsm1111_tx_unblock_chv(ms, 0x00, payload, payload_len, + payload2, payload_len2); + case SIM_JOB_PIN2_UNBLOCK: + payload_len = strlen((char *)payload); + payload2 = payload + payload_len + 1; + payload_len2 = strlen((char *)payload2); + if (payload_len != 8) { + LOGP(DSIM, LOGL_ERROR, "key1 not 8 digits\n"); + break; + } + if (payload_len2 < 4 || payload_len2 > 8) { + LOGP(DSIM, LOGL_ERROR, "key2 not in range 4..8\n"); + break; + } + sim->job_state = SIM_JST_PIN2_UNBLOCK; + return gsm1111_tx_unblock_chv(ms, 0x02, payload, payload_len, + payload2, payload_len2); + } + + LOGP(DSIM, LOGL_ERROR, "unknown job %x, please fix\n", sh->job_type); + cause = SIM_CAUSE_REQUEST_ERROR; + gsm_sim_reply(ms, SIM_JOB_ERROR, &cause, 1); + + return 0; +} + +/* receive SIM response */ +int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_sim *sim = &ms->sim; + uint8_t *payload; + uint16_t payload_len; + uint8_t *data = msg->data; + int length = msg->len, ef_len; + uint8_t sw1, sw2; + uint8_t cause; + uint8_t pin_cause[2]; + struct sim_hdr *sh; + struct gsm1111_response_ef *ef; + struct gsm1111_response_mfdf *mfdf; + struct gsm1111_response_mfdf_gsm *mfdf_gsm; + int i; + + /* ignore, if current job already gone */ + if (!sim->job_msg) { + LOGP(DSIM, LOGL_ERROR, "received APDU but no job, " + "please fix!\n"); + msgb_free(msg); + return 0; + } + + sh = (struct sim_hdr *)sim->job_msg->data; + payload = sim->job_msg->data + sizeof(*sh); + payload_len = sim->job_msg->len - sizeof(*sh); + + /* process status */ + if (length < 2) { + msgb_free(msg); + return 0; + } + sw1 = data[length - 2]; + sw2 = data[length - 1]; + length -= 2; + LOGP(DSIM, LOGL_INFO, "received APDU (len=%d sw1=0x%02x sw2=0x%02x)\n", + length, sw1, sw2); + + switch (sw1) { + case GSM1111_STAT_SECURITY: + LOGP(DSIM, LOGL_NOTICE, "SIM Security\n"); + /* error */ + if (sw2 != GSM1111_SEC_NO_ACCESS && sw2 != GSM1111_SEC_BLOCKED) + goto sim_error; + + /* select the right remaining counter an cause */ + // FIXME: read status to replace "*_remain"-counters + switch (sim->job_state) { + case SIM_JST_PIN1_UNBLOCK: + if (sw2 == GSM1111_SEC_NO_ACCESS) { + pin_cause[0] = SIM_CAUSE_PIN1_BLOCKED; + pin_cause[1] = --sim->unblk1_remain; + } else { + pin_cause[0] = SIM_CAUSE_PUC_BLOCKED; + pin_cause[1] = 0; + } + break; + case SIM_JST_PIN2_UNLOCK: + case SIM_JST_PIN2_CHANGE: + if (sw2 == GSM1111_SEC_NO_ACCESS && sim->chv2_remain) { + pin_cause[0] = SIM_CAUSE_PIN2_REQUIRED; + pin_cause[1] = sim->chv2_remain--; + } else { + pin_cause[0] = SIM_CAUSE_PIN2_BLOCKED; + pin_cause[1] = sim->unblk2_remain; + } + break; + case SIM_JST_PIN2_UNBLOCK: + if (sw2 == GSM1111_SEC_NO_ACCESS) { + pin_cause[0] = SIM_CAUSE_PIN2_BLOCKED; + pin_cause[1] = --sim->unblk2_remain; + } else { + pin_cause[0] = SIM_CAUSE_PUC_BLOCKED; + pin_cause[1] = 0; + } + case SIM_JST_PIN1_UNLOCK: + case SIM_JST_PIN1_CHANGE: + case SIM_JST_PIN1_DISABLE: + case SIM_JST_PIN1_ENABLE: + default: + if (sw2 == GSM1111_SEC_NO_ACCESS && sim->chv1_remain) { + pin_cause[0] = SIM_CAUSE_PIN1_REQUIRED; + pin_cause[1] = sim->chv1_remain--; + } else { + pin_cause[0] = SIM_CAUSE_PIN1_BLOCKED; + pin_cause[1] = sim->unblk1_remain; + } + break; + } + gsm_sim_reply(ms, SIM_JOB_ERROR, pin_cause, 2); + msgb_free(msg); + return 0; + case GSM1111_STAT_MEM_PROBLEM: + if (sw2 >= 0x40) { + LOGP(DSIM, LOGL_NOTICE, "memory of SIM failed\n"); + sim_error: + cause = SIM_CAUSE_SIM_ERROR; + gsm_sim_reply(ms, SIM_JOB_ERROR, &cause, 1); + msgb_free(msg); + return 0; + } + LOGP(DSIM, LOGL_NOTICE, "memory of SIM is bad (write took %d " + "times to succeed)\n", sw2); + /* fall through */ + case GSM1111_STAT_NORMAL: + case GSM1111_STAT_PROACTIVE: + case GSM1111_STAT_DL_ERROR: + case GSM1111_STAT_RESPONSE: + case GSM1111_STAT_RESPONSE_TOO: + LOGP(DSIM, LOGL_INFO, "command successfull\n"); + break; + default: + LOGP(DSIM, LOGL_INFO, "command failed\n"); + request_error: + cause = SIM_CAUSE_REQUEST_ERROR; + gsm_sim_reply(ms, SIM_JOB_ERROR, &cause, 1); + msgb_free(msg); + return 0; + } + + + switch (sim->job_state) { + /* step 1: after selecting MF / DF, request the response */ + case SIM_JST_SELECT_MFDF: + /* not enough data */ + if (sw2 < 22) { + LOGP(DSIM, LOGL_NOTICE, "expecting minimum 22 bytes\n"); + goto sim_error; + } + /* request response */ + sim->job_state = SIM_JST_SELECT_MFDF_RESP; + gsm1111_tx_get_response(ms, sw2); + msgb_free(msg); + return 0; + /* step 2: after getting response of selecting MF / DF, continue + * to "process_job". + */ + case SIM_JST_SELECT_MFDF_RESP: + if (length < 22) { + LOGP(DSIM, LOGL_NOTICE, "expecting minimum 22 bytes\n"); + goto sim_error; + } + mfdf = (struct gsm1111_response_mfdf *)data; + mfdf_gsm = (struct gsm1111_response_mfdf_gsm *)(data + 13); + sim->chv1_remain = mfdf_gsm->chv1_remain; + sim->chv2_remain = mfdf_gsm->chv2_remain; + sim->unblk1_remain = mfdf_gsm->unblk1_remain; + sim->unblk2_remain = mfdf_gsm->unblk2_remain; + /* if MF was selected */ + if (sim->path[0] == 0) { + /* if MF was selected, but MF is not indicated */ + if (ntohs(mfdf->file_id) != 0x3f00) { + LOGP(DSIM, LOGL_NOTICE, "Not MF\n"); + goto sim_error; + } + /* if MF was selected, but type is not indicated */ + if (mfdf->tof != GSM1111_TOF_MF) { + LOGP(DSIM, LOGL_NOTICE, "MF %02x != %02x " + "%04x\n", mfdf->tof, GSM1111_TOF_MF, + sim->path[0]); + goto sim_error; + } + /* now continue */ + msgb_free(msg); + return sim_process_job(ms); + } + /* if DF was selected, but this DF is not indicated */ + i = 0; + while (sim->path[i + 1]) + i++; + if (ntohs(mfdf->file_id) != sim->path[i]) { + LOGP(DSIM, LOGL_NOTICE, "Path %04x != %04x\n", + ntohs(mfdf->file_id), sim->path[i]); + goto sim_error; + } + /* if DF was selected, but type is not indicated */ + if (mfdf->tof != GSM1111_TOF_DF) { + LOGP(DSIM, LOGL_NOTICE, "TOF error\n"); + goto sim_error; + } + /* now continue */ + msgb_free(msg); + return sim_process_job(ms); + /* step 1: after selecting EF, request response of SELECT */ + case SIM_JST_SELECT_EF: + /* not enough data */ + if (sw2 < 14) { + LOGP(DSIM, LOGL_NOTICE, "expecting minimum 14 bytes\n"); + goto sim_error; + } + /* request response */ + sim->job_state = SIM_JST_SELECT_EF_RESP; + gsm1111_tx_get_response(ms, sw2); + msgb_free(msg); + return 0; + /* step 2: after getting response of selecting EF, do file command */ + case SIM_JST_SELECT_EF_RESP: + if (length < 14) { + LOGP(DSIM, LOGL_NOTICE, "expecting minimum 14 bytes\n"); + goto sim_error; + } + ef = (struct gsm1111_response_ef *)data; + /* if EF was selected, but type is not indicated */ + if (ntohs(ef->file_id) != sim->file) { + LOGP(DSIM, LOGL_NOTICE, "EF ID %04x != %04x\n", + ntohs(ef->file_id), sim->file); + goto sim_error; + } + /* get length of file */ + ef_len = ntohs(ef->file_size); + /* do file command */ + sim->job_state = SIM_JST_WAIT_FILE; + switch (sh->job_type) { + case SIM_JOB_READ_BINARY: + // FIXME: do chunks when greater or equal 256 bytes */ + gsm1111_tx_read_binary(ms, 0, ef_len); + break; + case SIM_JOB_UPDATE_BINARY: + // FIXME: do chunks when greater or equal 256 bytes */ + if (ef_len < payload_len) { + LOGP(DSIM, LOGL_NOTICE, "selected file is " + "smaller (%d) than data to update " + "(%d)\n", ef_len, payload_len); + goto request_error; + } + gsm1111_tx_update_binary(ms, 0, payload, payload_len); + break; + case SIM_JOB_READ_RECORD: + gsm1111_tx_read_record(ms, sh->rec_no, sh->rec_mode, + ef_len); + break; + case SIM_JOB_UPDATE_RECORD: + if (ef_len != payload_len) { + LOGP(DSIM, LOGL_NOTICE, "selected file length " + "(%d) does not equal record to update " + "(%d)\n", ef_len, payload_len); + goto request_error; + } + gsm1111_tx_update_record(ms, sh->rec_no, sh->rec_mode, + payload, payload_len); + break; + case SIM_JOB_SEEK_RECORD: + gsm1111_tx_seek(ms, sh->seek_type_mode, data, length); + break; + case SIM_JOB_INCREASE: + if (length != 4) { + LOGP(DSIM, LOGL_ERROR, "expecting uint32_t as " + "value lenght, but got %d bytes\n", + length); + goto request_error; + } + gsm1111_tx_increase(ms, *((uint32_t *)data)); + break; + case SIM_JOB_INVALIDATE: + gsm1111_tx_invalidate(ms); + break; + case SIM_JOB_REHABILITATE: + gsm1111_tx_rehabilitate(ms); + break; + } + msgb_free(msg); + return 0; + /* step 3: after processing file command, job is done */ + case SIM_JST_WAIT_FILE: + /* reply job with data */ + gsm_sim_reply(ms, SIM_JOB_OK, data, length); + msgb_free(msg); + return 0; + /* step 1: after running GSM algorithm, request response */ + case SIM_JST_RUN_GSM_ALGO: + /* not enough data */ + if (sw2 < 12) { + LOGP(DSIM, LOGL_NOTICE, "expecting minimum 12 bytes\n"); + goto sim_error; + } + /* request response */ + sim->job_state = SIM_JST_RUN_GSM_ALGO_RESP; + gsm1111_tx_get_response(ms, sw2); + msgb_free(msg); + return 0; + /* step 2: after processing GSM command, job is done */ + case SIM_JST_RUN_GSM_ALGO_RESP: + /* reply job with data */ + gsm_sim_reply(ms, SIM_JOB_OK, data, length); + msgb_free(msg); + return 0; + case SIM_JST_PIN1_UNLOCK: + case SIM_JST_PIN1_CHANGE: + case SIM_JST_PIN1_DISABLE: + case SIM_JST_PIN1_ENABLE: + case SIM_JST_PIN1_UNBLOCK: + case SIM_JST_PIN2_UNLOCK: + case SIM_JST_PIN2_CHANGE: + case SIM_JST_PIN2_UNBLOCK: + /* reply job with data */ + gsm_sim_reply(ms, SIM_JOB_OK, data, length); + msgb_free(msg); + return 0; + } + + LOGP(DSIM, LOGL_ERROR, "unknown state %u, please fix!\n", + sim->job_state); + goto request_error; +} + +/* + * API + */ + +/* open access to sim */ +uint32_t sim_open(struct osmocom_ms *ms, + void (*cb)(struct osmocom_ms *ms, struct msgb *msg)) +{ + struct gsm_sim *sim = &ms->sim; + struct gsm_sim_handler *handler; + + /* create handler and attach */ + handler = talloc_zero(l23_ctx, struct gsm_sim_handler); + if (!handler) + return 0; + handler->handle = new_handle++; + handler->cb = cb; + llist_add_tail(&handler->entry, &sim->handlers); + + return handler->handle; +} + +/* close access to sim */ +void sim_close(struct osmocom_ms *ms, uint32_t handle) +{ + struct gsm_sim *sim = &ms->sim; + struct gsm_sim_handler *handler; + + handler = sim_get_handler(sim, handle); + if (!handle) + return; + + /* kill ourself */ + llist_del(&handler->entry); + talloc_free(handler); +} + +/* send job */ +void sim_job(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_sim *sim = &ms->sim; + + msgb_enqueue(&sim->jobs, msg); +} + +/* + * init + */ + +int gsm_sim_init(struct osmocom_ms *ms) +{ + struct gsm_sim *sim = &ms->sim; + + /* current path is undefined, forching MF */ + sim->path[0] = 0x0bad; + sim->path[1] = 0; + sim->file = 0; + + INIT_LLIST_HEAD(&sim->handlers); + INIT_LLIST_HEAD(&sim->jobs); + + LOGP(DSIM, LOGL_INFO, "init SIM client\n"); + + return 0; +} + +int gsm_sim_exit(struct osmocom_ms *ms) +{ + struct gsm_sim *sim = &ms->sim; + struct gsm_sim_handler *handler, *handler2; + struct msgb *msg; + + LOGP(DSIM, LOGL_INFO, "exit SIM client\n"); + + /* remove pending job msg */ + if (sim->job_msg) { + msgb_free(sim->job_msg); + sim->job_msg = NULL; + } + /* flush handlers */ + llist_for_each_entry_safe(handler, handler2, &sim->handlers, entry) + sim_close(ms, handler->handle); + /* flush jobs */ + while ((msg = msgb_dequeue(&sim->jobs))) + msgb_free(msg); + + return 0; +} + + + + diff --git a/src/host/layer23/src/common/sysinfo.c b/src/host/layer23/src/common/sysinfo.c new file mode 100644 index 00000000..8f6b1b58 --- /dev/null +++ b/src/host/layer23/src/common/sysinfo.c @@ -0,0 +1,773 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> + +#include <osmocore/bitvec.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/networks.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/sysinfo.h> + +#define MIN(a, b) ((a < b) ? a : b) + +/* + * dumping + */ + +int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, + void (*print)(void *, const char *, ...), void *priv) +{ + char buffer[81]; + int i, j, k; + + /* available sysinfos */ + print(priv, "ARFCN = %d\n", arfcn); + print(priv, "Available SYSTEM INFORMATIONS ="); + if (s->si1) + print(priv, " 1"); + if (s->si2) + print(priv, " 2"); + if (s->si2bis) + print(priv, " 2bis"); + if (s->si2ter) + print(priv, " 2ter"); + if (s->si3) + print(priv, " 3"); + if (s->si4) + print(priv, " 4"); + if (s->si5) + print(priv, " 5"); + if (s->si5bis) + print(priv, " 5bis"); + if (s->si5ter) + print(priv, " 5ter"); + if (s->si6) + print(priv, " 6"); + print(priv, "\n"); + print(priv, "\n"); + + /* frequency list */ + j = 0; k = 0; + for (i = 0; i < 1024; i++) { + if ((s->freq[i].mask & FREQ_TYPE_SERV)) { + if (!k) { + sprintf(buffer, "serv. cell : "); + j = strlen(buffer); + } + if (j >= 75) { + buffer[j - 1] = '\0'; + print(priv, "%s\n", buffer); + sprintf(buffer, " "); + j = strlen(buffer); + } + sprintf(buffer + j, "%d,", i); + j = strlen(buffer); + k++; + } + } + if (j) { + buffer[j - 1] = '\0'; + print(priv, "%s\n", buffer); + } + j = 0; k = 0; + for (i = 0; i < 1024; i++) { + if ((s->freq[i].mask & FREQ_TYPE_NCELL)) { + if (!k) { + sprintf(buffer, "SI2 (neigh.) BA=%d: ", + s->nb_ba_ind_si2); + j = strlen(buffer); + } + if (j >= 70) { + buffer[j - 1] = '\0'; + print(priv, "%s\n", buffer); + sprintf(buffer, " "); + j = strlen(buffer); + } + sprintf(buffer + j, "%d,", i); + j = strlen(buffer); + k++; + } + } + if (j) { + buffer[j - 1] = '\0'; + print(priv, "%s\n", buffer); + } + j = 0; k = 0; + for (i = 0; i < 1024; i++) { + if ((s->freq[i].mask & FREQ_TYPE_REP)) { + if (!k) { + sprintf(buffer, "SI5 (report) BA=%d: ", + s->nb_ba_ind_si5); + j = strlen(buffer); + } + if (j >= 70) { + buffer[j - 1] = '\0'; + print(priv, "%s\n", buffer); + sprintf(buffer, " "); + j = strlen(buffer); + } + sprintf(buffer + j, "%d,", i); + j = strlen(buffer); + k++; + } + } + if (j) { + buffer[j - 1] = '\0'; + print(priv, "%s\n", buffer); + } + print(priv, "\n"); + + /* frequency map */ + for (i = 0; i < 1024; i += 64) { + if (i < 10) + sprintf(buffer, " %d ", i); + else if (i < 100) + sprintf(buffer, " %d ", i); + else + sprintf(buffer, " %d ", i); + for (j = 0; j < 64; j++) { + if ((s->freq[i+j].mask & FREQ_TYPE_SERV)) + buffer[j + 5] = 'S'; + else if ((s->freq[i+j].mask & FREQ_TYPE_NCELL) + && (s->freq[i+j].mask & FREQ_TYPE_REP)) + buffer[j + 5] = 'b'; + else if ((s->freq[i+j].mask & FREQ_TYPE_NCELL)) + buffer[j + 5] = 'n'; + else if ((s->freq[i+j].mask & FREQ_TYPE_REP)) + buffer[j + 5] = 'r'; + else + buffer[j + 5] = '.'; + } + sprintf(buffer + 69, " %d", i + 63); + print(priv, "%s\n", buffer); + } + print(priv, " 'S' = serv. cell 'n' = SI2 (neigh.) 'r' = SI5 (rep.) " + "'b' = SI2+SI5\n\n"); + + /* serving cell */ + print(priv, "Serving Cell:\n"); + print(priv, " BSIC = %d,%d MCC = %s MNC = %s LAC = 0x%04x Cell ID " + "= 0x%04x\n", s->bsic >> 3, s->bsic & 0x7, + gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac, + s->cell_id); + print(priv, " Country = %s Network Name = %s\n", gsm_get_mcc(s->mcc), + gsm_get_mnc(s->mcc, s->mnc)); + print(priv, " MAX_RETRANS = %d TX_INTEGER = %d re-establish = %s\n", + s->max_retrans, s->tx_integer, + (s->reest_denied) ? "denied" : "allowed"); + print(priv, " Cell barred = %s barred classes =", + (s->cell_barr ? "yes" : "no")); + for (i = 0; i < 16; i++) { + if ((s->class_barr & (1 << i))) + print(priv, " C%d", i); + } + print(priv, "\n"); + if (s->sp) + print(priv, " CBQ = %d CRO = %d TEMP_OFFSET = %d " + "PENALTY_TIME = %d\n", s->sp_cbq, s->sp_cro, s->sp_to, + s->sp_pt); + if (s->nb_ncc_permitted_si2) { + print(priv, "NCC Permitted BCCH ="); + for (i = 0; i < 8; i++) + if ((s->nb_ncc_permitted_si2 & (1 << i))) + print(priv, " %d", i); + print(priv, "\n"); + } + if (s->nb_ncc_permitted_si6) { + print(priv, "NCC Permitted SACCH/TCH ="); + for (i = 0; i < 8; i++) + if ((s->nb_ncc_permitted_si6 & (1 << i))) + print(priv, " %d", i); + print(priv, "\n"); + } + print(priv, "\n"); + + /* neighbor cell */ + print(priv, "Neighbor Cell:\n"); + print(priv, " MAX_RETRANS = %d TX_INTEGER = %d re-establish = %s\n", + s->nb_max_retrans, s->nb_tx_integer, + (s->nb_reest_denied) ? "denied" : "allowed"); + print(priv, " Cell barred = %s barred classes =", + (s->nb_cell_barr ? "yes" : "no")); + for (i = 0; i < 16; i++) { + if ((s->nb_class_barr & (1 << i))) + print(priv, " C%d", i); + } + print(priv, "\n"); + print(priv, "\n"); + + /* cell selection */ + print(priv, "MX_TXPWR_MAX_CCCH = %d CRH = %d RXLEV_MIN = %d " + "NECI = %d ACS = %d\n", s->ms_txpwr_max_cch, + s->cell_resel_hyst_db, s->rxlev_acc_min_db, s->neci, s->acs); + + /* bcch options */ + print(priv, "BCCH link timeout = %d DTX = %d PWRC = %d\n", + s->bcch_radio_link_timeout, s->bcch_dtx, s->bcch_pwrc); + + /* sacch options */ + print(priv, "SACCH link timeout = %d DTX = %d PWRC = %d\n", + s->sacch_radio_link_timeout, s->sacch_dtx, s->sacch_pwrc); + + /* control channel */ + switch(s->ccch_conf) { + case 0: + print(priv, "CCCH Config = 1 CCCH"); + break; + case 1: + print(priv, "CCCH Config = 1 CCCH + SDCCH"); + break; + case 2: + print(priv, "CCCH Config = 2 CCCH"); + break; + case 4: + print(priv, "CCCH Config = 3 CCCH"); + break; + case 6: + print(priv, "CCCH Config = 4 CCCH"); + break; + default: + print(priv, "CCCH Config = reserved"); + } + print(priv, " BS-PA-MFMS = %d Attachment = %s\n", + s->pag_mf_periods, (s->att_allowed) ? "allowed" : "denied"); + print(priv, "BS-AG_BLKS_RES = %d\n", s->bs_ag_blks_res); + + /* channel description */ + if (s->h) + print(priv, "chan_nr = 0x%02x TSC = %d MAIO = %d HSN = %d\n", + s->chan_nr, s->tsc, s->maio, s->hsn); + else + print(priv, "chan_nr = 0x%02x TSC = %d ARFCN = %d\n", + s->chan_nr, s->tsc, s->arfcn); + print(priv, "\n"); + + return 0; +} + +/* + * decoding + */ + +int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc, + uint16_t *mnc, uint16_t *lac) +{ + *mcc = ((lai->digits[0] & 0x0f) << 8) + | (lai->digits[0] & 0xf0) + | (lai->digits[1] & 0x0f); + *mnc = ((lai->digits[2] & 0x0f) << 8) + | (lai->digits[2] & 0xf0) + | ((lai->digits[1] & 0xf0) >> 4); + *lac = ntohs(lai->lac); + + return 0; +} + +int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc, + uint16_t *arfcn) +{ + *tsc = cd->h0.tsc; + *arfcn = cd->h0.arfcn_low | (cd->h0.arfcn_high << 8); + + return 0; +} + +int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc, + uint8_t *maio, uint8_t *hsn) +{ + *tsc = cd->h1.tsc; + *maio = cd->h1.maio_low | (cd->h1.maio_high << 2); + *hsn = cd->h1.hsn; + + return 0; +} + +/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */ +static int decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd, + uint8_t len, uint8_t mask, uint8_t frqt) +{ +#if 0 + /* only Bit map 0 format for P-GSM */ + if ((cd[0] & 0xc0 & mask) != 0x00 && + (set->p_gsm && !set->e_gsm && !set->r_gsm && !set->dcs)) + return 0; +#endif + + return gsm48_decode_freq_list(f, cd, len, mask, frqt); +} + +/* decode "Cell Selection Parameters" (10.5.2.4) */ +static int gsm48_decode_cell_sel_param(struct gsm48_sysinfo *s, + struct gsm48_cell_sel_par *cs) +{ +#ifdef TODO + convert ms_txpwr_max_ccch dependant on the current frequenc and support + to the right powe level +#endif + s->ms_txpwr_max_cch = cs->ms_txpwr_max_ccch; + s->cell_resel_hyst_db = cs->cell_resel_hyst * 2; + s->rxlev_acc_min_db = cs->rxlev_acc_min - 110; + s->neci = cs->neci; + s->acs = cs->acs; + + return 0; +} + +/* decode "Cell Options (BCCH)" (10.5.2.3) */ +static int gsm48_decode_cellopt_bcch(struct gsm48_sysinfo *s, + struct gsm48_cell_options *co) +{ + s->bcch_radio_link_timeout = (co->radio_link_timeout + 1) * 4; + s->bcch_dtx = co->dtx; + s->bcch_pwrc = co->pwrc; + + return 0; +} + +/* decode "Cell Options (SACCH)" (10.5.2.3a) */ +static int gsm48_decode_cellopt_sacch(struct gsm48_sysinfo *s, + struct gsm48_cell_options *co) +{ + s->sacch_radio_link_timeout = (co->radio_link_timeout + 1) * 4; + s->sacch_dtx = co->dtx; + s->sacch_pwrc = co->pwrc; + + return 0; +} + +/* decode "Control Channel Description" (10.5.2.11) */ +static int gsm48_decode_ccd(struct gsm48_sysinfo *s, + struct gsm48_control_channel_descr *cc) +{ + s->ccch_conf = cc->ccch_conf; + s->bs_ag_blks_res = cc->bs_ag_blks_res; + s->att_allowed = cc->att; + s->pag_mf_periods = cc->bs_pa_mfrms + 2; + s->t3212 = cc->t3212 * 360; /* convert deci-hours to seconds */ + + return 0; +} + +/* decode "Mobile Allocation" (10.5.2.21) */ +int gsm48_decode_mobile_alloc(struct gsm_sysinfo_freq *freq, + uint8_t *ma, uint8_t len, uint16_t *hopping, uint8_t *hopp_len, int si4) +{ + int i, j = 0; + uint16_t f[len << 3]; + + /* not more than 64 hopping indexes allowed in IE */ + if (len > 8) + return -EINVAL; + + /* tabula rasa */ + *hopp_len = 0; + if (si4) { + for (i = 0; i < 1024; i++) + freq[i].mask &= ~FREQ_TYPE_HOPP; + } + + /* generating list of all frequencies (1..1023,0) */ + for (i = 1; i <= 1024; i++) { + if ((freq[i & 1023].mask & FREQ_TYPE_SERV)) { + LOGP(DRR, LOGL_INFO, "Serving cell ARFCN #%d: %d\n", + j, i & 1023); + f[j++] = i & 1023; + if (j == (len << 3)) + break; + } + } + + /* fill hopping table with frequency index given by IE + * and set hopping type bits + */ + for (i = 0; i < (len << 3); i++) { + /* if bit is set, this frequency index is used for hopping */ + if ((ma[len - 1 - (i >> 3)] & (1 << (i & 7)))) { + LOGP(DRR, LOGL_INFO, "Hopping ARFCN: %d (bit %d)\n", + i, f[i]); + /* index higher than entries in list ? */ + if (i >= j) { + LOGP(DRR, LOGL_NOTICE, "Mobile Allocation " + "hopping index %d exceeds maximum " + "number of cell frequencies. (%d)\n", + i + 1, j); + break; + } + hopping[(*hopp_len)++] = f[i]; + if (si4) + freq[f[i]].mask |= FREQ_TYPE_HOPP; + } + } + + return 0; +} + +/* Rach Control decode tables */ +static uint8_t gsm48_max_retrans[4] = { + 1, 2, 4, 7 +}; +static uint8_t gsm48_tx_integer[16] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50 +}; + +/* decode "RACH Control Parameter" (10.5.2.29) */ +static int gsm48_decode_rach_ctl_param(struct gsm48_sysinfo *s, + struct gsm48_rach_control *rc) +{ + s->reest_denied = rc->re; + s->cell_barr = rc->cell_bar; + s->tx_integer = gsm48_tx_integer[rc->tx_integer]; + s->max_retrans = gsm48_max_retrans[rc->max_trans]; + s->class_barr = (rc->t2 << 8) | rc->t3; + + return 0; +} +static int gsm48_decode_rach_ctl_neigh(struct gsm48_sysinfo *s, + struct gsm48_rach_control *rc) +{ + s->nb_reest_denied = rc->re; + s->nb_cell_barr = rc->cell_bar; + s->nb_tx_integer = gsm48_tx_integer[rc->tx_integer]; + s->nb_max_retrans = gsm48_max_retrans[rc->max_trans]; + s->nb_class_barr = (rc->t2 << 8) | rc->t3; + + return 0; +} + +/* decode "SI 1 Rest Octets" (10.5.2.32) */ +static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si, + uint8_t len) +{ + return 0; +} + +/* decode "SI 3 Rest Octets" (10.5.2.34) */ +static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, uint8_t *si, + uint8_t len) +{ + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data_len = len; + bv.data = si; + + /* Optional Selection Parameters */ + if (bitvec_get_bit_high(&bv) == H) { + s->sp = 1; + s->sp_cbq = bitvec_get_uint(&bv, 1); + s->sp_cro = bitvec_get_uint(&bv, 6); + s->sp_to = bitvec_get_uint(&bv, 3); + s->sp_pt = bitvec_get_uint(&bv, 5); + } + /* Optional Power Offset */ + if (bitvec_get_bit_high(&bv) == H) { + s->po = 1; + s->po_value = bitvec_get_uint(&bv, 3); + } + /* System Onformation 2ter Indicator */ + if (bitvec_get_bit_high(&bv) == H) + s->si2ter_ind = 1; + /* Early Classark Sending Control */ + if (bitvec_get_bit_high(&bv) == H) + s->ecsm = 1; + /* Scheduling if and where */ + if (bitvec_get_bit_high(&bv) == H) { + s->sched = 1; + s->sched_where = bitvec_get_uint(&bv, 3); + } + /* GPRS Indicator */ + s->gi_ra_colour = bitvec_get_uint(&bv, 3); + s->gi_si13_pos = bitvec_get_uint(&bv, 1); + return 0; +} + +/* decode "SI 4 Rest Octets" (10.5.2.35) */ +static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s, uint8_t *si, + uint8_t len) +{ + return 0; +} + +/* decode "SI 6 Rest Octets" (10.5.2.35a) */ +static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s, uint8_t *si, + uint8_t len) +{ + return 0; +} + +int gsm48_decode_sysinfo1(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_1 *si, int len) +{ + int payload_len = len - sizeof(*si); + + memcpy(s->si1_msg, si, MIN(len, sizeof(s->si1_msg))); + + /* Cell Channel Description */ + decode_freq_list(s->freq, si->cell_channel_description, + sizeof(si->cell_channel_description), 0xce, FREQ_TYPE_SERV); + /* RACH Control Parameter */ + gsm48_decode_rach_ctl_param(s, &si->rach_control); + /* SI 1 Rest Octets */ + if (payload_len) + gsm48_decode_si1_rest(s, si->rest_octets, payload_len); + + if (!s->si1 && s->si4) { + s->si1 = 1; + LOGP(DRR, LOGL_NOTICE, "Now decoding previously received " + "SYSTEM INFORMATION 4\n"); + gsm48_decode_sysinfo4(s, + (struct gsm48_system_information_type_4 *) s->si4_msg, + sizeof(s->si4_msg)); + + return -EBUSY; + } + + s->si1 = 1; + + return 0; +} + +int gsm48_decode_sysinfo2(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_2 *si, int len) +{ + memcpy(s->si2_msg, si, MIN(len, sizeof(s->si2_msg))); + + /* Neighbor Cell Description */ + s->nb_ext_ind_si2 = (si->bcch_frequency_list[0] >> 6) & 1; + s->nb_ba_ind_si2 = (si->bcch_frequency_list[0] >> 5) & 1; + decode_freq_list(s->freq, si->bcch_frequency_list, + sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2); + /* NCC Permitted */ + s->nb_ncc_permitted_si2 = si->ncc_permitted; + /* RACH Control Parameter */ + gsm48_decode_rach_ctl_neigh(s, &si->rach_control); + + s->si2 = 1; + + return 0; +} + +int gsm48_decode_sysinfo2bis(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_2bis *si, int len) +{ + memcpy(s->si2b_msg, si, MIN(len, sizeof(s->si2b_msg))); + + /* Neighbor Cell Description */ + s->nb_ext_ind_si2bis = (si->bcch_frequency_list[0] >> 6) & 1; + s->nb_ba_ind_si2bis = (si->bcch_frequency_list[0] >> 5) & 1; + decode_freq_list(s->freq, si->bcch_frequency_list, + sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2bis); + /* RACH Control Parameter */ + gsm48_decode_rach_ctl_neigh(s, &si->rach_control); + + s->si2bis = 1; + + return 0; +} + +int gsm48_decode_sysinfo2ter(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_2ter *si, int len) +{ + memcpy(s->si2t_msg, si, MIN(len, sizeof(s->si2t_msg))); + + /* Neighbor Cell Description 2 */ + s->nb_multi_rep_si2ter = (si->ext_bcch_frequency_list[0] >> 6) & 3; + s->nb_ba_ind_si2ter = (si->ext_bcch_frequency_list[0] >> 5) & 1; + decode_freq_list(s->freq, si->ext_bcch_frequency_list, + sizeof(si->ext_bcch_frequency_list), 0x8e, + FREQ_TYPE_NCELL_2ter); + + s->si2ter = 1; + + return 0; +} + +int gsm48_decode_sysinfo3(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_3 *si, int len) +{ + int payload_len = len - sizeof(*si); + + memcpy(s->si3_msg, si, MIN(len, sizeof(s->si3_msg))); + + /* Cell Identity */ + s->cell_id = ntohs(si->cell_identity); + /* LAI */ + gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac); + /* Control Channel Description */ + gsm48_decode_ccd(s, &si->control_channel_desc); + /* Cell Options (BCCH) */ + gsm48_decode_cellopt_bcch(s, &si->cell_options); + /* Cell Selection Parameters */ + gsm48_decode_cell_sel_param(s, &si->cell_sel_par); + /* RACH Control Parameter */ + gsm48_decode_rach_ctl_param(s, &si->rach_control); + /* SI 3 Rest Octets */ + if (payload_len >= 4) + gsm48_decode_si3_rest(s, si->rest_octets, payload_len); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 3 (mcc %s mnc %s " + "lac 0x%04x)\n", gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); + + s->si3 = 1; + + return 0; +} + +int gsm48_decode_sysinfo4(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_4 *si, int len) +{ + int payload_len = len - sizeof(*si); + + uint8_t *data = si->data; + struct gsm48_chan_desc *cd; + + memcpy(s->si4_msg, si, MIN(len, sizeof(s->si4_msg))); + + if (!s->si1) { + LOGP(DRR, LOGL_NOTICE, "Ignoring SYSTEM INFORMATION 4 " + "until SI 1 is received.\n"); + s->si4 = 1; + return -EBUSY; + } + /* LAI */ + gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac); + /* Cell Selection Parameters */ + gsm48_decode_cell_sel_param(s, &si->cell_sel_par); + /* RACH Control Parameter */ + gsm48_decode_rach_ctl_param(s, &si->rach_control); + /* CBCH Channel Description */ + if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_CHAN_DESC) { + if (payload_len < 4) { +short_read: + LOGP(DRR, LOGL_NOTICE, "Short read!\n"); + return -EIO; + } + cd = (struct gsm48_chan_desc *) (data + 1); + s->chan_nr = cd->chan_nr; + if (cd->h0.h) { + s->h = 1; + gsm48_decode_chan_h1(cd, &s->tsc, &s->maio, &s->hsn); + } else { + s->h = 0; + gsm48_decode_chan_h0(cd, &s->tsc, &s->arfcn); + } + payload_len -= 4; + data += 4; + } + /* CBCH Mobile Allocation */ + if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_MOB_AL) { + if (payload_len < 1 || payload_len < 2 + data[1]) + goto short_read; + gsm48_decode_mobile_alloc(s->freq, data + 2, data[1], + s->hopping, &s->hopp_len, 1); + payload_len -= 2 + data[1]; + data += 2 + data[1]; + } + /* SI 4 Rest Octets */ + if (payload_len > 0) + gsm48_decode_si4_rest(s, data, payload_len); + + s->si4 = 1; + + return 0; +} + +int gsm48_decode_sysinfo5(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_5 *si, int len) +{ + memcpy(s->si5_msg, si, MIN(len, sizeof(s->si5_msg))); + + /* Neighbor Cell Description */ + s->nb_ext_ind_si5 = (si->bcch_frequency_list[0] >> 6) & 1; + s->nb_ba_ind_si5 = (si->bcch_frequency_list[0] >> 5) & 1; + decode_freq_list(s->freq, si->bcch_frequency_list, + sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5); + + s->si5 = 1; + + return 0; +} + +int gsm48_decode_sysinfo5bis(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_5bis *si, int len) +{ + memcpy(s->si5b_msg, si, MIN(len, sizeof(s->si5b_msg))); + + /* Neighbor Cell Description */ + s->nb_ext_ind_si5bis = (si->bcch_frequency_list[0] >> 6) & 1; + s->nb_ba_ind_si5bis = (si->bcch_frequency_list[0] >> 5) & 1; + decode_freq_list(s->freq, si->bcch_frequency_list, + sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5bis); + + s->si5bis = 1; + + return 0; +} + +int gsm48_decode_sysinfo5ter(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_5ter *si, int len) +{ + memcpy(s->si5t_msg, si, MIN(len, sizeof(s->si5t_msg))); + + /* Neighbor Cell Description */ + s->nb_multi_rep_si5ter = (si->bcch_frequency_list[0] >> 6) & 3; + s->nb_ba_ind_si5ter = (si->bcch_frequency_list[0] >> 5) & 1; + decode_freq_list(s->freq, si->bcch_frequency_list, + sizeof(si->bcch_frequency_list), 0x8e, FREQ_TYPE_REP_5ter); + + s->si5ter = 1; + + return 0; +} + +int gsm48_decode_sysinfo6(struct gsm48_sysinfo *s, + struct gsm48_system_information_type_6 *si, int len) +{ + int payload_len = len - sizeof(*si); + + memcpy(s->si6_msg, si, MIN(len, sizeof(s->si6_msg))); + + /* Cell Identity */ + if (s->si6 && s->cell_id != ntohs(si->cell_identity)) + LOGP(DRR, LOGL_INFO, "Cell ID on SI 6 differs from previous " + "read.\n"); + s->cell_id = ntohs(si->cell_identity); + /* LAI */ + gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac); + /* Cell Options (SACCH) */ + gsm48_decode_cellopt_sacch(s, &si->cell_options); + /* NCC Permitted */ + s->nb_ncc_permitted_si6 = si->ncc_permitted; + /* SI 6 Rest Octets */ + if (payload_len >= 4) + gsm48_decode_si6_rest(s, si->rest_octets, payload_len); + + s->si6 = 1; + + return 0; +} + diff --git a/src/host/layer23/src/misc/Makefile.am b/src/host/layer23/src/misc/Makefile.am new file mode 100644 index 00000000..f8a0b737 --- /dev/null +++ b/src/host/layer23/src/misc/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) +LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) + +bin_PROGRAMS = bcch_scan layer23 echo_test cell_log cbch_sniff + +bcch_scan_SOURCES = ../common/main.c app_bcch_scan.c bcch_scan.c +layer23_SOURCES = ../common/main.c app_phone.c layer3.c rslms.c +echo_test_SOURCES = ../common/main.c app_echo_test.c +cell_log_LDADD = $(LDADD) -lm +cell_log_SOURCES = ../common/main.c app_cell_log.c cell_log.c \ + ../../../gsmmap/geo.c +cbch_sniff_SOURCES = ../common/main.c app_cbch_sniff.c diff --git a/src/host/layer23/src/misc/app_bcch_scan.c b/src/host/layer23/src/misc/app_bcch_scan.c new file mode 100644 index 00000000..cce22b3b --- /dev/null +++ b/src/host/layer23/src/misc/app_bcch_scan.c @@ -0,0 +1,68 @@ +/* "Application" code of the layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/misc/layer3.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/signal.h> + +static int signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_RESET: + ms = signal_data; + return fps_start(ms); + } + return 0; +} + +int l23_app_init(struct osmocom_ms *ms) +{ + /* don't do layer3_init() as we don't want an actualy L3 */ + fps_init(ms); + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + return register_signal_handler(SS_L1CTL, &signal_cb, NULL); +} + +static struct l23_app_info info = { + .copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n", + .contribution = "Contributions by Holger Hans Peter Freyther\n", +}; + +struct l23_app_info *l23_app_info() +{ + return &info; +} diff --git a/src/host/layer23/src/misc/app_cbch_sniff.c b/src/host/layer23/src/misc/app_cbch_sniff.c new file mode 100644 index 00000000..9e2d1a99 --- /dev/null +++ b/src/host/layer23/src/misc/app_cbch_sniff.c @@ -0,0 +1,199 @@ +/* CBCH passive sniffer */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Alex Badea <vamposdecampos@gmail.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/misc/layer3.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/signal.h> +#include <osmocore/rsl.h> + +struct osmocom_ms *g_ms; +struct gsm48_sysinfo g_sysinfo = {}; + +static int try_cbch(struct osmocom_ms *ms, struct gsm48_sysinfo *s) +{ + if (!s->si1 || !s->si4) + return 0; + if (!s->chan_nr) { + LOGP(DRR, LOGL_INFO, "no CBCH chan_nr found\n"); + return 0; + } + + if (s->h) { + LOGP(DRR, LOGL_INFO, "chan_nr = 0x%02x TSC = %d MAIO = %d " + "HSN = %d hseq (%d): %s\n", + s->chan_nr, s->tsc, s->maio, s->hsn, + s->hopp_len, + hexdump((unsigned char *) s->hopping, s->hopp_len * 2)); + return l1ctl_tx_dm_est_req_h1(ms, + s->maio, s->hsn, s->hopping, s->hopp_len, + s->chan_nr, s->tsc, + GSM48_CMODE_SIGN); + } else { + LOGP(DRR, LOGL_INFO, "chan_nr = 0x%02x TSC = %d ARFCN = %d\n", + s->chan_nr, s->tsc, s->arfcn); + return l1ctl_tx_dm_est_req_h0(ms, s->arfcn, + s->chan_nr, s->tsc, GSM48_CMODE_SIGN); + } +} + + +/* receive BCCH at RR layer */ +static int bcch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_header *sih = msgb_l3(msg); + struct gsm48_sysinfo *s = &g_sysinfo; + + if (msgb_l3len(msg) != 23) { + LOGP(DRR, LOGL_NOTICE, "Invalid BCCH message length\n"); + return -EINVAL; + } + switch (sih->system_information) { + case GSM48_MT_RR_SYSINFO_1: + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 1\n"); + gsm48_decode_sysinfo1(s, + (struct gsm48_system_information_type_1 *) sih, + msgb_l3len(msg)); + return try_cbch(ms, s); + case GSM48_MT_RR_SYSINFO_4: + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4\n"); + gsm48_decode_sysinfo4(s, + (struct gsm48_system_information_type_4 *) sih, + msgb_l3len(msg)); + return try_cbch(ms, s); + default: + return 0; + } +} + +static int unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + struct tlv_parsed tv; + uint8_t ch_type, ch_subch, ch_ts; + + DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", + rllh->chan_nr, rllh->link_id); + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n"); + return -EIO; + } + msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); + + rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts); + switch (ch_type) { + case RSL_CHAN_BCCH: + return bcch(ms, msg); + default: + return 0; + } +} + +static int rcv_rll(struct osmocom_ms *ms, struct msgb *msg) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + int msg_type = rllh->c.msg_type; + + if (msg_type == RSL_MT_UNIT_DATA_IND) { + unit_data_ind(ms, msg); + } else + LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n"); + + msgb_free(msg); + + return 0; +} + +static int rcv_rsl(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + int rc = 0; + + switch (rslh->msg_discr & 0xfe) { + case ABIS_RSL_MDISC_RLL: + rc = rcv_rll(ms, msg); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n", + rslh->msg_discr); + msgb_free(msg); + rc = -EINVAL; + break; + } + + return rc; +} + +static int signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_RESET: + case S_L1CTL_FBSB_ERR: + ms = g_ms; + return l1ctl_tx_fbsb_req(ms, ms->test_arfcn, + L1CTL_FBSB_F_FB01SB, 100, 0, CCCH_MODE_COMBINED); + case S_L1CTL_FBSB_RESP: + return 0; + } + return 0; +} + +int l23_app_init(struct osmocom_ms *ms) +{ + /* don't do layer3_init() as we don't want an actualy L3 */ + + g_ms = ms; + osmol2_register_handler(ms, &rcv_rsl); + + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + /* FIXME: L1CTL_RES_T_FULL doesn't reset dedicated mode + * (if previously set), so we release it here. */ + l1ctl_tx_dm_rel_req(ms); + return register_signal_handler(SS_L1CTL, &signal_cb, NULL); +} + +static struct l23_app_info info = { + .copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n", + .contribution = "Contributions by Holger Hans Peter Freyther\n", +}; + +struct l23_app_info *l23_app_info() +{ + return &info; +} diff --git a/src/host/layer23/src/misc/app_cell_log.c b/src/host/layer23/src/misc/app_cell_log.c new file mode 100644 index 00000000..84f5a83d --- /dev/null +++ b/src/host/layer23/src/misc/app_cell_log.c @@ -0,0 +1,138 @@ +/* "Application" code of the layer2/3 stack */ + +/* (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <signal.h> +#include <stdlib.h> +#include <time.h> +#include <getopt.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/misc/cell_log.h> + +#include <osmocore/talloc.h> +#include <osmocore/utils.h> + +extern struct log_target *stderr_target; +extern void *l23_ctx; + +char *logname = "/var/log/osmocom.log"; +int RACH_MAX = 2; + +int _scan_work(struct osmocom_ms *ms) +{ + return 0; +} + +int _scan_exit(struct osmocom_ms *ms) +{ + /* in case there is a lockup during exit */ + signal(SIGINT, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + + scan_exit(); + + return 0; +} + +int l23_app_init(struct osmocom_ms *ms) +{ + int rc; + + srand(time(NULL)); + +// log_parse_category_mask(stderr_target, "DL1C:DRSL:DRR:DGPS:DSUM"); + log_parse_category_mask(stderr_target, "DSUM"); + log_set_log_level(stderr_target, LOGL_INFO); + + l23_app_work = _scan_work; + l23_app_exit = _scan_exit; + + rc = scan_init(ms); + if (rc) + return rc; + + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + printf("Mobile initialized, please start phone now!\n"); + return 0; +} + +static int l23_cfg_supported() +{ + return L23_OPT_TAP | L23_OPT_DBG; +} + +static int l23_getopt_options(struct option **options) +{ + static struct option opts [] = { + {"logfile", 1, 0, 'l'}, + {"rach", 1, 0, 'r'}, + {"no-rach", 1, 0, 'n'}, + }; + + *options = opts; + return ARRAY_SIZE(opts); +} + +static int l23_cfg_print_help() +{ + printf("\nApplication specific\n"); + printf(" -l --logfile LOGFILE Logfile for the cell log.\n"); + printf(" -r --rach RACH Nr. of RACH bursts to send.\n"); + printf(" -n --no-rach Send no rach bursts.\n"); + return 0; +} + +static int l23_cfg_handle(int c, const char *optarg) +{ + switch (c) { + case 'l': + logname = talloc_strdup(l23_ctx, optarg); + break; + case 'r': + RACH_MAX = atoi(optarg); + break; + case 'n': + RACH_MAX = 0; + break; + } + + return 0; +} + +static struct l23_app_info info = { + .copyright = "Copyright (C) 2010 Andreas Eversberg\n", + .getopt_string = "l:r:n", + .cfg_supported = l23_cfg_supported, + .cfg_getopt_opt = l23_getopt_options, + .cfg_handle_opt = l23_cfg_handle, + .cfg_print_help = l23_cfg_print_help, +}; + +struct l23_app_info *l23_app_info() +{ + return &info; +} diff --git a/src/host/layer23/src/misc/app_echo_test.c b/src/host/layer23/src/misc/app_echo_test.c new file mode 100644 index 00000000..c9b18958 --- /dev/null +++ b/src/host/layer23/src/misc/app_echo_test.c @@ -0,0 +1,66 @@ +/* TEST code, regularly transmit ECHO REQ packet to L1 */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/misc/layer3.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> + + +static struct { + struct timer_list timer; +} test_data; + +static void test_tmr_cb(void *data) +{ + struct osmocom_ms *ms = data; + + l1ctl_tx_echo_req(ms, 62); + bsc_schedule_timer(&test_data.timer, 1, 0); +} + +int l23_app_init(struct osmocom_ms *ms) +{ + test_data.timer.cb = &test_tmr_cb; + test_data.timer.data = ms; + + bsc_schedule_timer(&test_data.timer, 1, 0); + + return 0; +} + +static struct l23_app_info info = { + .copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n", + .contribution = "Contributions by Holger Hans Peter Freyther\n", +}; + +struct l23_app_info *l23_app_info() +{ + return &info; +} diff --git a/src/host/layer23/src/misc/app_phone.c b/src/host/layer23/src/misc/app_phone.c new file mode 100644 index 00000000..85d0da9d --- /dev/null +++ b/src/host/layer23/src/misc/app_phone.c @@ -0,0 +1,72 @@ +/* "Application" code of the layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/misc/layer3.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/signal.h> + +static int signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_RESET: + ms = signal_data; + layer3_app_reset(); + return l1ctl_tx_fbsb_req(ms, ms->test_arfcn, + L1CTL_FBSB_F_FB01SB, 100, 0, + CCCH_MODE_NONE); + break; + } + return 0; +} + + +int l23_app_init(struct osmocom_ms *ms) +{ + register_signal_handler(SS_L1CTL, &signal_cb, NULL); + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + return layer3_init(ms); +} + +static struct l23_app_info info = { + .copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n", + .contribution = "Contributions by Holger Hans Peter Freyther\n", +}; + +struct l23_app_info *l23_app_info() +{ + return &info; +} diff --git a/src/host/layer23/src/misc/bcch_scan.c b/src/host/layer23/src/misc/bcch_scan.c new file mode 100644 index 00000000..dff4dc9b --- /dev/null +++ b/src/host/layer23/src/misc/bcch_scan.c @@ -0,0 +1,318 @@ +/* BCCH Scanning code for OsmocomBB */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <l1ctl_proto.h> + +#include <osmocore/logging.h> +#include <osmocore/talloc.h> +#include <osmocore/signal.h> +#include <osmocore/timer.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/protocol/gsm_04_08.h> +#include <osmocore/protocol/gsm_08_58.h> +#include <osmocore/rsl.h> + +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/common/logging.h> + +/* somewhere in 05.08 */ +#define MAX_CELLS_IN_BA 32 + +/* Information about a single cell / BCCH */ +struct cell_info { + struct llist_head list; + + uint16_t band_arfcn; + uint8_t bsic; + uint8_t rxlev; + + struct { + uint16_t mcc; /* Mobile Country Code */ + uint16_t mnc; /* Mobile Network Code */ + uint16_t lac; /* Location Area Code */ + uint16_t rac; /* Routing Area Code */ + uint16_t cid; /* Cell ID */ + } id; + uint16_t ba_arfcn[MAX_CELLS_IN_BA]; + uint8_t ba_arfcn_num; + + struct { + int32_t fn_delta; /* delta to current L1 fn */ + int16_t qbit_delta; + int16_t afc_delta; + } l1_sync; +}; + +#define AFS_F_PM_DONE 0x01 +#define AFS_F_TESTED 0x02 +#define AFS_F_BCCH 0x04 +struct arfcn_state { + uint8_t rxlev; + uint8_t flags; +}; + +enum bscan_state { + BSCAN_S_NONE, + BSCAN_S_WAIT_DATA, + BSCAN_S_DONE, +}; + +enum fps_state { + FPS_S_NONE, + FPS_S_PM_GSM900, + FPS_S_PM_EGSM900, + FPS_S_PM_GSM1800, + FPS_S_BINFO, +}; + +struct full_power_scan { + /* Full Power Scan */ + enum fps_state fps_state; + struct arfcn_state arfcn_state[1024]; + + struct osmocom_ms *ms; + + /* BCCH info part */ + enum bscan_state state; + struct llist_head cell_list; + struct cell_info *cur_cell; + uint16_t cur_arfcn; + struct timer_list timer; +}; + +static struct full_power_scan fps; + +static int get_next_arfcn(struct full_power_scan *fps) +{ + unsigned int i; + uint8_t best_rxlev = 0; + int best_arfcn = -1; + + for (i = 0; i < ARRAY_SIZE(fps->arfcn_state); i++) { + struct arfcn_state *af = &fps->arfcn_state[i]; + /* skip ARFCN's where we don't have a PM */ + if (!(af->flags & AFS_F_PM_DONE)) + continue; + /* skip ARFCN's that we already tested */ + if (af->flags & AFS_F_TESTED) + continue; + /* if current arfcn_state is better than best so far, + * select it */ + if (af->rxlev > best_rxlev) { + best_rxlev = af->rxlev; + best_arfcn = i; + } + } + printf("arfcn=%d rxlev=%u\n", best_arfcn, best_rxlev); + return best_arfcn; +} + +static struct cell_info *cell_info_alloc(void) +{ + struct cell_info *ci = talloc_zero(NULL, struct cell_info); + return ci; +} + +static void cell_info_free(struct cell_info *ci) +{ + talloc_free(ci); +} + +/* start to scan for one ARFCN */ +static int _cinfo_start_arfcn(unsigned int band_arfcn) +{ + int rc; + + /* ask L1 to try to tune to new ARFCN */ + /* FIXME: decode band */ + rc = l1ctl_tx_fbsb_req(fps.ms, band_arfcn, + L1CTL_FBSB_F_FB01SB, 100, 0, CCCH_MODE_COMBINED); + if (rc < 0) + return rc; + + /* allocate new cell info structure */ + fps.cur_cell = cell_info_alloc(); + fps.cur_arfcn = band_arfcn; + fps.cur_cell->band_arfcn = band_arfcn; + /* FIXME: start timer in case we never get a sync */ + fps.state = BSCAN_S_WAIT_DATA; + bsc_schedule_timer(&fps.timer, 2, 0); + + return 0; +} + + +static void cinfo_next_cell(void *data) +{ + int rc; + + /* we've been waiting for BCCH info */ + fps.arfcn_state[fps.cur_arfcn].flags |= AFS_F_TESTED; + /* if there is a BCCH, we need to add the collected BCCH + * information to our list */ + + if (fps.arfcn_state[fps.cur_arfcn].flags & AFS_F_BCCH) + llist_add(&fps.cur_cell->list, &fps.cell_list); + else + cell_info_free(fps.cur_cell); + + rc = get_next_arfcn(&fps); + if (rc < 0) { + fps.state = BSCAN_S_DONE; + return; + } + /* start syncing to the next ARFCN */ + _cinfo_start_arfcn(rc); +} + +static void cinfo_timer_cb(void *data) +{ + switch (fps.state) { + case BSCAN_S_WAIT_DATA: + cinfo_next_cell(data); + break; + } +} + +/* Update cell_info for current cell with received BCCH info */ +static int rx_bcch_info(const uint8_t *data) +{ + struct cell_info *ci = fps.cur_cell; + struct gsm48_system_information_type_header *si_hdr; + si_hdr = (struct gsm48_system_information_type_header *) data;; + + /* we definitely have a BCCH on this channel */ + fps.arfcn_state[ci->band_arfcn].flags |= AFS_F_BCCH; + + switch (si_hdr->system_information) { + case GSM48_MT_RR_SYSINFO_1: + /* FIXME: CA, RACH control */ + break; + case GSM48_MT_RR_SYSINFO_2: + /* FIXME: BA, NCC, RACH control */ + break; + case GSM48_MT_RR_SYSINFO_3: + /* FIXME: cell_id, LAI */ + break; + case GSM48_MT_RR_SYSINFO_4: + /* FIXME: LAI */ + break; + } + return 0; +} + +/* Update L1/SCH information (AFC/QBIT/FN offset, BSIC) */ +static int rx_sch_info() +{ + /* FIXME */ +} + +static int bscan_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct cell_info *ci = fps.cur_cell; + struct osmocom_ms *ms; + struct osmobb_meas_res *mr; + uint16_t arfcn; + int rc; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_PM_RES: + mr = signal_data; + /* check if PM result is for same MS */ + if (fps.ms != mr->ms) + return 0; + arfcn = mr->band_arfcn & 0x3ff; + /* update RxLev and notice that PM was done */ + fps.arfcn_state[arfcn].rxlev = mr->rx_lev; + fps.arfcn_state[arfcn].flags |= AFS_F_PM_DONE; + break; + case S_L1CTL_PM_DONE: + ms = signal_data; + switch (fps.fps_state) { + case FPS_S_PM_GSM900: + fps.fps_state = FPS_S_PM_EGSM900; + return l1ctl_tx_pm_req_range(ms, 955, 1023); + case FPS_S_PM_EGSM900: + fps.fps_state = FPS_S_PM_GSM1800; + return l1ctl_tx_pm_req_range(ms, 512, 885); + case FPS_S_PM_GSM1800: + /* power measurement has finished, we can start + * to actually iterate over the ARFCN's and try + * to sync to BCCHs */ + fps.fps_state = FPS_S_BINFO; + rc = get_next_arfcn(&fps); + if (rc < 0) { + fps.state = BSCAN_S_DONE; + return; + } + _cinfo_start_arfcn(rc); + break; + } + break; + case S_L1CTL_FBSB_RESP: + /* We actually got a FCCH/SCH burst */ +#if 0 + fps.arfcn_state[ci->band_arfcn].flags |= AFS_F_BCCH; + /* fallthrough */ +#else + break; +#endif + case S_L1CTL_FBSB_ERR: + /* We timed out, move on */ + if (fps.state == BSCAN_S_WAIT_DATA) + cinfo_next_cell(NULL); + break; + } + return 0; +} + +/* start the full power scan */ +int fps_start(struct osmocom_ms *ms) +{ + memset(&fps, 0, sizeof(fps)); + fps.ms = ms; + + fps.timer.cb = cinfo_timer_cb; + fps.timer.data = &fps; + + /* Start by scanning the good old GSM900 band */ + fps.fps_state = FPS_S_PM_GSM900; + return l1ctl_tx_pm_req_range(ms, 0, 124); +} + +int fps_init(void) +{ + return register_signal_handler(SS_L1CTL, &bscan_sig_cb, NULL); +} diff --git a/src/host/layer23/src/misc/cell_log.c b/src/host/layer23/src/misc/cell_log.c new file mode 100644 index 00000000..93616f2a --- /dev/null +++ b/src/host/layer23/src/misc/cell_log.c @@ -0,0 +1,819 @@ +/* Cell Scanning code for OsmocomBB */ + +/* (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <errno.h> + +#include <l1ctl_proto.h> + +#include <osmocore/logging.h> +#include <osmocore/timer.h> +#include <osmocore/signal.h> +#include <osmocore/msgb.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/protocol/gsm_04_08.h> +#include <osmocore/rsl.h> + +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/networks.h> +#include <osmocom/bb/common/gps.h> +#include <osmocom/bb/misc/cell_log.h> +#include "../../../gsmmap/geo.h" + +#define READ_WAIT 2, 0 +#define RACH_WAIT 0, 900000 +#define MIN_RXLEV -106 +#define MAX_DIST 2000 + +enum { + SCAN_STATE_PM, + SCAN_STATE_SYNC, + SCAN_STATE_READ, + SCAN_STATE_RACH, +}; + +/* ranges of bands */ +static uint16_t band_range[][2] = {{0, 124}, {512, 885}, {955, 1023}, {0, 0}}; + +#define INFO_FLG_PM 1 +#define INFO_FLG_SYNC 2 +#define INFO_FLG_SI1 4 +#define INFO_FLG_SI2 8 +#define INFO_FLG_SI2bis 16 +#define INFO_FLG_SI2ter 32 +#define INFO_FLG_SI3 64 +#define INFO_FLG_SI4 128 + +static struct osmocom_ms *ms; +static struct timer_list timer; + +static struct pm_info { + uint16_t flags; + int8_t rxlev; +} pm[1024]; + +static int started = 0; +static int state; +static int8_t min_rxlev = MIN_RXLEV; +static int sync_count; +static int pm_index, pm_gps_valid; +static double pm_gps_x, pm_gps_y, pm_gps_z; +static int arfcn; +static int rach_count; +static FILE *logfp = NULL; +extern char *logname; +extern int RACH_MAX; + + +static struct gsm48_sysinfo sysinfo; + +static struct log_si { + uint16_t flags; + uint8_t bsic; + int8_t rxlev; + uint16_t mcc, mnc, lac, cellid; + uint8_t ta; + double latitude, longitude; +} log_si; + +struct rach_ref { + uint8_t valid; + uint8_t cr; + uint8_t t1, t2, t3; +} rach_ref; + +#define LOGFILE(fmt, args...) \ + fprintf(logfp, fmt, ## args); +#define LOGFLUSH() \ + fflush(logfp); + +static void start_sync(void); +static void start_rach(void); +static void start_pm(void); + +static void log_gps(void) +{ + if (!gps.enable || !gps.valid) + return; + LOGFILE("position %.8f %.8f\n", gps.longitude, gps.latitude); +} + +static void log_time(void) +{ + time_t now; + + if (gps.enable && gps.valid) + now = gps.gmt; + else + time(&now); + LOGFILE("time %lu\n", now); +} + +static void log_frame(char *tag, uint8_t *data) +{ + int i; + + LOGFILE("%s", tag); + for (i = 0; i < 23; i++) + LOGFILE(" %02x", *data++); + LOGFILE("\n"); +} + +static void log_pm(void) +{ + int count = 0, i; + + LOGFILE("[power]\n"); + log_time(); + log_gps(); + for (i = 0; i <= 1023; i++) { + if ((pm[i].flags & INFO_FLG_PM)) { + if (!count) + LOGFILE("arfcn %d", i); + LOGFILE(" %d", pm[i].rxlev); + count++; + if (count == 12) { + LOGFILE("\n"); + count = 0; + } + } else { + if (count) { + LOGFILE("\n"); + count = 0; + } + } + } + if (count) + LOGFILE("\n"); + + LOGFILE("\n"); + LOGFLUSH(); +} + +static void log_sysinfo(void) +{ + struct rx_meas_stat *meas = &ms->meas; + struct gsm48_sysinfo *s = &sysinfo; + int8_t rxlev; + char ta_str[32] = ""; + + if (log_si.ta != 0xff) + sprintf(ta_str, " TA=%d", log_si.ta); + + LOGP(DSUM, LOGL_INFO, "Cell: ARFCN=%d MCC=%s MNC=%s (%s, %s)%s\n", + arfcn, gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), + gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc), ta_str); + + LOGFILE("[sysinfo]\n"); + LOGFILE("arfcn %d\n", s->arfcn); + log_time(); + log_gps(); + LOGFILE("bsic %d,%d\n", s->bsic >> 3, s->bsic & 7); + rxlev = meas->rxlev / meas->frames - 110; + LOGFILE("rxlev %d\n", rxlev); + if (s->si1) + log_frame("si1", s->si1_msg); + if (s->si2) + log_frame("si2", s->si2_msg); + if (s->si2bis) + log_frame("si2bis", s->si2b_msg); + if (s->si2ter) + log_frame("si2ter", s->si2t_msg); + if (s->si3) + log_frame("si3", s->si3_msg); + if (s->si4) + log_frame("si4", s->si4_msg); + if (log_si.ta != 0xff) + LOGFILE("ta %d\n", log_si.ta); + + LOGFILE("\n"); + LOGFLUSH(); +} + +static void timeout_cb(void *arg) +{ + switch (state) { + case SCAN_STATE_READ: + LOGP(DRR, LOGL_INFO, "Timeout reading BCCH\n"); + start_sync(); + break; + case SCAN_STATE_RACH: + LOGP(DRR, LOGL_INFO, "Timeout on RACH\n"); + rach_count++; + start_rach(); + break; + } +} + +static void stop_timer(void) +{ + if (bsc_timer_pending(&timer)) + bsc_del_timer(&timer); +} + +static void start_timer(int sec, int micro) +{ + stop_timer(); + timer.cb = timeout_cb; + timer.data = ms; + bsc_schedule_timer(&timer, sec, micro); +} + +static void start_rach(void) +{ + struct gsm48_sysinfo *s = &sysinfo; + uint8_t chan_req_val, chan_req_mask; + struct msgb *nmsg; + struct abis_rsl_cchan_hdr *ncch; + + if (rach_count == RACH_MAX) { + log_sysinfo(); + start_sync(); + return; + } + + state = SCAN_STATE_RACH; + + if (s->neci) { + chan_req_mask = 0x0f; + chan_req_val = 0x01; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " + "(OTHER with NECI)\n", chan_req_val); + } else { + chan_req_mask = 0x1f; + chan_req_val = 0xe0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OTHER no NECI)\n", + chan_req_val); + } + + rach_ref.valid = 0; + rach_ref.cr = random(); + rach_ref.cr &= chan_req_mask; + rach_ref.cr |= chan_req_val; + + nmsg = msgb_alloc_headroom(RSL_ALLOC_SIZE+RSL_ALLOC_HEADROOM, + RSL_ALLOC_HEADROOM, "GSM 04.06 RSL"); + if (!nmsg) + return; + nmsg->l2h = nmsg->data; + ncch = (struct abis_rsl_cchan_hdr *) msgb_put(nmsg, sizeof(*ncch) + + 4 + 2 + 2); + rsl_init_cchan_hdr(ncch, RSL_MT_CHAN_RQD); + ncch->chan_nr = RSL_CHAN_RACH; + ncch->data[0] = RSL_IE_REQ_REFERENCE; + ncch->data[1] = rach_ref.cr; + ncch->data[2] = (s->ccch_conf == 1) << 7; + ncch->data[3] = 0; + ncch->data[4] = RSL_IE_ACCESS_DELAY; + ncch->data[5] = 0; /* no delay */ + ncch->data[6] = RSL_IE_MS_POWER; + ncch->data[7] = 0; /* full power */ + + start_timer(RACH_WAIT); + + rslms_recvmsg(nmsg, ms); +} + +static void start_sync(void) +{ + int rxlev = -128; + int i, dist = 0; + char dist_str[32] = ""; + + arfcn = 0xffff; + for (i = 0; i <= 1023; i++) { + if ((pm[i].flags & INFO_FLG_PM) + && !(pm[i].flags & INFO_FLG_SYNC)) { + if (pm[i].rxlev > rxlev) { + rxlev = pm[i].rxlev; + arfcn = i; + } + } + } + /* if GPS becomes valid, like after exitting a tunnel */ + if (!pm_gps_valid && gps.valid) { + pm_gps_valid = 1; + geo2space(&pm_gps_x, &pm_gps_y, &pm_gps_z, gps.longitude, + gps.latitude); + } + if (pm_gps_valid && gps.valid) { + double x, y, z; + + geo2space(&x, &y, &z, gps.longitude, gps.latitude); + dist = distinspace(pm_gps_x, pm_gps_y, pm_gps_z, x, y, z); + sprintf(dist_str, " dist %d", (int)dist); + } + if (dist > MAX_DIST || arfcn == 0xffff || rxlev < min_rxlev) { + memset(pm, 0, sizeof(pm)); + pm_index = 0; + sync_count = 0; + start_pm(); + return; + } + pm[arfcn].flags |= INFO_FLG_SYNC; + LOGP(DSUM, LOGL_INFO, "Sync ARFCN %d (rxlev %d, %d syncs " + "left)%s\n", arfcn, pm[arfcn].rxlev, sync_count--, dist_str); + memset(&sysinfo, 0, sizeof(sysinfo)); + sysinfo.arfcn = arfcn; + state = SCAN_STATE_SYNC; + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + l1ctl_tx_fbsb_req(ms, arfcn, L1CTL_FBSB_F_FB01SB, 100, 0, + CCCH_MODE_NONE); +} + +static void start_pm(void) +{ + uint16_t from, to; + + state = SCAN_STATE_PM; + from = band_range[pm_index][0]; + to = band_range[pm_index][1]; + + if (from == 0 && to == 0) { + LOGP(DSUM, LOGL_INFO, "Measurement done\n"); + pm_gps_valid = gps.enable && gps.valid; + if (pm_gps_valid) + geo2space(&pm_gps_x, &pm_gps_y, &pm_gps_z, + gps.longitude, gps.latitude); + log_pm(); + start_sync(); + return; + } + LOGP(DSUM, LOGL_INFO, "Measure from %d to %d\n", from, to); + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + l1ctl_tx_pm_req_range(ms, from, to); +} + +static int signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmobb_meas_res *mr; + struct osmobb_fbsb_res *fr; + uint16_t index; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_PM_RES: + mr = signal_data; + index = mr->band_arfcn & 0x3ff; + pm[index].flags |= INFO_FLG_PM; + pm[index].rxlev = mr->rx_lev - 110; + if (pm[index].rxlev >= min_rxlev) + sync_count++; +// printf("rxlev %d = %d (sync_count %d)\n", index, pm[index].rxlev, sync_count); + break; + case S_L1CTL_PM_DONE: + pm_index++; + start_pm(); + break; + case S_L1CTL_FBSB_RESP: + fr = signal_data; + sysinfo.bsic = fr->bsic; + state = SCAN_STATE_READ; + memset(&ms->meas, 0, sizeof(ms->meas)); + memset(&log_si, 0, sizeof(log_si)); + log_si.flags |= INFO_FLG_SYNC; + log_si.ta = 0xff; /* invalid */ + start_timer(READ_WAIT); + LOGP(DRR, LOGL_INFO, "Synchronized, start reading\n"); + break; + case S_L1CTL_FBSB_ERR: + LOGP(DRR, LOGL_INFO, "Sync failed\n"); + start_sync(); + break; + case S_L1CTL_RESET: + if (started) + break; + started = 1; + memset(pm, 0, sizeof(pm)); + pm_index = 0; + sync_count = 0; + start_pm(); + } + return 0; +} + +static int ta_result(uint8_t ta) +{ + stop_timer(); + + if (ta == 0xff) + LOGP(DSUM, LOGL_INFO, "Got assignment reject\n"); + else { + LOGP(DSUM, LOGL_DEBUG, "Got assignment TA = %d\n", ta); + log_si.ta = ta; + } + + log_sysinfo(); + start_sync(); + + return 0; +} + +/* match request reference agains request */ +static int match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref) +{ + uint8_t ia_t1, ia_t2, ia_t3; + + /* filter confirmed RACH requests only */ + if (rach_ref.valid && ref->ra == rach_ref.cr) { + ia_t1 = ref->t1; + ia_t2 = ref->t2; + ia_t3 = (ref->t3_high << 3) | ref->t3_low; + if (ia_t1 == rach_ref.t1 && ia_t2 == rach_ref.t2 + && ia_t3 == rach_ref.t3) { + LOGP(DRR, LOGL_INFO, "request %02x matches " + "(fn=%d,%d,%d)\n", ref->ra, ia_t1, ia_t2, + ia_t3); + return 1; + } else + LOGP(DRR, LOGL_INFO, "request %02x matches but not " + "frame number (IMM.ASS fn=%d,%d,%d != RACH " + "fn=%d,%d,%d)\n", ref->ra, ia_t1, ia_t2, ia_t3, + rach_ref.t1, rach_ref.t2, rach_ref.t3); + } + + return 0; +} + +/* 9.1.18 IMMEDIATE ASSIGNMENT is received */ +static int imm_ass(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_imm_ass *ia = msgb_l3(msg); + + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n"); + + if (state != SCAN_STATE_RACH) { + LOGP(DRR, LOGL_INFO, "Not for us, no request.\n"); + return 0; + } + + /* request ref */ + if (match_ra(ms, &ia->req_ref)) { + return ta_result(ia->timing_advance); + } + LOGP(DRR, LOGL_INFO, "Request, but not for us.\n"); + + return 0; +} + +/* 9.1.19 IMMEDIATE ASSIGNMENT EXTENDED is received */ +static int imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_imm_ass_ext *ia = msgb_l3(msg); + + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT EXTENDED:\n"); + + if (state != SCAN_STATE_RACH) { + LOGP(DRR, LOGL_INFO, "Not for us, no request.\n"); + return 0; + } + + /* request ref 1 */ + if (match_ra(ms, &ia->req_ref1)) { + return ta_result(ia->timing_advance1); + } + /* request ref 2 */ + if (match_ra(ms, &ia->req_ref2)) { + return ta_result(ia->timing_advance2); + } + LOGP(DRR, LOGL_INFO, "Request, but not for us.\n"); + + return 0; +} + +/* 9.1.20 IMMEDIATE ASSIGNMENT REJECT is received */ +static int imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_imm_ass_rej *ia = msgb_l3(msg); + int i; + struct gsm48_req_ref *req_ref; + + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT REJECT:\n"); + + if (state != SCAN_STATE_RACH) { + LOGP(DRR, LOGL_INFO, "Not for us, no request.\n"); + return 0; + } + + for (i = 0; i < 4; i++) { + /* request reference */ + req_ref = (struct gsm48_req_ref *) + (((uint8_t *)&ia->req_ref1) + i * 4); + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT REJECT " + "(ref 0x%02x)\n", req_ref->ra); + if (match_ra(ms, req_ref)) { + return ta_result(0xff); + } + } + + return 0; +} + +/* receive CCCH at RR layer */ +static int pch_agch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_header *sih = msgb_l3(msg); + + switch (sih->system_information) { + case GSM48_MT_RR_PAG_REQ_1: + case GSM48_MT_RR_PAG_REQ_2: + case GSM48_MT_RR_PAG_REQ_3: + return 0; + case GSM48_MT_RR_IMM_ASS: + return imm_ass(ms, msg); + case GSM48_MT_RR_IMM_ASS_EXT: + return imm_ass_ext(ms, msg); + case GSM48_MT_RR_IMM_ASS_REJ: + return imm_ass_rej(ms, msg); + default: + return -EINVAL; + } +} + +/* check if sysinfo is complete, change to RACH state */ +static int new_sysinfo(void) +{ + struct gsm48_sysinfo *s = &sysinfo; + + /* restart timer */ + start_timer(READ_WAIT); + + /* mandatory */ + if (!s->si1 || !s->si2 || !s->si3 || !s->si4) { + LOGP(DRR, LOGL_INFO, "not all mandatory SI received\n"); + return 0; + } + + /* extended band */ + if (s->nb_ext_ind_si2 && !s->si2bis) { + LOGP(DRR, LOGL_INFO, "extended ba, but si2bis not received\n"); + return 0; + } + + /* 2ter */ + if (s->si2ter_ind && !s->si2ter) { + LOGP(DRR, LOGL_INFO, "si2ter_ind, but si2ter not received\n"); + return 0; + } + + LOGP(DRR, LOGL_INFO, "Sysinfo complete\n"); + + stop_timer(); + + rach_count = 0; + start_rach(); + + return 0; +} + +/* receive BCCH at RR layer */ +static int bcch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_sysinfo *s = &sysinfo; + struct gsm48_system_information_type_header *sih = msgb_l3(msg); + uint8_t ccch_mode; + + if (msgb_l3len(msg) != 23) { + LOGP(DRR, LOGL_NOTICE, "Invalid BCCH message length\n"); + return -EINVAL; + } + switch (sih->system_information) { + case GSM48_MT_RR_SYSINFO_1: + if (!memcmp(sih, s->si1_msg, sizeof(s->si1_msg))) + return 0; + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 1\n"); + gsm48_decode_sysinfo1(s, + (struct gsm48_system_information_type_1 *) sih, + msgb_l3len(msg)); + return new_sysinfo(); + case GSM48_MT_RR_SYSINFO_2: + if (!memcmp(sih, s->si2_msg, sizeof(s->si2_msg))) + return 0; + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2\n"); + gsm48_decode_sysinfo2(s, + (struct gsm48_system_information_type_2 *) sih, + msgb_l3len(msg)); + return new_sysinfo(); + case GSM48_MT_RR_SYSINFO_2bis: + if (!memcmp(sih, s->si2b_msg, sizeof(s->si2b_msg))) + return 0; + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2bis\n"); + gsm48_decode_sysinfo2bis(s, + (struct gsm48_system_information_type_2bis *) sih, + msgb_l3len(msg)); + return new_sysinfo(); + case GSM48_MT_RR_SYSINFO_2ter: + if (!memcmp(sih, s->si2t_msg, sizeof(s->si2t_msg))) + return 0; + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2ter\n"); + gsm48_decode_sysinfo2ter(s, + (struct gsm48_system_information_type_2ter *) sih, + msgb_l3len(msg)); + return new_sysinfo(); + case GSM48_MT_RR_SYSINFO_3: + if (!memcmp(sih, s->si3_msg, sizeof(s->si3_msg))) + return 0; + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 3\n"); + gsm48_decode_sysinfo3(s, + (struct gsm48_system_information_type_3 *) sih, + msgb_l3len(msg)); + ccch_mode = (s->ccch_conf == 1) ? CCCH_MODE_COMBINED : + CCCH_MODE_NON_COMBINED; + LOGP(DRR, LOGL_INFO, "Changing CCCH_MODE to %d\n", ccch_mode); + l1ctl_tx_ccch_mode_req(ms, ccch_mode); + return new_sysinfo(); + case GSM48_MT_RR_SYSINFO_4: + if (!memcmp(sih, s->si4_msg, sizeof(s->si4_msg))) + return 0; + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4\n"); + gsm48_decode_sysinfo4(s, + (struct gsm48_system_information_type_4 *) sih, + msgb_l3len(msg)); + return new_sysinfo(); + default: + return -EINVAL; + } +} + +static int unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + struct tlv_parsed tv; + uint8_t ch_type, ch_subch, ch_ts; + + DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", + rllh->chan_nr, rllh->link_id); + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n"); + return -EIO; + } + msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); + + if (state != SCAN_STATE_READ && state != SCAN_STATE_RACH) { + return -EINVAL; + } + + rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts); + switch (ch_type) { + case RSL_CHAN_PCH_AGCH: + return pch_agch(ms, msg); + case RSL_CHAN_BCCH: + return bcch(ms, msg); +#if 0 + case RSL_CHAN_Bm_ACCHs: + case RSL_CHAN_Lm_ACCHs: + case RSL_CHAN_SDCCH4_ACCH: + case RSL_CHAN_SDCCH8_ACCH: + return rx_acch(ms, msg); +#endif + default: + LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n", + rllh->chan_nr); + return -EINVAL; + } +} + +static int rcv_rll(struct osmocom_ms *ms, struct msgb *msg) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + int msg_type = rllh->c.msg_type; + + if (msg_type == RSL_MT_UNIT_DATA_IND) { + unit_data_ind(ms, msg); + } else + LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n"); + + msgb_free(msg); + + return 0; +} + +int chan_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct abis_rsl_cchan_hdr *ch = msgb_l2(msg); + struct gsm48_req_ref *ref = (struct gsm48_req_ref *) (ch->data + 1); + + if (msgb_l2len(msg) < sizeof(*ch) + sizeof(*ref)) { + LOGP(DRR, LOGL_ERROR, "CHAN_CNF too slort\n"); + return -EINVAL; + } + + rach_ref.valid = 1; + rach_ref.t1 = ref->t1; + rach_ref.t2 = ref->t2; + rach_ref.t3 = ref->t3_low | (ref->t3_high << 3); + + return 0; +} + +static int rcv_cch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct abis_rsl_cchan_hdr *ch = msgb_l2(msg); + int msg_type = ch->c.msg_type; + int rc; + + LOGP(DRSL, LOGL_INFO, "Received '%s' from layer1\n", + get_rsl_name(msg_type)); + + if (state == SCAN_STATE_RACH && msg_type == RSL_MT_CHAN_CONF) { + rc = chan_conf(ms, msg); + msgb_free(msg); + return rc; + } + + LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n"); + msgb_free(msg); + return 0; +} + +static int rcv_rsl(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + int rc = 0; + + switch (rslh->msg_discr & 0xfe) { + case ABIS_RSL_MDISC_RLL: + rc = rcv_rll(ms, msg); + break; + case ABIS_RSL_MDISC_COM_CHAN: + rc = rcv_cch(ms, msg); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n", + rslh->msg_discr); + msgb_free(msg); + rc = -EINVAL; + break; + } + + return rc; +} + +int scan_init(struct osmocom_ms *_ms) +{ + ms = _ms; + register_signal_handler(SS_L1CTL, &signal_cb, NULL); + memset(&timer, 0, sizeof(timer)); + osmol2_register_handler(ms, &rcv_rsl); + gps.enable = 1; + gps_init(); + if (gps_open()) + gps.enable = 0; + + if (!strcmp(logname, "-")) + logfp = stdout; + else + logfp = fopen(logname, "a"); + if (!logfp) { + fprintf(stderr, "Failed to open logfile '%s'\n", logname); + scan_exit(); + return -errno; + } + LOGP(DSUM, LOGL_INFO, "Scanner initialized\n"); + + return 0; +} + +int scan_exit(void) +{ + LOGP(DSUM, LOGL_INFO, "Scanner exit\n"); + if (gps.valid) + gps_close(); + if (logfp) + fclose(logfp); + unregister_signal_handler(SS_L1CTL, &signal_cb, NULL); + stop_timer(); + + return 0; +} diff --git a/src/host/layer23/src/misc/layer3.c b/src/host/layer23/src/misc/layer3.c new file mode 100644 index 00000000..936623f3 --- /dev/null +++ b/src/host/layer23/src/misc/layer3.c @@ -0,0 +1,308 @@ +#include <stdint.h> +#include <errno.h> +#include <stdio.h> + +#include <osmocore/msgb.h> +#include <osmocore/rsl.h> +#include <osmocore/tlv.h> +#include <osmocore/gsm48_ie.h> +#include <osmocore/protocol/gsm_04_08.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/misc/rslms.h> +#include <osmocom/bb/misc/layer3.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1ctl.h> + +static struct { + int has_si1; + int ccch_mode; + int ccch_enabled; + int rach_count; + struct gsm_sysinfo_freq cell_arfcns[1024]; +} app_state; + + +static void dump_bcch(struct osmocom_ms *ms, uint8_t tc, const uint8_t *data) +{ + struct gsm48_system_information_type_header *si_hdr; + si_hdr = (struct gsm48_system_information_type_header *) data; + + /* GSM 05.02 §6.3.1.3 Mapping of BCCH data */ + switch (si_hdr->system_information) { + case GSM48_MT_RR_SYSINFO_1: + fprintf(stderr, "\tSI1"); +#ifdef BCCH_TC_CHECK + if (tc != 0) + fprintf(stderr, " on wrong TC"); +#endif + if (!app_state.has_si1) { + struct gsm48_system_information_type_1 *si1 = + (struct gsm48_system_information_type_1 *)data; + + gsm48_decode_freq_list(&app_state.cell_arfcns, + si1->cell_channel_description, + sizeof(si1->cell_channel_description), + 0xff, 0x01); + + app_state.has_si1 = 1; + } + break; + case GSM48_MT_RR_SYSINFO_2: + fprintf(stderr, "\tSI2"); +#ifdef BCCH_TC_CHECK + if (tc != 1) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_3: + fprintf(stderr, "\tSI3"); +#ifdef BCCH_TC_CHECK + if (tc != 2 && tc != 6) + fprintf(stderr, " on wrong TC"); +#endif + if (app_state.ccch_mode == CCCH_MODE_NONE) { + struct gsm48_system_information_type_3 *si3 = + (struct gsm48_system_information_type_3 *)data; + + if (si3->control_channel_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) + app_state.ccch_mode = CCCH_MODE_COMBINED; + else + app_state.ccch_mode = CCCH_MODE_NON_COMBINED; + + l1ctl_tx_ccch_mode_req(ms, app_state.ccch_mode); + } + break; + case GSM48_MT_RR_SYSINFO_4: + fprintf(stderr, "\tSI4"); +#ifdef BCCH_TC_CHECK + if (tc != 3 && tc != 7) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_5: + fprintf(stderr, "\tSI5"); + break; + case GSM48_MT_RR_SYSINFO_6: + fprintf(stderr, "\tSI6"); + break; + case GSM48_MT_RR_SYSINFO_7: + fprintf(stderr, "\tSI7"); +#ifdef BCCH_TC_CHECK + if (tc != 7) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_8: + fprintf(stderr, "\tSI8"); +#ifdef BCCH_TC_CHECK + if (tc != 3) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_9: + fprintf(stderr, "\tSI9"); +#ifdef BCCH_TC_CHECK + if (tc != 4) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_13: + fprintf(stderr, "\tSI13"); +#ifdef BCCH_TC_CHECK + if (tc != 4 && tc != 0) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_16: + fprintf(stderr, "\tSI16"); +#ifdef BCCH_TC_CHECK + if (tc != 6) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_17: + fprintf(stderr, "\tSI17"); +#ifdef BCCH_TC_CHECK + if (tc != 2) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_2bis: + fprintf(stderr, "\tSI2bis"); +#ifdef BCCH_TC_CHECK + if (tc != 5) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_2ter: + fprintf(stderr, "\tSI2ter"); +#ifdef BCCH_TC_CHECK + if (tc != 5 && tc != 4) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_5bis: + fprintf(stderr, "\tSI5bis"); + break; + case GSM48_MT_RR_SYSINFO_5ter: + fprintf(stderr, "\tSI5ter"); + break; + default: + fprintf(stderr, "\tUnknown SI"); + break; + }; + + fprintf(stderr, "\n"); +} + + +/* send location updating request * (as part of RSLms EST IND / + LAPDm SABME) */ +static int gsm48_tx_loc_upd_req(struct osmocom_ms *ms, uint8_t chan_nr) +{ + struct msgb *msg = msgb_alloc_headroom(256, 16, "loc_upd_req"); + struct gsm48_hdr *gh; + struct gsm48_loc_upd_req *lu_r; + + DEBUGP(DMM, "chan_nr=%u\n", chan_nr); + + msg->l3h = msgb_put(msg, sizeof(*gh)); + gh = (struct gsm48_hdr *) msg->l3h; + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_LOC_UPD_REQUEST; + lu_r = (struct gsm48_loc_upd_req *) msgb_put(msg, sizeof(*lu_r)); + lu_r->type = GSM48_LUPD_IMSI_ATT; + lu_r->key_seq = 0; + /* FIXME: set LAI and CM1 */ + /* FIXME: set MI */ + lu_r->mi_len = 0; + + return rslms_tx_rll_req_l3(ms, RSL_MT_EST_REQ, chan_nr, 0, msg); +} + +static int gsm48_rx_imm_ass(struct msgb *msg, struct osmocom_ms *ms) +{ + struct gsm48_imm_ass *ia = msgb_l3(msg); + uint8_t ch_type, ch_subch, ch_ts; + int rv; + + /* Discard packet TBF assignement */ + if (ia->page_mode & 0xf0) + return 0; + + /* FIXME: compare RA and GSM time with when we sent RACH req */ + + rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts); + + if (!ia->chan_desc.h0.h) { + /* Non-hopping */ + uint16_t arfcn; + + arfcn = ia->chan_desc.h0.arfcn_low | (ia->chan_desc.h0.arfcn_high << 8); + + DEBUGP(DRR, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, " + "ARFCN=%u, TS=%u, SS=%u, TSC=%u) ", ia->req_ref.ra, + ia->chan_desc.chan_nr, arfcn, ch_ts, ch_subch, + ia->chan_desc.h0.tsc); + + /* request L1 to go to dedicated mode on assigned channel */ + rv = l1ctl_tx_dm_est_req_h0(ms, + arfcn, ia->chan_desc.chan_nr, ia->chan_desc.h0.tsc, + GSM48_CMODE_SIGN); + } else { + /* Hopping */ + uint8_t maio, hsn, ma_len; + uint16_t ma[64], arfcn; + int i, j, k; + + hsn = ia->chan_desc.h1.hsn; + maio = ia->chan_desc.h1.maio_low | (ia->chan_desc.h1.maio_high << 2); + + DEBUGP(DRR, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, " + "HSN=%u, MAIO=%u, TS=%u, SS=%u, TSC=%u) ", ia->req_ref.ra, + ia->chan_desc.chan_nr, hsn, maio, ch_ts, ch_subch, + ia->chan_desc.h1.tsc); + + /* decode mobile allocation */ + ma_len = 0; + for (i=1, j=0; i<=1024; i++) { + arfcn = i & 1023; + if (app_state.cell_arfcns[arfcn].mask & 0x01) { + k = ia->mob_alloc_len - (j>>3) - 1; + if (ia->mob_alloc[k] & (1 << (j&7))) { + ma[ma_len++] = arfcn; + } + j++; + } + } + + /* request L1 to go to dedicated mode on assigned channel */ + rv = l1ctl_tx_dm_est_req_h1(ms, + maio, hsn, ma, ma_len, + ia->chan_desc.chan_nr, ia->chan_desc.h1.tsc, + GSM48_CMODE_SIGN); + } + + DEBUGPC(DRR, "\n"); + + rv = gsm48_tx_loc_upd_req(ms, ia->chan_desc.chan_nr); + + return rv; +} + +int gsm48_rx_ccch(struct msgb *msg, struct osmocom_ms *ms) +{ + struct gsm48_system_information_type_header *sih = msgb_l3(msg); + int rc = 0; + + if (sih->rr_protocol_discriminator != GSM48_PDISC_RR) + LOGP(DRR, LOGL_ERROR, "PCH pdisc != RR\n"); + + switch (sih->system_information) { + case GSM48_MT_RR_PAG_REQ_1: + case GSM48_MT_RR_PAG_REQ_2: + case GSM48_MT_RR_PAG_REQ_3: + /* FIXME: implement decoding of paging request */ + break; + case GSM48_MT_RR_IMM_ASS: + rc = gsm48_rx_imm_ass(msg, ms); + break; + default: + LOGP(DRR, LOGL_NOTICE, "unknown PCH/AGCH type 0x%02x\n", + sih->system_information); + rc = -EINVAL; + } + + return rc; +} + +int gsm48_rx_bcch(struct msgb *msg, struct osmocom_ms *ms) +{ + /* FIXME: we have lost the gsm frame time until here, need to store it + * in some msgb context */ + //dump_bcch(dl->time.tc, ccch->data); + dump_bcch(ms, 0, msg->l3h); + + /* Req channel logic */ + if (app_state.ccch_enabled && (app_state.rach_count < 2)) { + l1ctl_tx_rach_req(ms, app_state.rach_count, 0, + app_state.ccch_mode == CCCH_MODE_COMBINED); + app_state.rach_count++; + } + + return 0; +} + +void layer3_app_reset(void) +{ + /* Reset state */ + app_state.has_si1 = 0; + app_state.ccch_mode = CCCH_MODE_NONE; + app_state.ccch_enabled = 0; + app_state.rach_count = 0; + + memset(&app_state.cell_arfcns, 0x00, sizeof(app_state.cell_arfcns)); +} + diff --git a/src/host/layer23/src/misc/rslms.c b/src/host/layer23/src/misc/rslms.c new file mode 100644 index 00000000..b2e00472 --- /dev/null +++ b/src/host/layer23/src/misc/rslms.c @@ -0,0 +1,148 @@ +/* RSLms - GSM 08.58 like protocol between L2 and L3 of GSM Um interface */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <stdio.h> + +#include <osmocore/msgb.h> +#include <osmocore/rsl.h> +#include <osmocore/tlv.h> +#include <osmocore/protocol/gsm_04_08.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/misc/rslms.h> +#include <osmocom/bb/misc/layer3.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1ctl.h> + +/* Send a 'simple' RLL request to L2 */ +int rslms_tx_rll_req(struct osmocom_ms *ms, uint8_t msg_type, + uint8_t chan_nr, uint8_t link_id) +{ + struct msgb *msg; + + msg = rsl_rll_simple(msg_type, chan_nr, link_id, 1); + + return rslms_recvmsg(msg, ms); +} + +/* Send a RLL request (including L3 info) to L2 */ +int rslms_tx_rll_req_l3(struct osmocom_ms *ms, uint8_t msg_type, + uint8_t chan_nr, uint8_t link_id, struct msgb *msg) +{ + rsl_rll_push_l3(msg, msg_type, chan_nr, link_id, 1); + + return rslms_recvmsg(msg, ms); +} + +static int rslms_rx_udata_ind(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + struct tlv_parsed tv; + int rc = 0; + + DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", + rllh->chan_nr, rllh->link_id); + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n"); + return -EIO; + } + msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); + + if (rllh->chan_nr == RSL_CHAN_PCH_AGCH) { + rc = gsm48_rx_ccch(msg, ms); + } else if (rllh->chan_nr == RSL_CHAN_BCCH) { + rc = gsm48_rx_bcch(msg, ms); + } + + return rc; +} + +static int rslms_rx_rll(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + int rc = 0; + + switch (rllh->c.msg_type) { + case RSL_MT_DATA_IND: + DEBUGP(DRSL, "RSLms DATA IND\n"); + /* FIXME: implement this */ + break; + case RSL_MT_UNIT_DATA_IND: + rc = rslms_rx_udata_ind(msg, ms); + break; + case RSL_MT_EST_IND: + DEBUGP(DRSL, "RSLms EST IND\n"); + /* FIXME: implement this */ + break; + case RSL_MT_EST_CONF: + DEBUGP(DRSL, "RSLms EST CONF\n"); + /* FIXME: implement this */ + break; + case RSL_MT_REL_CONF: + DEBUGP(DRSL, "RSLms REL CONF\n"); + /* FIXME: implement this */ + break; + case RSL_MT_ERROR_IND: + DEBUGP(DRSL, "RSLms ERR IND\n"); + /* FIXME: implement this */ + break; + default: + LOGP(DRSL, LOGL_NOTICE, "unknown RSLms message type " + "0x%02x\n", rllh->c.msg_type); + rc = -EINVAL; + break; + } + msgb_free(msg); + return rc; +} + +/* input function that L2 calls when sending messages up to L3 */ +static int layer3_from_layer2(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + int rc = 0; + + switch (rslh->msg_discr & 0xfe) { + case ABIS_RSL_MDISC_RLL: + rc = rslms_rx_rll(msg, ms); + break; + default: + /* FIXME: implement this */ + LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n", + rslh->msg_discr); + msgb_free(msg); + rc = -EINVAL; + break; + } + + return rc; +} + +int layer3_init(struct osmocom_ms *ms) +{ + return osmol2_register_handler(ms, &layer3_from_layer2); +} diff --git a/src/host/layer23/src/mobile/Makefile.am b/src/host/layer23/src/mobile/Makefile.am new file mode 100644 index 00000000..12f0f76b --- /dev/null +++ b/src/host/layer23/src/mobile/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) +LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) + +noinst_LIBRARIES = libmobile.a +libmobile_a_SOURCES = gsm322.c gsm48_cc.c gsm48_mm.c gsm48_rr.c \ + mnccms.c settings.c subscriber.c support.c \ + transaction.c vty_interface.c + +bin_PROGRAMS = mobile + +mobile_SOURCES = main.c app_mobile.c +mobile_LDADD = libmobile.a $(LDADD) + + diff --git a/src/host/layer23/src/mobile/app_mobile.c b/src/host/layer23/src/mobile/app_mobile.c new file mode 100644 index 00000000..822ea640 --- /dev/null +++ b/src/host/layer23/src/mobile/app_mobile.c @@ -0,0 +1,393 @@ +/* "Application" code of the layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <errno.h> +#include <signal.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1l2_interface.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/lapdm.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/gps.h> +#include <osmocom/bb/mobile/gsm48_rr.h> +#include <osmocom/bb/mobile/vty.h> +#include <osmocom/bb/mobile/app_mobile.h> +#include <osmocom/bb/mobile/mncc.h> +#include <osmocom/vty/telnet_interface.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/signal.h> + +extern void *l23_ctx; +extern struct llist_head ms_list; +extern int vty_reading; + +int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg); +int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg); +int (*mncc_recv_app)(struct osmocom_ms *ms, int, void *); +static int quit; + +/* handle ms instance */ +int mobile_work(struct osmocom_ms *ms) +{ + int work = 0, w; + + do { + w = 0; + w |= gsm48_rsl_dequeue(ms); + w |= gsm48_rr_dequeue(ms); + w |= gsm48_mmxx_dequeue(ms); + w |= gsm48_mmr_dequeue(ms); + w |= gsm48_mmevent_dequeue(ms); + w |= gsm322_plmn_dequeue(ms); + w |= gsm322_cs_dequeue(ms); + w |= gsm_sim_job_dequeue(ms); + w |= mncc_dequeue(ms); + if (w) + work = 1; + } while (w); + return work; +} + +/* run ms instance, if layer1 is available */ +int mobile_signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + struct gsm_settings *set; + struct msgb *nmsg; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_RESET: + ms = signal_data; + set = &ms->settings; + + if (ms->started) + break; + + /* insert test card, if enabled */ + switch (set->sim_type) { + case GSM_SIM_TYPE_READER: + /* trigger sim card reader process */ + gsm_subscr_simcard(ms); + break; + case GSM_SIM_TYPE_TEST: + gsm_subscr_testcard(ms, set->test_rplmn_mcc, + set->test_rplmn_mnc, set->test_lac, + set->test_tmsi); + break; + default: + /* no SIM, trigger PLMN selection process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + } + + ms->started = 1; + } + return 0; +} + +/* power-off ms instance */ +int mobile_exit(struct osmocom_ms *ms, int force) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + if (!force && ms->started) { + struct msgb *nmsg; + + ms->shutdown = 1; /* going down */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_IMSI_DETACH); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(mm->ms, nmsg); + + return -EBUSY; + } + + gsm322_exit(ms); + gsm48_mm_exit(ms); + gsm48_rr_exit(ms); + gsm_subscr_exit(ms); + gsm48_cc_exit(ms); + gsm_sim_exit(ms); + lapdm_exit(&ms->l2_entity.lapdm_acch); + lapdm_exit(&ms->l2_entity.lapdm_dcch); + + ms->shutdown = 2; /* being down */ + vty_notify(ms, NULL); + vty_notify(ms, "Power off!\n"); + printf("Power off! (MS %s)\n", ms->name); + + return 0; +} + +/* power-on ms instance */ +int mobile_init(struct osmocom_ms *ms) +{ + int rc; + + lapdm_init(&ms->l2_entity.lapdm_dcch, ms); + lapdm_init(&ms->l2_entity.lapdm_acch, ms); + gsm_sim_init(ms); + gsm48_cc_init(ms); + gsm_subscr_init(ms); + gsm48_rr_init(ms); + gsm48_mm_init(ms); + INIT_LLIST_HEAD(&ms->trans_list); + gsm322_init(ms); + + rc = layer2_open(ms, ms->settings.layer2_socket_path); + if (rc < 0) { + fprintf(stderr, "Failed during layer2_open()\n"); + ms->l2_wq.bfd.fd = -1; + mobile_exit(ms, 1); + return rc; + } + +#if 0 + rc = sap_open(ms, ms->settings.sap_socket_path); + if (rc < 0) { + fprintf(stderr, "Failed during sap_open(), no SIM reader\n"); + ms->sap_wq.bfd.fd = -1; + mobile_exit(ms, 1); + return rc; + } +#endif + + if (mncc_recv_app) + ms->cclayer.mncc_recv = mncc_recv_app; + else if (ms->settings.ch_cap == GSM_CAP_SDCCH) + ms->cclayer.mncc_recv = mncc_recv_dummy; + else + ms->cclayer.mncc_recv = mncc_recv_mobile; + + gsm_random_imei(&ms->settings); + + ms->shutdown = 0; + ms->started = 0; + + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + printf("Mobile '%s' initialized, please start phone now!\n", ms->name); + return 0; +} + +/* create ms instance */ +struct osmocom_ms *mobile_new(char *name) +{ + static struct osmocom_ms *ms; + + ms = talloc_zero(l23_ctx, struct osmocom_ms); + if (!ms) { + fprintf(stderr, "Failed to allocate MS\n"); + exit(1); + } + llist_add_tail(&ms->entity, &ms_list); + + strcpy(ms->name, name); + + ms->l2_wq.bfd.fd = -1; + ms->sap_wq.bfd.fd = -1; + + gsm_support_init(ms); + gsm_settings_init(ms); + + ms->shutdown = 2; /* being down */ + + if (mncc_recv_app) { + struct msgb *msg; + + msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC"); + if (msg) { + struct gsm_mncc *mncc = (struct gsm_mncc *)msg->data; + + mncc->msg_type = MS_NEW; + mncc_recv_app(ms, mncc->msg_type, mncc); + } + ms->cclayer.mncc_recv = mncc_recv_app; + } else + ms->cclayer.mncc_recv = mncc_recv_dummy; + + return ms; +} + +/* destroy ms instance */ +int mobile_delete(struct osmocom_ms *ms, int force) +{ + int rc; + + ms->deleting = 1; + + if (ms->shutdown == 0 || (ms->shutdown == 1 && force)) { + rc = mobile_exit(ms, force); + if (rc < 0) + return rc; + } + + if (mncc_recv_app) { + struct msgb *msg; + + msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC"); + if (msg) { + struct gsm_mncc *mncc = (struct gsm_mncc *)msg->data; + + mncc->msg_type = MS_DELETE; + mncc_recv_app(ms, mncc->msg_type, mncc); + } + } + + return 0; +} + +/* handle global shutdown */ +int global_signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms, *ms2; + + if (subsys != SS_GLOBAL) + return 0; + + switch (signal) { + case S_GLOBAL_SHUTDOWN: + llist_for_each_entry_safe(ms, ms2, &ms_list, entity) + mobile_delete(ms, quit); + + /* if second signal is received, force to exit */ + quit = 1; + break; + } + return 0; +} + +/* global work handler */ +int l23_app_work(int *_quit) +{ + struct osmocom_ms *ms, *ms2; + int work = 0; + + llist_for_each_entry_safe(ms, ms2, &ms_list, entity) { + if (ms->shutdown != 2) + work |= mobile_work(ms); + if (ms->shutdown == 2) { + if (ms->l2_wq.bfd.fd > -1) { + layer2_close(ms); + ms->l2_wq.bfd.fd = -1; + } + + if (ms->sap_wq.bfd.fd > -1) { + sap_close(ms); + ms->sap_wq.bfd.fd = -1; + } + + if (ms->deleting) { + gsm_settings_exit(ms); + llist_del(&ms->entity); + talloc_free(ms); + work = 1; + } + } + } + + /* return, if a shutdown was scheduled (quit = 1) */ + *_quit = quit; + return work; +} + +/* global exit */ +int l23_app_exit(void) +{ + unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL); + unregister_signal_handler(SS_L1CTL, &mobile_signal_cb, NULL); + unregister_signal_handler(SS_GLOBAL, &global_signal_cb, NULL); + + gps_close(); + + return 0; +} + +static struct vty_app_info vty_info = { + .name = "OsmocomBB", + .version = PACKAGE_VERSION, + .go_parent_cb = ms_vty_go_parent, +}; + +/* global init */ +int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *), + const char *config_file, uint16_t vty_port) +{ + struct telnet_connection dummy_conn; + int rc; + + mncc_recv_app = mncc_recv; + + gps_init(); + + vty_init(&vty_info); + ms_vty_init(); + dummy_conn.priv = NULL; + vty_reading = 1; + rc = vty_read_config_file(config_file, &dummy_conn); + if (rc < 0) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", + config_file); + fprintf(stderr, "Please check or create config file using: " + "'touch %s'\n", config_file); + return rc; + } + vty_reading = 0; + telnet_init(l23_ctx, NULL, vty_port); + if (rc < 0) + return rc; + printf("VTY available on port %u.\n", vty_port); + + register_signal_handler(SS_GLOBAL, &global_signal_cb, NULL); + register_signal_handler(SS_L1CTL, &mobile_signal_cb, NULL); + register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL); + + if (llist_empty(&ms_list)) { + struct osmocom_ms *ms; + + printf("No Mobile Station defined, creating: MS '1'\n"); + ms = mobile_new("1"); + if (ms) + mobile_init(ms); + } + + quit = 0; + + return 0; +} + diff --git a/src/host/layer23/src/mobile/gsm322.c b/src/host/layer23/src/mobile/gsm322.c new file mode 100644 index 00000000..3c920e4e --- /dev/null +++ b/src/host/layer23/src/mobile/gsm322.c @@ -0,0 +1,3605 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/utils.h> +#include <osmocore/gsm48.h> +#include <osmocore/signal.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/networks.h> +#include <osmocom/bb/mobile/vty.h> +#include <osmocom/bb/mobile/app_mobile.h> + +extern void *l23_ctx; + +static void gsm322_cs_timeout(void *arg); +static void gsm322_cs_loss(void *arg); +static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed); +static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg); + +#define SKIP_MAX_PER_BAND + +#warning HACKING!!! +int hack; + +/* + * notes + */ + +/* Cell selection process + * + * The process depends on states and events (finites state machine). + * + * During states of cell selection or cell re-selection, the search for a cell + * is performed in two steps: + * + * 1. Measurement of received level of all relevant frequencies (rx-lev) + * + * 2. Receive system information messages of all relevant frequencies + * + * During this process, the results are stored in a list of all frequencies. + * This list is checked whenever a cell is selected. It depends on the results + * if the cell is 'suitable' and 'allowable' to 'camp' on. + * + * This list is also used to generate a list of available networks. + * + * The states are: + * + * - cs->list[0..1023].xxx for each cell, where + * - flags and rxlev are used to store outcome of cell scanning process + * - sysinfo pointing to sysinfo memory, allocated temporarily + * - cs->selected and cs->sel_* states of the current / last selected cell. + * + * + * There is a special state: GSM322_PLMN_SEARCH + * It is used to search for all cells, to find the HPLMN. This is triggered + * by a timer. Also it is used before selecting PLMN from list. + * + */ + +/* PLMN selection process + * + * The PLMN (Public Land Mobile Network = Operator's Network) has two different + * search processes: + * + * 1. Automatic search + * + * 2. Manual search + * + * The process depends on states and events (finites state machine). + * + */ + +/* File format of BA list: + * + * uint16_t mcc + * uint16_t mcc + * uint8_t freq[128]; + * where frequency 0 is bit 0 of first byte + * + * If not end-of-file, the next BA list is stored. + */ + +/* List of lists: + * + * * subscr->plmn_list + * + * The "PLMN Selector list" stores prefered networks to select during PLMN + * search process. This list is also stored in the SIM. + * + * * subscr->plmn_na + * + * The "forbidden PLMNs" list stores all networks that rejected us. The stored + * network will not be used when searching PLMN automatically. This list is + * also stored din the SIM. + * + * * plmn->forbidden_la + * + * The "forbidden LAs for roaming" list stores all location areas where roaming + * was not allowed. + * + * * cs->list[1024] + * + * This list stores measurements and cell informations during cell selection + * process. It can be used to speed up repeated cell selection. + * + * * cs->ba_list + * + * This list stores a map of frequencies used for a PLMN. If this lists exists + * for a PLMN, it helps to speedup cell scan process. + * + * * plmn->sorted_plmn + * + * This list is generated whenever a PLMN search is started and a list of PLMNs + * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found + * during scan process. + */ + +/* + * event messages + */ + +static const struct value_string gsm322_event_names[] = { + { GSM322_EVENT_SWITCH_ON, "EVENT_SWITCH_ON" }, + { GSM322_EVENT_SWITCH_OFF, "EVENT_SWITCH_OFF" }, + { GSM322_EVENT_SIM_INSERT, "EVENT_SIM_INSERT" }, + { GSM322_EVENT_SIM_REMOVE, "EVENT_SIM_REMOVE" }, + { GSM322_EVENT_REG_FAILED, "EVENT_REG_FAILED" }, + { GSM322_EVENT_ROAMING_NA, "EVENT_ROAMING_NA" }, + { GSM322_EVENT_INVALID_SIM, "EVENT_INVALID_SIM" }, + { GSM322_EVENT_REG_SUCCESS, "EVENT_REG_SUCCESS" }, + { GSM322_EVENT_NEW_PLMN, "EVENT_NEW_PLMN" }, + { GSM322_EVENT_ON_PLMN, "EVENT_ON_PLMN" }, + { GSM322_EVENT_PLMN_SEARCH_START,"EVENT_PLMN_SEARCH_START" }, + { GSM322_EVENT_PLMN_SEARCH_END, "EVENT_PLMN_SEARCH_END" }, + { GSM322_EVENT_USER_RESEL, "EVENT_USER_RESEL" }, + { GSM322_EVENT_PLMN_AVAIL, "EVENT_PLMN_AVAIL" }, + { GSM322_EVENT_CHOOSE_PLMN, "EVENT_CHOOSE_PLMN" }, + { GSM322_EVENT_SEL_MANUAL, "EVENT_SEL_MANUAL" }, + { GSM322_EVENT_SEL_AUTO, "EVENT_SEL_AUTO" }, + { GSM322_EVENT_CELL_FOUND, "EVENT_CELL_FOUND" }, + { GSM322_EVENT_NO_CELL_FOUND, "EVENT_NO_CELL_FOUND" }, + { GSM322_EVENT_LEAVE_IDLE, "EVENT_LEAVE_IDLE" }, + { GSM322_EVENT_RET_IDLE, "EVENT_RET_IDLE" }, + { GSM322_EVENT_CELL_RESEL, "EVENT_CELL_RESEL" }, + { GSM322_EVENT_SYSINFO, "EVENT_SYSINFO" }, + { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" }, + { 0, NULL } +}; + +const char *get_event_name(int value) +{ + return get_value_string(gsm322_event_names, value); +} + + +/* allocate a 03.22 event message */ +struct msgb *gsm322_msgb_alloc(int msg_type) +{ + struct msgb *msg; + struct gsm322_msg *gm; + + msg = msgb_alloc_headroom(sizeof(*gm), 0, "GSM 03.22 event"); + if (!msg) + return NULL; + + gm = (struct gsm322_msg *)msgb_put(msg, sizeof(*gm)); + gm->msg_type = msg_type; + + return msg; +} + +/* queue PLMN selection message */ +int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + + msgb_enqueue(&plmn->event_queue, msg); + + return 0; +} + +/* queue cell selection message */ +int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + msgb_enqueue(&cs->event_queue, msg); + + return 0; +} + +/* + * support + */ + +char *gsm_print_rxlev(uint8_t rxlev) +{ + static char string[5]; + if (rxlev == 0) + return "<=-110"; + if (rxlev >= 63) + return ">=-48"; + sprintf(string, "-%d", 110 - rxlev); + return string; +} + +static int gsm322_sync_to_cell(struct gsm322_cellsel *cs) +{ + struct osmocom_ms *ms = cs->ms; + struct gsm48_sysinfo *s = cs->si; + struct rx_meas_stat *meas = &ms->meas; + + cs->ccch_state = GSM322_CCCH_ST_INIT; + if (s && s->si3) { + if (s->ccch_conf == 1) { + LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s " + "(Sysinfo, ccch mode COMB)\n", cs->arfcn, + gsm_print_rxlev(cs->list[cs->arfcn].rxlev)); + cs->ccch_mode = CCCH_MODE_COMBINED; + } else { + LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s " + "(Sysinfo, ccch mode NON-COMB)\n", cs->arfcn, + gsm_print_rxlev(cs->list[cs->arfcn].rxlev)); + cs->ccch_mode = CCCH_MODE_NON_COMBINED; + } + } else { + LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%d rxlev=%s (No sysinfo " + "yet, ccch mode NONE)\n", cs->arfcn, + gsm_print_rxlev(cs->list[cs->arfcn].rxlev)); + cs->ccch_mode = CCCH_MODE_NONE; + } + + meas->frames = meas->snr = meas->berr = meas->rxlev = 0; + + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + return l1ctl_tx_fbsb_req(ms, cs->arfcn, + L1CTL_FBSB_F_FB01SB, 100, 0, + cs->ccch_mode); +} + +static void gsm322_unselect_cell(struct gsm322_cellsel *cs) +{ + cs->selected = 0; + if (cs->si) + cs->si->si5 = 0; /* unset SI5* */ + cs->si = NULL; + memset(&cs->sel_si, 0, sizeof(cs->sel_si)); + cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0; +} + +/* print to DCS logging */ +static void print_dcs(void *priv, const char *fmt, ...) +{ + char buffer[1000]; + va_list args; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + if (buffer[0]) +// LOGP(DCS, LOGL_INFO, "%s", buffer); + printf("%s", buffer); +} + +/* del forbidden LA */ +int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, + uint16_t mnc, uint16_t lac) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_la_list *la; + + llist_for_each_entry(la, &plmn->forbidden_la, entry) { + if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) { + LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " + "LAs (mcc=%s, mnc=%s, lac=%04x)\n", + gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac); + llist_del(&la->entry); + talloc_free(la); + return 0; + } + } + + return -EINVAL; +} + +/* add forbidden LA */ +int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, + uint16_t mnc, uint16_t lac, uint8_t cause) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_la_list *la; + + LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs " + "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc), + gsm_print_mnc(mnc), lac); + la = talloc_zero(l23_ctx, struct gsm322_la_list); + if (!la) + return -ENOMEM; + la->mcc = mcc; + la->mnc = mnc; + la->lac = lac; + la->cause = cause; + llist_add_tail(&la->entry, &plmn->forbidden_la); + + return 0; +} + +/* search forbidden LA */ +int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, + uint16_t lac) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_la_list *la; + + llist_for_each_entry(la, &plmn->forbidden_la, entry) { + if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) + return 1; + } + + return 0; +} + +/* search for PLMN in all BA lists */ +static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs, + uint16_t mcc, uint16_t mnc) +{ + struct gsm322_ba_list *ba, *ba_found = NULL; + + /* search for BA list */ + llist_for_each_entry(ba, &cs->ba_list, entry) { + if (ba->mcc == mcc + && ba->mnc == mnc) { + ba_found = ba; + break; + } + } + + return ba_found; +} + +/* search available PLMN */ +int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc) +{ + int i; + + for (i = 0; i <= 1023; i++) { + if (cs->list[i].sysinfo + && cs->list[i].sysinfo->mcc == mcc + && cs->list[i].sysinfo->mnc == mnc) + return 1; + } + + return 0; +} + +/* search available HPLMN */ +int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi) +{ + int i; + + for (i = 0; i <= 1023; i++) { + if (cs->list[i].sysinfo + && gsm_match_mnc(cs->list[i].sysinfo->mcc, + cs->list[i].sysinfo->mnc, imsi)) + return 1; + } + + return 0; +} + +/* del forbidden LA */ +/* + * timer + */ + +/*plmn search timer event */ +static void plmn_timer_timeout(void *arg) +{ + struct gsm322_plmn *plmn = arg; + struct msgb *nmsg; + + LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n"); + + /* indicate PLMN selection T timeout */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH); + if (!nmsg) + return; + gsm322_plmn_sendmsg(plmn->ms, nmsg); +} + +/* start plmn search timer */ +static void start_plmn_timer(struct gsm322_plmn *plmn, int secs) +{ + LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n", + secs / 60); + plmn->timer.cb = plmn_timer_timeout; + plmn->timer.data = plmn; + bsc_schedule_timer(&plmn->timer, secs, 0); +} + +/* stop plmn search timer */ +static void stop_plmn_timer(struct gsm322_plmn *plmn) +{ + if (bsc_timer_pending(&plmn->timer)) { + LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n"); + bsc_del_timer(&plmn->timer); + } +} + +/* start cell selection timer */ +void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro) +{ + LOGP(DCS, LOGL_DEBUG, "Starting CS timer with %d seconds.\n", sec); + cs->timer.cb = gsm322_cs_timeout; + cs->timer.data = cs; + bsc_schedule_timer(&cs->timer, sec, micro); +} + +/* stop cell selection timer */ +static void stop_cs_timer(struct gsm322_cellsel *cs) +{ + if (bsc_timer_pending(&cs->timer)) { + LOGP(DCS, LOGL_DEBUG, "stopping pending CS timer.\n"); + bsc_del_timer(&cs->timer); + } +} + +/* + * state change + */ + +const char *plmn_a_state_names[] = { + "A0 null", + "A1 trying RPLMN", + "A2 on PLMN", + "A3 trying PLMN", + "A4 wait for PLMN to appear", + "A5 HPLMN search", + "A6 no SIM inserted" +}; + +const char *plmn_m_state_names[] = { + "M0 null", + "M1 trying RPLMN", + "M2 on PLMN", + "M3 not on PLMN", + "M4 trying PLMN", + "M5 no SIM inserted" +}; + +const char *cs_state_names[] = { + "C0 null", + "C1 normal cell selection", + "C2 stored cell selection", + "C3 camped normally", + "C4 normal cell re-selection", + "C5 choose cell", + "C6 any cell selection", + "C7 camped on any cell", + "C8 any cell re-selection", + "C9 choose any cell", + "PLMN search", + "HPLMN search" +}; + + +/* new automatic PLMN search state */ +static void new_a_state(struct gsm322_plmn *plmn, int state) +{ + if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) { + LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n"); + return; + } + + stop_plmn_timer(plmn); + + if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *))) + return; + + LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n", + plmn_a_state_names[plmn->state], plmn_a_state_names[state]); + + plmn->state = state; +} + +/* new manual PLMN search state */ +static void new_m_state(struct gsm322_plmn *plmn, int state) +{ + if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) { + LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n"); + return; + } + + if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *))) + return; + + LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n", + plmn_m_state_names[plmn->state], plmn_m_state_names[state]); + + plmn->state = state; +} + +/* new Cell selection state */ +static void new_c_state(struct gsm322_cellsel *cs, int state) +{ + if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *))) + return; + + LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n", + cs_state_names[cs->state], cs_state_names[state]); + + /* stop cell selection timer, if running */ + stop_cs_timer(cs); + + /* stop scanning of power measurement */ + if (cs->powerscan) { + LOGP(DCS, LOGL_INFO, "changing state while power scanning\n"); + l1ctl_tx_reset_req(cs->ms, L1CTL_RES_T_FULL); + cs->powerscan = 0; + } + + cs->state = state; +} + +/* + * list of PLMNs + */ + +/* 4.4.3 create sorted list of PLMN + * + * the source of entries are + * + * - HPLMN + * - entries found in the SIM's PLMN Selector list + * - scanned PLMNs above -85 dB (random order) + * - scanned PLMNs below or equal -85 (by received level) + * + * NOTE: + * + * The list only includes networks found at last scan. + * + * The list always contains HPLMN if available, even if not used by PLMN + * search process at some conditions. + * + * The list contains all PLMNs even if not allowed, so entries have to be + * removed when selecting from the list. (In case we use manual cell selection, + * we need to provide non-allowed networks also.) + */ +static int gsm322_sort_list(struct osmocom_ms *ms) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_sub_plmn_list *sim_entry; + struct gsm_sub_plmn_na *na_entry; + struct llist_head temp_list; + struct gsm322_plmn_list *temp, *found; + struct llist_head *lh, *lh2; + int i, entries, move; + int8_t search = 0; + + /* flush list */ + llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) { + llist_del(lh); + talloc_free(lh); + } + + /* Create a temporary list of all networks */ + INIT_LLIST_HEAD(&temp_list); + for (i = 0; i <= 1023; i++) { + if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA) + || !cs->list[i].sysinfo) + continue; + + /* search if network has multiple cells */ + found = NULL; + llist_for_each_entry(temp, &temp_list, entry) { + if (temp->mcc == cs->list[i].sysinfo->mcc + && temp->mnc == cs->list[i].sysinfo->mnc) { + found = temp; + break; + } + } + /* update or create */ + if (found) { + if (cs->list[i].rxlev > found->rxlev) + found->rxlev = cs->list[i].rxlev; + } else { + temp = talloc_zero(l23_ctx, struct gsm322_plmn_list); + if (!temp) + return -ENOMEM; + temp->mcc = cs->list[i].sysinfo->mcc; + temp->mnc = cs->list[i].sysinfo->mnc; + temp->rxlev = cs->list[i].rxlev; + llist_add_tail(&temp->entry, &temp_list); + } + } + + /* move Home PLMN, if in list, else add it */ + if (subscr->sim_valid) { + found = NULL; + llist_for_each_entry(temp, &temp_list, entry) { + if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) { + found = temp; + break; + } + } + if (found) { + /* move */ + llist_del(&found->entry); + llist_add_tail(&found->entry, &plmn->sorted_plmn); + } + } + + /* move entries if in SIM's PLMN Selector list */ + llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) { + found = NULL; + llist_for_each_entry(temp, &temp_list, entry) { + if (temp->mcc == sim_entry->mcc + && temp->mnc == sim_entry->mnc) { + found = temp; + break; + } + } + if (found) { + llist_del(&found->entry); + llist_add_tail(&found->entry, &plmn->sorted_plmn); + } + } + + /* move PLMN above -85 dBm in random order */ + entries = 0; + llist_for_each_entry(temp, &temp_list, entry) { + if (rxlev2dbm(temp->rxlev) > -85) + entries++; + } + while(entries) { + move = random() % entries; + i = 0; + llist_for_each_entry(temp, &temp_list, entry) { + if (rxlev2dbm(temp->rxlev) > -85) { + if (i == move) { + llist_del(&temp->entry); + llist_add_tail(&temp->entry, + &plmn->sorted_plmn); + break; + } + i++; + } + } + entries--; + } + + /* move ohter PLMN in decreasing order */ + while(1) { + found = NULL; + llist_for_each_entry(temp, &temp_list, entry) { + if (!found + || temp->rxlev > search) { + search = temp->rxlev; + found = temp; + } + } + if (!found) + break; + llist_del(&found->entry); + llist_add_tail(&found->entry, &plmn->sorted_plmn); + } + + /* mark forbidden PLMNs, if in list of forbidden networks */ + i = 0; + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) { + llist_for_each_entry(na_entry, &subscr->plmn_na, entry) { + if (temp->mcc == na_entry->mcc + && temp->mnc == na_entry->mnc) { + temp->cause = na_entry->cause; + break; + } + } + LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. " + "(%02d: mcc %s mnc %s allowed %s rx-lev %s)\n", + i, gsm_print_mcc(temp->mcc), + gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes", + gsm_print_rxlev(temp->rxlev)); + i++; + } + + gsm322_dump_sorted_plmn(ms); + + return 0; +} + +/* + * handler for automatic search + */ + +/* go On PLMN state */ +static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + + new_a_state(plmn, GSM322_A2_ON_PLMN); + + /* start timer, if on VPLMN of home country OR special case */ + if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi) + && (subscr->always_search_hplmn + || gsm_match_mcc(plmn->mcc, subscr->imsi)) + && subscr->sim_valid && subscr->t6m_hplmn) + start_plmn_timer(plmn, subscr->t6m_hplmn * 360); + else + stop_plmn_timer(plmn); + + return 0; +} + +/* indicate selected PLMN */ +static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + + vty_notify(ms, NULL); + vty_notify(ms, "Selected Network: %s, %s\n", + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + + return gsm322_a_go_on_plmn(ms, msg); +} + +/* no (more) PLMN in list */ +static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; + int found; + + /* any allowable PLMN available? */ + plmn->mcc = plmn->mnc = 0; + found = gsm322_cs_select(ms, 0, 1); + + /* if no PLMN in list */ + if (found < 0) { + LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable.\n"); + + new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN); + +#if 0 + /* we must forward this, otherwhise "Any cell selection" + * will not start automatically. + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); +#endif + LOGP(DPLMN, LOGL_INFO, "Trigger full PLMN search.\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; + } + + /* select first PLMN in list */ + plmn->mcc = cs->list[found].sysinfo->mcc; + plmn->mnc = cs->list[found].sysinfo->mnc; + + LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + /* go On PLMN */ + return gsm322_a_indicate_selected(ms, msg); +} + +/* select first PLMN in list */ +static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct gsm322_plmn_list *plmn_entry; + struct gsm322_plmn_list *plmn_first = NULL; + int i; + + /* generate list */ + gsm322_sort_list(ms); + + /* select first entry */ + i = 0; + llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) { + /* if last selected PLMN was HPLMN, we skip that */ + if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc, + subscr->imsi) + && plmn_entry->mcc == plmn->mcc + && plmn_entry->mnc == plmn->mnc) { + LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was " + "previously selected.\n"); + i++; + continue; + } + /* select first allowed network */ + if (!plmn_entry->cause) { + plmn_first = plmn_entry; + break; + } + LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), " + "because it is not allowed (cause %d).\n", i, + gsm_print_mcc(plmn_entry->mcc), + gsm_print_mnc(plmn_entry->mnc), + plmn_entry->cause); + i++; + } + plmn->plmn_curr = i; + + /* if no PLMN in list */ + if (!plmn_first) { + LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n"); + gsm322_a_no_more_plmn(ms, msg); + + return 0; + } + + LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s " + "mnc=%s %s, %s)\n", plmn->plmn_curr, + gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc), + gsm_get_mcc(plmn_first->mcc), + gsm_get_mnc(plmn_first->mcc, plmn_first->mnc)); + + /* set current network */ + plmn->mcc = plmn_first->mcc; + plmn->mnc = plmn_first->mnc; + + new_a_state(plmn, GSM322_A3_TRYING_PLMN); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* select next PLMN in list */ +static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + struct gsm322_plmn_list *plmn_entry; + struct gsm322_plmn_list *plmn_next = NULL; + int i, ii; + + /* select next entry from list */ + i = 0; + ii = plmn->plmn_curr + 1; + llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) { + /* skip previously selected networks */ + if (i < ii) { + i++; + continue; + } + /* select next allowed network */ + if (!plmn_entry->cause) { + plmn_next = plmn_entry; + break; + } + LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), " + "because it is not allowed (cause %d).\n", i, + gsm_print_mcc(plmn_entry->mcc), + gsm_print_mnc(plmn_entry->mnc), + plmn_entry->cause); + i++; + } + plmn->plmn_curr = i; + + /* if no more PLMN in list */ + if (!plmn_next) { + LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n"); + gsm322_a_no_more_plmn(ms, msg); + return 0; + } + + /* set next network */ + plmn->mcc = plmn_next->mcc; + plmn->mnc = plmn_next->mnc; + + LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s " + "mnc=%s %s, %s)\n", plmn->plmn_curr, + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + + new_a_state(plmn, GSM322_A3_TRYING_PLMN); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* User re-selection event */ +static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_plmn_list *plmn_entry; + struct gsm322_plmn_list *plmn_found = NULL; + + if (!subscr->sim_valid) { + return 0; + } + + /* try again later, if not idle */ + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n"); + + return 0; + } + + /* search current PLMN in list */ + llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) { + if (plmn_entry->mcc == plmn->mcc + && plmn_entry->mnc == plmn->mnc) + plmn_found = plmn_entry; + break; + } + + /* abort if list is empty */ + if (!plmn_found) { + LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n"); + return 0; + } + + LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list " + "and restarting PLMN search process.\n"); + + /* move entry to end of list */ + llist_del(&plmn_found->entry); + llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn); + + /* select first PLMN in list */ + return gsm322_a_sel_first_plmn(ms, msg); +} + +/* PLMN becomes available */ +static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + + if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc + && subscr->plmn_mnc == gm->mnc) { + /* go On PLMN */ + plmn->mcc = gm->mcc; + plmn->mnc = gm->mnc; + LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n"); + return gsm322_a_go_on_plmn(ms, msg); + } else { + /* select first PLMN in list */ + LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN " + "search process.\n"); + return gsm322_a_sel_first_plmn(ms, msg); + } +} + +/* loss of radio coverage */ +static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + int found; + struct msgb *nmsg; + + /* any PLMN available */ + found = gsm322_cs_select(ms, 0, 1); + + /* if PLMN in list */ + if (found >= 0) { + LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc( + cs->list[found].sysinfo->mcc), + gsm_print_mnc(cs->list[found].sysinfo->mnc), + gsm_get_mcc(cs->list[found].sysinfo->mcc), + gsm_get_mnc(cs->list[found].sysinfo->mcc, + cs->list[found].sysinfo->mnc)); + return gsm322_a_sel_first_plmn(ms, msg); + } + + LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n"); + + plmn->mcc = plmn->mnc = 0; + + new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN); + + /* Tell cell selection process to handle "no cell found". */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* MS is switched on OR SIM is inserted OR removed */ +static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + + if (!subscr->sim_valid) { + LOGP(DSUM, LOGL_INFO, "SIM is removed\n"); + LOGP(DPLMN, LOGL_INFO, "SIM is removed\n"); + new_a_state(plmn, GSM322_A6_NO_SIM); + + return 0; + } + + /* if there is a registered PLMN */ + if (subscr->plmn_valid) { + /* select the registered PLMN */ + plmn->mcc = subscr->plmn_mcc; + plmn->mnc = subscr->plmn_mnc; + + LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN " + "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + + new_a_state(plmn, GSM322_A1_TRYING_RPLMN); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; + } + + /* initiate search at cell selection */ + LOGP(DSUM, LOGL_INFO, "Search for network\n"); + LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* MS is switched off */ +static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + + new_a_state(plmn, GSM322_A0_NULL); + + return 0; +} + +static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg) +{ + LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n"); + return 0; +} + +/* SIM is removed */ +static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + int msg_type = gm->msg_type; + struct msgb *nmsg; + + if (msg_type == GSM322_EVENT_INVALID_SIM) { + vty_notify(ms, NULL); + vty_notify(ms, "SIM not valid\n"); + } + /* indicate SIM remove to cell selection process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return gsm322_a_switch_on(ms, msg); +} + +/* location update response: "Roaming not allowed" */ +static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg) +{ + /* store in list of forbidden LAs is done in gsm48* */ + + return gsm322_a_sel_first_plmn(ms, msg); +} + +/* On VPLMN of home country and timeout occurs */ +static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; + + /* try again later, if not idle and not camping */ + if (rr->state != GSM48_RR_ST_IDLE + || cs->state != GSM322_C3_CAMPED_NORMALLY) { + LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n"); + start_plmn_timer(plmn, 60); + + return 0; + } + + new_a_state(plmn, GSM322_A5_HPLMN_SEARCH); + + /* initiate search at cell selection */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* manual mode selected */ +static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + + /* restart state machine */ + gsm322_a_switch_off(ms, msg); + ms->settings.plmn_mode = PLMN_MODE_MANUAL; + gsm322_m_switch_on(ms, msg); + + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + return 0; +} + +/* + * handler for manual search + */ + +/* display PLMNs and to Not on PLMN */ +static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + int msg_type = gm->msg_type; + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_sub_plmn_list *temp; + + /* generate list */ + gsm322_sort_list(ms); + + vty_notify(ms, NULL); + switch (msg_type) { + case GSM322_EVENT_REG_FAILED: + vty_notify(ms, "Failed to register to network %s, %s " + "(%s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + break; + case GSM322_EVENT_NO_CELL_FOUND: + vty_notify(ms, "No cell found for network %s, %s " + "(%s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + break; + case GSM322_EVENT_ROAMING_NA: + vty_notify(ms, "Roaming not allowed to network %s, %s " + "(%s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + break; + } + + if (llist_empty(&plmn->sorted_plmn)) + vty_notify(ms, "Search network!\n"); + else { + vty_notify(ms, "Search or select from network:\n"); + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) + vty_notify(ms, " Network %s, %s (%s, %s)\n", + gsm_print_mcc(temp->mcc), + gsm_print_mnc(temp->mnc), + gsm_get_mcc(temp->mcc), + gsm_get_mnc(temp->mcc, temp->mnc)); + } + + /* go Not on PLMN state */ + new_m_state(plmn, GSM322_M3_NOT_ON_PLMN); + + return 0; +} + +/* user starts reselection */ +static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + + if (!subscr->sim_valid) { + return 0; + } + + /* try again later, if not idle */ + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n"); + + return 0; + } + + /* initiate search at cell selection */ + vty_notify(ms, NULL); + vty_notify(ms, "Searching Network, please wait...\n"); + LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* MS is switched on OR SIM is inserted OR removed */ +static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + + if (!subscr->sim_valid) { + LOGP(DSUM, LOGL_INFO, "SIM is removed\n"); + LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n"); + new_m_state(plmn, GSM322_M5_NO_SIM); + + return 0; + } + + /* if there is a registered PLMN */ + if (subscr->plmn_valid) { + struct msgb *nmsg; + + /* select the registered PLMN */ + plmn->mcc = subscr->plmn_mcc; + plmn->mnc = subscr->plmn_mnc; + + LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN " + "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + + new_m_state(plmn, GSM322_M1_TRYING_RPLMN); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; + } + + /* initiate search at cell selection */ + LOGP(DSUM, LOGL_INFO, "Search for network\n"); + LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n"); + vty_notify(ms, NULL); + vty_notify(ms, "Searching Network, please wait...\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* MS is switched off */ +static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + + stop_plmn_timer(plmn); + + new_m_state(plmn, GSM322_M0_NULL); + + return 0; +} + +static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg) +{ + LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n"); + return 0; +} + +/* SIM is removed */ +static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + + stop_plmn_timer(plmn); + + /* indicate SIM remove to cell selection process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return gsm322_m_switch_on(ms, msg); +} + +/* go to On PLMN state */ +static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + + /* set last registered PLMN */ + subscr->plmn_valid = 1; + subscr->plmn_mcc = plmn->mcc; + subscr->plmn_mnc = plmn->mnc; +#ifdef TODO + store on sim +#endif + + new_m_state(plmn, GSM322_M2_ON_PLMN); + + return 0; +} + +/* indicate selected PLMN */ +static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + + vty_notify(ms, NULL); + vty_notify(ms, "Selected Network: %s, %s\n", + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + + return gsm322_m_go_on_plmn(ms, msg); +} + +/* previously selected PLMN becomes available again */ +static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + + new_m_state(plmn, GSM322_M1_TRYING_RPLMN); + + if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) { + struct msgb *nmsg; + + LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not " + "selected, so start selection.\n"); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + } + + return 0; +} + +/* the user has selected given PLMN */ +static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + struct msgb *nmsg; + + /* use user selection */ + plmn->mcc = gm->mcc; + plmn->mnc = gm->mnc; + + vty_notify(ms, NULL); + vty_notify(ms, "Selected Network: %s, %s\n", + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + + /* if selected PLMN is in list of forbidden PLMNs */ + gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc); + + new_m_state(plmn, GSM322_M4_TRYING_PLMN); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* auto mode selected */ +static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + + /* restart state machine */ + gsm322_m_switch_off(ms, msg); + ms->settings.plmn_mode = PLMN_MODE_AUTO; + gsm322_a_switch_on(ms, msg); + + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + return 0; +} + +/* if no cell is found in other states than in *_TRYING_* states */ +static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + + /* Tell cell selection process to handle "no cell found". */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* + * cell scanning process + */ + +/* select a suitable and allowable cell */ +static int gsm322_cs_select(struct osmocom_ms *ms, int any, int plmn_allowed) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_settings *set = &ms->settings; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_sysinfo *s; + int i, found = -1, power = 0; + uint8_t flags, mask; + uint16_t acc_class; + + /* set out access class depending on the cell selection type */ + if (any) { + acc_class = subscr->acc_class | 0x0400; /* add emergency */ + LOGP(DCS, LOGL_DEBUG, "Select using access class with " + "Emergency class.\n"); + } else { + acc_class = subscr->acc_class; + LOGP(DCS, LOGL_DEBUG, "Select using access class \n"); + } + + /* flags to match */ + mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO; + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C5_CHOOSE_CELL) + mask |= GSM322_CS_FLAG_BA; + flags = mask; /* all masked flags are requied */ + + /* loop through all scanned frequencies and select cell */ + for (i = 0; i <= 1023; i++) { + cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA; + s = cs->list[i].sysinfo; + + /* channel has no informations for us */ + if (!s || (cs->list[i].flags & mask) != flags) { + continue; + } + + /* check C1 criteria not fullfilled */ + // TODO: C1 is also dependant on power class and max power + if (rxlev2dbm(cs->list[i].rxlev) < s->rxlev_acc_min_db + && !set->stick) { + LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria " + "not met. (rxlev %s < min %d)\n", i, + gsm_print_rxlev(cs->list[i].rxlev), + s->rxlev_acc_min_db); + continue; + } + + /* if cell is barred and we don't override */ + if (!subscr->acc_barr + && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) { + LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is " + "barred.\n", i); + continue; + } + + /* if cell is in list of forbidden LAs */ + if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) { + LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in " + "list of forbidden LAs. (mcc=%s mnc=%s " + "lai=%04x)\n", i, gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); + continue; + } + + /* if cell is in list of forbidden PLMNs */ + if (plmn_allowed && gsm_subscr_is_forbidden_plmn(subscr, + s->mcc, s->mnc)) { + LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in " + "list of forbidden PLMNs. (mcc=%s mnc=%s)\n", i, + gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc)); + continue; + } + + /* if we have no access to the cell and we don't override */ + if (!subscr->acc_barr + && !(acc_class & (s->class_barr ^ 0xffff))) { + LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is " + "barred for out access. (access=%04x " + "barred=%04x)\n", i, acc_class, s->class_barr); + continue; + } + + /* store temporary available and allowable flag */ + cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA; + + /* if we search a specific PLMN, but it does not match */ + if (!any && cs->mcc && (cs->mcc != s->mcc + || cs->mnc != s->mnc)) { + LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell " + "does not match target PLMN. (mcc=%s " + "mnc=%s)\n", i, gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc)); + continue; + } + + LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%s " + "mcc=%s mnc=%s lac=%04x %s, %s)\n", i, + gsm_print_rxlev(cs->list[i].rxlev), + gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac, + gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc)); + + /* find highest power cell */ + if (found < 0 || cs->list[i].rxlev > power) { + power = cs->list[i].rxlev; + found = i; + } + } + + if (found >= 0) + LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found); + + return found; +} + +/* tune to first/next unscanned frequency and search for PLMN */ +static int gsm322_cs_scan(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i; +#ifndef SKIP_MAX_PER_BAND + int j; +#endif + uint8_t mask, flags; + uint32_t weight = 0, test = cs->scan_state; + + /* search for strongest unscanned cell */ + mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL; + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C5_CHOOSE_CELL) + mask |= GSM322_CS_FLAG_BA; + flags = mask; /* all masked flags are requied */ + for (i = 0; i <= 1023; i++) { +#ifndef SKIP_MAX_PER_BAND + /* skip if band has enough frequencies scanned (3.2.1) */ + for (j = 0; gsm_sup_smax[j].max; j++) { + if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) { + if (gsm_sup_smax[j].start >= i + && gsm_sup_smax[j].end <= i) + break; + } else { + if (gsm_sup_smax[j].end <= i + || gsm_sup_smax[j].start >= i) + break; + } + } + if (gsm_sup_smax[j].max) { + if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max) + continue; + } +#endif + /* search for unscanned frequency */ + if ((cs->list[i].flags & mask) == flags) { + /* weight depends on the power level + * if it is the same, it depends on arfcn + */ + test = cs->list[i].rxlev + 1; + test = (test << 16) | i; + if (test >= cs->scan_state) + continue; + if (test > weight) + weight = test; + } + } + cs->scan_state = weight; + + if (!weight) + gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs, + NULL); + + /* special case for PLMN search */ + if (cs->state == GSM322_PLMN_SEARCH && !weight) { + struct msgb *nmsg; + + /* create AA flag */ + cs->mcc = cs->mnc = 0; + gsm322_cs_select(ms, 0, 0); + + new_c_state(cs, GSM322_C0_NULL); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END); + LOGP(DCS, LOGL_INFO, "PLMN search finished.\n"); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* special case for HPLMN search */ + if (cs->state == GSM322_HPLMN_SEARCH && !weight) { + struct msgb *nmsg; + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n"); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); + + cs->arfcn = cs->sel_arfcn; + LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev " + "%s).\n", cs->arfcn, + gsm_print_rxlev(cs->list[cs->arfcn].rxlev)); + hack = 1; + gsm322_sync_to_cell(cs); + + return 0; + } + + /* if all frequencies have been searched */ + if (!weight) { + struct msgb *nmsg; +#if 0 + int found, any = 0; + + LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n"); + + /* just see, if we search for any cell */ + if (cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL + || cs->state == GSM322_C9_CHOOSE_ANY_CELL) + any = 1; + + found = gsm322_cs_select(ms, any, 0); + + /* if found */ + if (found >= 0) { + struct gsm322_plmn *plmn = &ms->plmn; + + LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found); + /* tune */ + cs->arfcn = found; + cs->si = cs->list[cs->arfcn].sysinfo; + hack = 1; + gsm322_sync_to_cell(cs); + + /* selected PLMN (manual) or any PLMN (auto) */ + switch (ms->settings.plmn_mode) { + case PLMN_MODE_AUTO: + if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + case PLMN_MODE_MANUAL: + if (plmn->state == GSM322_M3_NOT_ON_PLMN + && gsm322_is_plmn_avail(cs, plmn->mcc, + plmn->mnc)) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + } + + /* set selected cell */ + cs->selected = 1; + cs->sel_arfcn = cs->arfcn; + memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si)); + cs->sel_mcc = cs->si->mcc; + cs->sel_mnc = cs->si->mnc; + cs->sel_lac = cs->si->lac; + cs->sel_id = cs->si->cell_id; + + /* tell CS process about available cell */ + LOGP(DCS, LOGL_INFO, "Cell available.\n"); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND); + } else { +#endif + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* tell CS process about no cell available */ + LOGP(DCS, LOGL_INFO, "No cell available.\n"); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); +// } + if (!nmsg) + return -ENOMEM; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + + return 0; + } + + /* NOTE: We might already have system information from previous + * scan. But we need recent informations, so we scan again! + */ + + /* Tune to frequency for a while, to receive broadcasts. */ + cs->arfcn = weight & 1023; + LOGP(DCS, LOGL_DEBUG, "Scanning frequency %d (rxlev %s).\n", cs->arfcn, + gsm_print_rxlev(cs->list[cs->arfcn].rxlev)); + hack = 1; + gsm322_sync_to_cell(cs); + + /* Allocate/clean system information. */ + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO; + if (cs->list[cs->arfcn].sysinfo) + memset(cs->list[cs->arfcn].sysinfo, 0, + sizeof(struct gsm48_sysinfo)); + else + cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx, + struct gsm48_sysinfo); + if (!cs->list[cs->arfcn].sysinfo) + exit(-ENOMEM); + cs->si = cs->list[cs->arfcn].sysinfo; + + /* increase scan counter for each maximum scan range */ +#ifndef SKIP_MAX_PER_BAND + if (gsm_sup_smax[j].max) { + LOGP(DCS, LOGL_DEBUG, "%d frequencies left in band %d..%d\n", + gsm_sup_smax[j].max - gsm_sup_smax[j].temp, + gsm_sup_smax[j].start, gsm_sup_smax[j].end); + gsm_sup_smax[j].temp++; + } +#endif + + return 0; +} + +/* check if cell is now suitable and allowable */ +static int gsm322_cs_store(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + int found, any = 0; + + if (cs->state != GSM322_C2_STORED_CELL_SEL + && cs->state != GSM322_C1_NORMAL_CELL_SEL + && cs->state != GSM322_C6_ANY_CELL_SEL + && cs->state != GSM322_C4_NORMAL_CELL_RESEL + && cs->state != GSM322_C8_ANY_CELL_RESEL + && cs->state != GSM322_C5_CHOOSE_CELL + && cs->state != GSM322_C9_CHOOSE_ANY_CELL + && cs->state != GSM322_PLMN_SEARCH + && cs->state != GSM322_HPLMN_SEARCH) { + LOGP(DCS, LOGL_FATAL, "This must only happen during cell " + "(re-)selection, please fix!\n"); + return -EINVAL; + } + + /* store sysinfo */ + cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO; + if (s->cell_barr + && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp && + cs->list[cs->arfcn].sysinfo->sp_cbq)) + cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED; + else + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED; + +#if 0 + cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db; + cs->list[cs->arfcn].class_barr = s->class_barr; + cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch; +#endif + + /* store selected network */ + if (s->mcc) { +#if 0 + cs->list[cs->arfcn].mcc = s->mcc; + cs->list[cs->arfcn].mnc = s->mnc; + cs->list[cs->arfcn].lac = s->lac; +#endif + + if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac)) + cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD; + else + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD; + } + + LOGP(DCS, LOGL_DEBUG, "Scan frequency %d: Cell found. (rxlev %s " + "mcc %s mnc %s lac %04x)\n", cs->arfcn, + gsm_print_rxlev(cs->list[cs->arfcn].rxlev), + gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac); + + /* special case for PLMN search */ + if (cs->state == GSM322_PLMN_SEARCH) + /* tune to next cell */ + return gsm322_cs_scan(ms); + + /* special case for HPLMN search */ + if (cs->state == GSM322_HPLMN_SEARCH) { + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + + if (!gsm322_is_hplmn_avail(cs, subscr->imsi)) + /* tune to next cell */ + return gsm322_cs_scan(ms); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND); + LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n"); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* just see, if we search for any cell */ + if (cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL + || cs->state == GSM322_C9_CHOOSE_ANY_CELL) + any = 1; + + found = gsm322_cs_select(ms, any, 0); + + /* if not found */ + if (found < 0) { + LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n"); + /* tune to next cell */ + return gsm322_cs_scan(ms); + } + + LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found); + /* tune */ + cs->arfcn = found; + cs->si = cs->list[cs->arfcn].sysinfo; + hack = 1; + gsm322_sync_to_cell(cs); + + /* selected PLMN (manual) or any PLMN (auto) */ + switch (ms->settings.plmn_mode) { + case PLMN_MODE_AUTO: + if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + case PLMN_MODE_MANUAL: + if (plmn->state == GSM322_M3_NOT_ON_PLMN + && gsm322_is_plmn_avail(cs, plmn->mcc, + plmn->mnc)) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + } + + /* set selected cell */ + cs->selected = 1; + cs->sel_arfcn = cs->arfcn; + memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si)); + cs->sel_mcc = cs->si->mcc; + cs->sel_mnc = cs->si->mnc; + cs->sel_lac = cs->si->lac; + cs->sel_id = cs->si->cell_id; + + /* tell CS process about available cell */ + LOGP(DCS, LOGL_INFO, "Cell available.\n"); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + + return 0; +} + +/* process system information when returing to idle mode */ +struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s; + struct gsm322_ba_list *ba = NULL; + int i; + uint8_t freq[128]; + + if (!cs) { + LOGP(DCS, LOGL_INFO, "No BA, because no cell selected\n"); + return ba; + } + s = cs->si; + if (!s) { + LOGP(DCS, LOGL_INFO, "No BA, because no sysinfo\n"); + return ba; + } + + /* collect system information received during dedicated mode */ + if (s->si5 && (!s->nb_ext_ind_si5 || s->si5bis)) { + /* find or create ba list */ + ba = gsm322_find_ba_list(cs, s->mcc, s->mnc); + if (!ba) { + ba = talloc_zero(l23_ctx, struct gsm322_ba_list); + if (!ba) + return NULL; + ba->mcc = s->mcc; + ba->mnc = s->mnc; + llist_add_tail(&ba->entry, &cs->ba_list); + } + /* update (add) ba list */ + memset(freq, 0, sizeof(freq)); + for (i = 0; i <= 1023; i++) { + if ((s->freq[i].mask & (FREQ_TYPE_SERV + | FREQ_TYPE_NCELL | FREQ_TYPE_REP))) + freq[i >> 3] |= (1 << (i & 7)); + } + if (!!memcmp(freq, ba->freq, sizeof(freq))) { + LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s " + "%s, %s).\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + memcpy(ba->freq, freq, sizeof(freq)); + } + } + + return ba; +} + +/* store BA whenever a system informations changes */ +static int gsm322_store_ba_list(struct gsm322_cellsel *cs, + struct gsm48_sysinfo *s) +{ + struct gsm322_ba_list *ba; + int i; + uint8_t freq[128]; + + /* find or create ba list */ + ba = gsm322_find_ba_list(cs, s->mcc, s->mnc); + if (!ba) { + ba = talloc_zero(l23_ctx, struct gsm322_ba_list); + if (!ba) + return -ENOMEM; + ba->mcc = s->mcc; + ba->mnc = s->mnc; + llist_add_tail(&ba->entry, &cs->ba_list); + } + /* update ba list */ + memset(freq, 0, sizeof(freq)); + freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7)); + for (i = 0; i <= 1023; i++) { + if ((s->freq[i].mask & + (FREQ_TYPE_SERV | FREQ_TYPE_NCELL | FREQ_TYPE_REP))) + freq[i >> 3] |= (1 << (i & 7)); + } + if (!!memcmp(freq, ba->freq, sizeof(freq))) { + LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s " + "%s, %s).\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + memcpy(ba->freq, freq, sizeof(freq)); + } + + return 0; +} + +/* process system information during camping on a cell */ +static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) +{ +// struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + struct msgb *nmsg; + +#if 0 + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n"); + return -EBUSY; + } +#endif + + /* Store BA if we have full system info about cells and neigbor cells. + * Depending on the extended bit in the channel description, + * we require more or less system informations about neighbor cells + */ + if (s->mcc + && s->mnc + && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1 + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2 + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter) + && s->si1 + && s->si2 + && (!s->nb_ext_ind_si2 + || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) + || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 + && s->nb_ext_ind_si2bis))) + gsm322_store_ba_list(cs, s); + + /* update sel_si, if all relevant system informations received */ + if (s->si1 && s->si2 && s->si3 + && (!s->nb_ext_ind_si2 + || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) + || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 + && s->nb_ext_ind_si2bis))) { + if (cs->selected) { + LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is " + "updated.\n"); + memcpy(&cs->sel_si, s, sizeof(cs->sel_si)); + //gsm48_sysinfo_dump(s, print_dcs, NULL); + } + } + + /* check for barred cell */ + if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) { + /* check if cell becomes barred */ + if (!subscr->acc_barr && s->cell_barr + && !(cs->list[cs->arfcn].sysinfo + && cs->list[cs->arfcn].sysinfo->sp + && cs->list[cs->arfcn].sysinfo->sp_cbq)) { + LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n"); + trigger_resel: + /* mark cell as unscanned */ + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO; + if (cs->list[cs->arfcn].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + cs->arfcn); + talloc_free(cs->list[cs->arfcn].sysinfo); + cs->list[cs->arfcn].sysinfo = NULL; + gsm322_unselect_cell(cs); + } + /* trigger reselection without queueing, + * because other sysinfo message may be queued + * before + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL); + if (!nmsg) + return -ENOMEM; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + + return 0; + } + /* check if cell access becomes barred */ + if (!((subscr->acc_class & 0xfbff) + & (s->class_barr ^ 0xffff))) { + LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n"); + goto trigger_resel; + } + } + + /* check if MCC, MNC, LAC, cell ID changes */ + if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc + || cs->sel_lac != s->lac) { + LOGP(DCS, LOGL_NOTICE, "Cell changes location area. " + "This is not good!\n"); + goto trigger_resel; + } + if (cs->sel_id != s->cell_id) { + LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. " + "This is not good!\n"); + goto trigger_resel; + } + + return 0; +} + +/* process system information during channel scanning */ +static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + + /* no sysinfo if we are not done with power scan */ + if (cs->powerscan) { + LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n"); + return -EINVAL; + } + + /* Store BA if we have full system info about cells and neigbor cells. + * Depending on the extended bit in the channel description, + * we require more or less system informations about neighbor cells + */ + if (s->mcc + && s->mnc + && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1 + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2 + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter) + && s->si1 + && s->si2 + && (!s->nb_ext_ind_si2 || s->si2bis) + && (!s->si2ter_ind || s->si2ter)) + gsm322_store_ba_list(cs, s); + + /* all relevant system informations received */ + if (s->si1 && s->si2 && s->si3 + && (!s->nb_ext_ind_si2 || s->si2bis) + && (!s->si2ter_ind || s->si2ter)) { + LOGP(DCS, LOGL_DEBUG, "Received relevant sysinfo.\n"); + /* stop timer */ + stop_cs_timer(cs); + + //gsm48_sysinfo_dump(s, print_dcs, NULL); + + /* store sysinfo and continue scan */ + return gsm322_cs_store(ms); + } + + /* wait for more sysinfo or timeout */ + return 0; +} + +static void gsm322_cs_timeout(void *arg) +{ + struct gsm322_cellsel *cs = arg; + struct osmocom_ms *ms = cs->ms; + + /* if we have no lock, we retry */ + if (cs->ccch_state != GSM322_CCCH_ST_SYNC) + LOGP(DCS, LOGL_INFO, "Cell selection failed, sync timeout.\n"); + else + LOGP(DCS, LOGL_INFO, "Cell selection failed, read timeout.\n"); + + /* remove system information */ + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO; + if (cs->list[cs->arfcn].sysinfo) { + LOGP(DCS, LOGL_DEBUG, "free sysinfo arfcn=%d\n", cs->arfcn); + talloc_free(cs->list[cs->arfcn].sysinfo); + cs->list[cs->arfcn].sysinfo = NULL; + gsm322_unselect_cell(cs); + } + + /* tune to next cell */ + gsm322_cs_scan(ms); + + return; +} + +/* + * power scan process + */ + +/* search for block of unscanned frequencies and start scanning */ +static int gsm322_cs_powerscan(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_settings *set = &ms->settings; + int i, s = -1, e; + uint8_t mask, flags; + + again: + + mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER; + flags = GSM322_CS_FLAG_SUPPORT; + + /* in case of sticking to a cell, we only select it */ + if (set->stick) { + LOGP(DCS, LOGL_DEBUG, "Scanning power for sticked cell.\n"); + i = set->stick_arfcn; + if ((cs->list[i].flags & mask) == flags) + s = e = i; + } else { + /* search for first frequency to scan */ + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C5_CHOOSE_CELL) { + LOGP(DCS, LOGL_DEBUG, "Scanning power for stored BA " + "list.\n"); + mask |= GSM322_CS_FLAG_BA; + flags |= GSM322_CS_FLAG_BA; + } else + LOGP(DCS, LOGL_DEBUG, "Scanning power for all " + "frequencies.\n"); + for (i = 0; i <= 1023; i++) { + if ((cs->list[i].flags & mask) == flags) { + s = e = i; + break; + } + } + } + + /* if there is no more frequency, we can tune to that cell */ + if (s < 0) { + int found = 0; + + /* stop power level scanning */ + cs->powerscan = 0; + + /* check if not signal is found */ + for (i = 0; i <= 1023; i++) { + if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL)) + found++; + } + if (!found) { + struct msgb *nmsg; + + LOGP(DCS, LOGL_INFO, "Found no frequency.\n"); + /* on normal cell selection, start over */ + if (cs->state == GSM322_C1_NORMAL_CELL_SEL) { + for (i = 0; i <= 1023; i++) { + /* clear flag that this was scanned */ + cs->list[i].flags &= + ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free " + "sysinfo arfcn=%d\n", + i); + talloc_free( + cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } + /* no cell selected */ + gsm322_unselect_cell(cs); + goto again; + } + /* on other cell selection, indicate "no cell found" */ + /* NOTE: PLMN search process handles it. + * If not handled there, CS process gets indicated. + * If we would continue to process CS, then we might get + * our list of scanned cells disturbed. + */ + if (cs->state == GSM322_PLMN_SEARCH) + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_PLMN_SEARCH_END); + else + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + /* if HPLMN search, select last frequency */ + if (cs->state == GSM322_HPLMN_SEARCH) { + new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); + + cs->arfcn = cs->sel_arfcn; + LOGP(DCS, LOGL_INFO, "Tuning back to frequency " + "%d (rxlev %s).\n", cs->arfcn, + gsm_print_rxlev( + cs->list[cs->arfcn].rxlev)); + hack = 1; + gsm322_sync_to_cell(cs); + + } else + new_c_state(cs, GSM322_C0_NULL); + + return 0; + } + LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found); + cs->scan_state = 0xffffffff; /* higher than high */ + /* clear counter of scanned frequencies of each range */ + for (i = 0; gsm_sup_smax[i].max; i++) + gsm_sup_smax[i].temp = 0; + return gsm322_cs_scan(ms); + } + + /* search last frequency to scan (en block) */ + e = i; + if (!set->stick) { + for (i = s + 1; i <= 1023; i++) { + if ((cs->list[i].flags & mask) == flags) + e = i; + else + break; + } + } + + LOGP(DCS, LOGL_DEBUG, "Scanning frequencies. (%d..%d)\n", s, e); + + /* start scan on radio interface */ + if (!cs->powerscan) { + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + cs->powerscan = 1; + } +//#warning TESTING!!!! +//usleep(300000); + return l1ctl_tx_pm_req_range(ms, s, e); +} + +int gsm322_l1_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + struct gsm322_cellsel *cs; + struct osmobb_meas_res *mr; + struct osmobb_fbsb_res *fr; + int i; + int8_t rxlev; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_PM_RES: + mr = signal_data; + ms = mr->ms; + cs = &ms->cellsel; + if (!cs->powerscan) + return -EINVAL; + i = mr->band_arfcn & 1023; + rxlev = mr->rx_lev; + if ((cs->list[i].flags & GSM322_CS_FLAG_POWER)) { + LOGP(DCS, LOGL_ERROR, "Getting PM for frequency %d " + "twice. Overwriting the first! Please fix " + "prim_pm.c\n", i); + } + cs->list[i].rxlev = rxlev; + cs->list[i].flags |= GSM322_CS_FLAG_POWER; + cs->list[i].flags &= ~GSM322_CS_FLAG_SIGNAL; + /* if minimum level is reached or if we stick to a cell */ + if (rxlev2dbm(rxlev) >= ms->settings.min_rxlev_db + || ms->settings.stick) { + cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL; + LOGP(DCS, LOGL_INFO, "Found signal (frequency %d " + "rxlev %s (%d))\n", i, + gsm_print_rxlev(rxlev), rxlev); + } + break; + case S_L1CTL_PM_DONE: + LOGP(DCS, LOGL_DEBUG, "Done with power scanning range.\n"); + ms = signal_data; + cs = &ms->cellsel; + if (!cs->powerscan) + return -EINVAL; + gsm322_cs_powerscan(ms); + break; + case S_L1CTL_FBSB_RESP: + fr = signal_data; + ms = fr->ms; + cs = &ms->cellsel; + if (cs->ccch_state == GSM322_CCCH_ST_INIT) { + LOGP(DCS, LOGL_INFO, "Channel synched. (ARFCN=%d, " + "snr=%u, BSIC=%u)\n", cs->arfcn, fr->snr, + fr->bsic); + cs->ccch_state = GSM322_CCCH_ST_SYNC; + if (cs->si) + cs->si->bsic = fr->bsic; +#if 0 + stop_cs_timer(cs); + + /* in dedicated mode */ + if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND) + return gsm48_rr_tx_rand_acc(ms, NULL); +#endif + + /* set timer for reading BCCH */ + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C1_NORMAL_CELL_SEL + || cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C4_NORMAL_CELL_RESEL + || cs->state == GSM322_C8_ANY_CELL_RESEL + || cs->state == GSM322_C5_CHOOSE_CELL + || cs->state == GSM322_C9_CHOOSE_ANY_CELL + || cs->state == GSM322_PLMN_SEARCH + || cs->state == GSM322_HPLMN_SEARCH) + start_cs_timer(cs, ms->support.scan_to, 0); + // TODO: timer depends on BCCH config + + /* set downlink signalling failure criterion */ + ms->meas.ds_fail = ms->meas.dsc = ms->settings.dsc_max; + LOGP(DRR, LOGL_INFO, "using DSC of %d\n", ms->meas.dsc); + } + break; + case S_L1CTL_FBSB_ERR: +#if 0 + if (hack) { + ms = signal_data; + cs = &ms->cellsel; + gsm322_sync_to_cell(cs); + hack--; + LOGP(DCS, LOGL_INFO, "Channel sync error, try again\n"); + break; + } +#endif + LOGP(DCS, LOGL_INFO, "Channel sync error.\n"); + ms = signal_data; + cs = &ms->cellsel; + + stop_cs_timer(cs); + if (cs->selected) + gsm322_cs_loss(cs); + else + gsm322_cs_timeout(cs); + break; + case S_L1CTL_LOSS_IND: + ms = signal_data; + cs = &ms->cellsel; + gsm322_cs_loss(cs); + break; + case S_L1CTL_RESET: + ms = signal_data; + if (ms->mmlayer.power_off_idle) { + mobile_exit(ms, 1); + return 0; + } + break; + } + + return 0; +} + +static void gsm322_cs_loss(void *arg) +{ + struct gsm322_cellsel *cs = arg; + struct osmocom_ms *ms = cs->ms; + struct gsm48_rrlayer *rr = &ms->rrlayer; + + LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n"); + + if (cs->state == GSM322_C3_CAMPED_NORMALLY + || cs->state == GSM322_C7_CAMPED_ANY_CELL) { + if (rr->state == GSM48_RR_ST_IDLE) { + struct msgb *nmsg; + + LOGP(DCS, LOGL_INFO, "Loss of CCCH, Trigger " + "re-selection.\n"); + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL); + if (!nmsg) + return; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + } else { + LOGP(DCS, LOGL_INFO, "Loss of SACCH, Trigger RR " + "abort.\n"); + + /* keep cell info for re-selection */ + + gsm48_rr_los(ms); + /* be shure that nothing else is done after here + * because the function call above may cause + * to return from idle state and trigger cell re-sel. + */ + } + } + + return; +} + +/* + * handler for cell selection process + */ + +/* start PLMN search */ +static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i; + + new_c_state(cs, GSM322_PLMN_SEARCH); + + /* mark all frequencies except our own BA to be scanned */ + for (i = 0; i <= 1023; i++) { + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + gsm322_unselect_cell(cs); + } + } + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start HPLMN search */ +static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i; + + new_c_state(cs, GSM322_HPLMN_SEARCH); + + /* mark all frequencies except our own BA to be scanned */ + for (i = 0; i <= 1023; i++) { + if (i != cs->sel_arfcn + && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO) + && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) { + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } + } + + /* no cell selected */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start stored cell selection */ +static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i; + + new_c_state(cs, GSM322_C2_STORED_CELL_SEL); + + /* flag all frequencies that are in current band allocation */ + for (i = 0; i <= 1023; i++) { + if ((ba->freq[i >> 3] & (1 << (i & 7)))) + cs->list[i].flags |= GSM322_CS_FLAG_BA; + else + cs->list[i].flags &= ~GSM322_CS_FLAG_BA; + } + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start noraml cell selection */ +static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i; + + /* except for stored cell selection state, we weed to rescan ?? */ + if (cs->state != GSM322_C2_STORED_CELL_SEL) { + for (i = 0; i <= 1023; i++) { + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } + } + + new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL); + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start any cell selection */ +static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + /* in case we already tried any cell (re-)selection, power scan again */ + if (cs->state == GSM322_C0_NULL + || cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL) { + int i; + + for (i = 0; i <= 1023; i++) { + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } + } + /* after re-selection, indicate no cell found */ + if (cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL) { + struct msgb *nmsg; + + /* tell that we have no cell found */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + } else { + new_c_state(cs, GSM322_C6_ANY_CELL_SEL); + } + + cs->mcc = cs->mnc = 0; + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start noraml cell re-selection */ +static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL); + + /* NOTE: We keep our scan info we have so far. + * This may cause a skip in power scan. */ + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start any cell re-selection */ +static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + new_c_state(cs, GSM322_C8_ANY_CELL_RESEL); + + /* NOTE: We keep our scan info we have so far. + * This may cause a skip in power scan. */ + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* a suitable cell was found, so we camp normally */ +static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; + + LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s " + "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc), + gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc), + gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + + /* tell that we have selected a (new) cell */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); + + return 0; +} + +/* a not suitable cell was found, so we camp on any cell */ +static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; + + LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s " + "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc), + gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc), + gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + + + /* tell that we have selected a (new) cell */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL); + + return 0; +} + +/* create temporary ba range with given frequency ranges */ +struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms, + uint32_t *range, uint8_t ranges) +{ + static struct gsm322_ba_list ba; + uint16_t lower, higher; + + memset(&ba, 0, sizeof(ba)); + + while(ranges--) { + lower = *range & 1023; + higher = (*range >> 16) & 1023; + range++; + LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher); + /* GSM 05.08 6.3 */ + while (1) { + ba.freq[lower >> 3] |= 1 << (lower & 7); + if (lower == higher) + break; + lower = (lower + 1) & 1023; + } + } + + return &ba; +} + +/* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */ +static int gsm322_cs_choose(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_ba_list *ba = NULL; + int i; + + /* NOTE: The call to this function is synchron to RR layer, so + * we may access the BA range there. + */ + if (rr->ba_ranges) + ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges); + else { + LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n"); + /* get and update BA of last received sysinfo 5* */ + ba = gsm322_cs_sysinfo_sacch(ms); + if (!ba) { + LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored " + "BA list.\n"); + ba = gsm322_find_ba_list(cs, cs->sel_si.mcc, + cs->sel_si.mnc); + } + } + + if (!ba) { + struct msgb *nmsg; + + LOGP(DCS, LOGL_INFO, "No BA list to use.\n"); + + /* tell CS to start over */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + + return 0; + } + + /* flag all frequencies that are in current band allocation */ + for (i = 0; i <= 1023; i++) { + if (cs->state == GSM322_C5_CHOOSE_CELL) { + if ((ba->freq[i >> 3] & (1 << (i & 7)))) { + cs->list[i].flags |= GSM322_CS_FLAG_BA; + } else { + cs->list[i].flags &= ~GSM322_CS_FLAG_BA; + } + } + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start 'Choose cell' after returning to idle mode */ +static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + + /* After location updating, we choose the last cell */ + if (gm->same_cell) { + struct msgb *nmsg; + + if (!cs->selected) { + printf("No cell selected when ret.idle, please fix!\n"); + exit(0L); + } + cs->arfcn = cs->sel_arfcn; + + /* be sure to go to current camping frequency on return */ + LOGP(DCS, LOGL_INFO, "Selecting frequency %d. after LOC.UPD.\n", + cs->arfcn); + hack = 1; + gsm322_sync_to_cell(cs); + cs->si = cs->list[cs->arfcn].sysinfo; + + new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); + + /* tell that we have selected the cell, so RR returns IDLE */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + return 0; + } + + new_c_state(cs, GSM322_C5_CHOOSE_CELL); + + return gsm322_cs_choose(ms); +} + +/* start 'Choose any cell' after returning to idle mode */ +static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL); + + return gsm322_cs_choose(ms); +} + +/* a new PLMN is selected by PLMN search process */ +static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_ba_list *ba; + + cs->mcc = plmn->mcc; + cs->mnc = plmn->mnc; + + LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s " + "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc), + gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc), + gsm_get_mnc(cs->mcc, cs->mnc)); + + /* search for BA list */ + ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc); + + if (ba) { + LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n"); + return gsm322_c_stored_cell_sel(ms, ba); + } else { + LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n"); + return gsm322_c_normal_cell_sel(ms, msg); + } +} + +/* go connected mode */ +static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + /* check for error */ + if (!cs->selected) + return -EINVAL; + cs->arfcn = cs->sel_arfcn; + + /* be sure to go to current camping frequency on return */ + LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn); + hack = 1; + gsm322_sync_to_cell(cs); + cs->si = cs->list[cs->arfcn].sysinfo; + + return 0; +} + +static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + /* check for error */ + if (!cs->selected) + return -EINVAL; + cs->arfcn = cs->sel_arfcn; + + /* be sure to go to current camping frequency on return */ + LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn); + hack = 1; + gsm322_sync_to_cell(cs); + cs->si = cs->list[cs->arfcn].sysinfo; + + return 0; +} + +/* switch on */ +static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + /* if no SIM is is MS */ + if (!subscr->sim_valid) { + LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n"); + return gsm322_c_any_cell_sel(ms, msg); + } + LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n"); + + /* stay in NULL state until PLMN is selected */ + + return 0; +} + +/* + * state machines + */ + +/* state machine for automatic PLMN selection events */ +static struct plmnastatelist { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} plmnastatelist[] = { + {SBIT(GSM322_A0_NULL), + GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on}, + + /* special case for full search */ + {SBIT(GSM322_A0_NULL), + GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn}, + + {ALL_STATES, + GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off}, + + {SBIT(GSM322_A0_NULL) | SBIT(GSM322_A6_NO_SIM), + GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on}, + + {ALL_STATES, + GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert}, + + {ALL_STATES, + GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed}, + + {ALL_STATES, + GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed}, + + {SBIT(GSM322_A1_TRYING_RPLMN), + GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn}, + + {SBIT(GSM322_A1_TRYING_RPLMN), + GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn}, + + {SBIT(GSM322_A1_TRYING_RPLMN), + GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn}, + + {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN), + GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected}, + + {SBIT(GSM322_A2_ON_PLMN), + GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na}, + + {SBIT(GSM322_A2_ON_PLMN), + GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start}, + + {SBIT(GSM322_A2_ON_PLMN), + GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio}, + + {SBIT(GSM322_A2_ON_PLMN), + GSM322_EVENT_USER_RESEL, gsm322_a_user_resel}, + + {SBIT(GSM322_A3_TRYING_PLMN), + GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn}, + + {SBIT(GSM322_A3_TRYING_PLMN), + GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn}, + + {SBIT(GSM322_A3_TRYING_PLMN), + GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn}, + + {SBIT(GSM322_A5_HPLMN_SEARCH), + GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn}, + + {SBIT(GSM322_A5_HPLMN_SEARCH), + GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn}, + + {SBIT(GSM322_A4_WAIT_FOR_PLMN), + GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail}, + + {SBIT(GSM322_A4_WAIT_FOR_PLMN), + GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_plmn_avail}, + + {ALL_STATES, + GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual}, + + {ALL_STATES, + GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found}, +}; + +#define PLMNASLLEN \ + (sizeof(plmnastatelist) / sizeof(struct plmnastatelist)) + +static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + int msg_type = gm->msg_type; + int rc; + int i; + + LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN " + "selection in state '%s'\n", ms->name, get_event_name(msg_type), + plmn_a_state_names[plmn->state]); + /* find function for current state and message */ + for (i = 0; i < PLMNASLLEN; i++) + if ((msg_type == plmnastatelist[i].type) + && ((1 << plmn->state) & plmnastatelist[i].states)) + break; + if (i == PLMNASLLEN) { + LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n"); + return 0; + } + + rc = plmnastatelist[i].rout(ms, msg); + + return rc; +} + +/* state machine for manual PLMN selection events */ +static struct plmnmstatelist { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} plmnmstatelist[] = { + {SBIT(GSM322_M0_NULL), + GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on}, + + {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) | + SBIT(GSM322_M2_ON_PLMN), + GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns}, + + {ALL_STATES, + GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off}, + + {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M5_NO_SIM), + GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on}, + + {ALL_STATES, + GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert}, + + {ALL_STATES, + GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed}, + + {SBIT(GSM322_M1_TRYING_RPLMN), + GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns}, + + {SBIT(GSM322_M1_TRYING_RPLMN), + GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns}, + + {SBIT(GSM322_M1_TRYING_RPLMN), + GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns}, + + {SBIT(GSM322_M1_TRYING_RPLMN), + GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected}, + + {SBIT(GSM322_M2_ON_PLMN), + GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns}, + + {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) | + SBIT(GSM322_M4_TRYING_PLMN), + GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed}, + + {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN), + GSM322_EVENT_USER_RESEL, gsm322_m_user_resel}, + + {SBIT(GSM322_M3_NOT_ON_PLMN), + GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail}, + + {SBIT(GSM322_M3_NOT_ON_PLMN), + GSM322_EVENT_CHOOSE_PLMN, gsm322_m_choose_plmn}, + + {SBIT(GSM322_M4_TRYING_PLMN), + GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn}, + + {SBIT(GSM322_M4_TRYING_PLMN), + GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns}, + + {SBIT(GSM322_M4_TRYING_PLMN), + GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns}, + + {SBIT(GSM322_M4_TRYING_PLMN), + GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns}, + + {ALL_STATES, + GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto}, + + {ALL_STATES, + GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found}, +}; + +#define PLMNMSLLEN \ + (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist)) + +static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + int msg_type = gm->msg_type; + int rc; + int i; + + LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection " + "in state '%s'\n", ms->name, get_event_name(msg_type), + plmn_m_state_names[plmn->state]); + /* find function for current state and message */ + for (i = 0; i < PLMNMSLLEN; i++) + if ((msg_type == plmnmstatelist[i].type) + && ((1 << plmn->state) & plmnmstatelist[i].states)) + break; + if (i == PLMNMSLLEN) { + LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n"); + return 0; + } + + rc = plmnmstatelist[i].rout(ms, msg); + + return rc; +} + +/* dequeue GSM 03.22 PLMN events */ +int gsm322_plmn_dequeue(struct osmocom_ms *ms) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&plmn->event_queue))) { + /* send event to PLMN select process */ + if (ms->settings.plmn_mode == PLMN_MODE_AUTO) + gsm322_a_event(ms, msg); + else + gsm322_m_event(ms, msg); + msgb_free(msg); + work = 1; /* work done */ + } + + return work; +} + +/* state machine for channel selection events */ +static struct cellselstatelist { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} cellselstatelist[] = { + {ALL_STATES, + GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on}, + + {ALL_STATES, + GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel}, + + {ALL_STATES, + GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn}, + + {ALL_STATES, + GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search}, + + {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) | + SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL), + GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally}, + + {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) | + SBIT(GSM322_C8_ANY_CELL_RESEL), + GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell}, + + {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) | + SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) | + SBIT(GSM322_C0_NULL), + GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel}, + + {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) | + SBIT(GSM322_C4_NORMAL_CELL_RESEL), + GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel}, + + {SBIT(GSM322_C3_CAMPED_NORMALLY), + GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1}, + + {SBIT(GSM322_C7_CAMPED_ANY_CELL), + GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2}, + + {SBIT(GSM322_C3_CAMPED_NORMALLY), + GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell}, + + {SBIT(GSM322_C7_CAMPED_ANY_CELL), + GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell}, + + {SBIT(GSM322_C3_CAMPED_NORMALLY), + GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel}, + + {SBIT(GSM322_C7_CAMPED_ANY_CELL), + GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel}, + + {SBIT(GSM322_C7_CAMPED_ANY_CELL), + GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel}, + + {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) | + SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) | + SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) | + SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH), + GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch}, + + {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL), + GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch}, + + {SBIT(GSM322_C3_CAMPED_NORMALLY), + GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search}, +}; + +#define CELLSELSLLEN \ + (sizeof(cellselstatelist) / sizeof(struct cellselstatelist)) + +int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + int msg_type = gm->msg_type; + int rc; + int i; + + if (msg_type != GSM322_EVENT_SYSINFO) + LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection " + "in state '%s'\n", ms->name, get_event_name(msg_type), + cs_state_names[cs->state]); + /* find function for current state and message */ + for (i = 0; i < CELLSELSLLEN; i++) + if ((msg_type == cellselstatelist[i].type) + && ((1 << cs->state) & cellselstatelist[i].states)) + break; + if (i == CELLSELSLLEN) { + LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n"); + return 0; + } + + rc = cellselstatelist[i].rout(ms, msg); + + return rc; +} + +/* dequeue GSM 03.22 cell selection events */ +int gsm322_cs_dequeue(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&cs->event_queue))) { + /* send event to cell selection process */ + gsm322_c_event(ms, msg); + msgb_free(msg); + work = 1; /* work done */ + } + + return work; +} + +/* + * dump lists + */ + +int gsm322_dump_sorted_plmn(struct osmocom_ms *ms) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_plmn_list *temp; + + printf("MCC |MNC |allowed|rx-lev\n"); + printf("-------+-------+-------+-------\n"); + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) { + printf("%s |%s%s |%s |%s\n", gsm_print_mcc(temp->mcc), + gsm_print_mnc(temp->mnc), + ((temp->mnc & 0x00f) == 0x00f) ? " ":"", + (temp->cause) ? "no ":"yes", + gsm_print_rxlev(temp->rxlev)); + } + + return 0; +} + +int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags, + void (*print)(void *, const char *, ...), void *priv) +{ + int i; + struct gsm48_sysinfo *s; + + print(priv, "arfcn |MCC |MNC |LAC |cell ID|forb.LA|prio |" + "min-db |max-pwr|rx-lev\n"); + print(priv, "-------+-------+-------+-------+-------+-------+-------+" + "-------+-------+-------\n"); + for (i = 0; i <= 1023; i++) { + s = cs->list[i].sysinfo; + if (!s || !(cs->list[i].flags & flags)) + continue; + print(priv, "%4d |", i); + if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) { + print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), + ((s->mnc & 0x00f) == 0x00f) ? " ":""); + print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id); + if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) + print(priv, "yes |"); + else + print(priv, "no |"); + if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED)) + print(priv, "barred |"); + else { + if (cs->list[i].sysinfo->cell_barr) + print(priv, "low |"); + else + print(priv, "normal |"); + } + print(priv, "%4d |%4d |%s\n", s->rxlev_acc_min_db, + s->ms_txpwr_max_cch, + gsm_print_rxlev(cs->list[i].rxlev)); + } else + print(priv, "n/a |n/a |n/a |n/a |n/a |" + "n/a |n/a |n/a\n"); + } + print(priv, "\n"); + + return 0; +} + +int gsm322_dump_forbidden_la(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_la_list *temp; + + print(priv, "MCC |MNC |LAC |cause\n"); + print(priv, "-------+-------+-------+-------\n"); + llist_for_each_entry(temp, &plmn->forbidden_la, entry) + print(priv, "%s |%s%s |0x%04x |#%d\n", + gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), + ((temp->mnc & 0x00f) == 0x00f) ? " ":"", + temp->lac, temp->cause); + + return 0; +} + +int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc, + void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm322_ba_list *ba; + int i; + + llist_for_each_entry(ba, &cs->ba_list, entry) { + if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc)) + continue; + print(priv, "Band Allocation of network: MCC %s MNC %s " + "(%s, %s)\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + for (i = 0; i <= 1023; i++) { + if ((ba->freq[i >> 3] & (1 << (i & 7)))) + print(priv, " %d", i); + } + print(priv, "\n"); + } + + return 0; +} + +/* + * initialization + */ + +int gsm322_init(struct osmocom_ms *ms) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + FILE *fp; + char filename[128]; + int i; + struct gsm322_ba_list *ba; + uint8_t buf[4]; + + LOGP(DPLMN, LOGL_INFO, "init PLMN process\n"); + LOGP(DCS, LOGL_INFO, "init Cell Selection process\n"); + + memset(plmn, 0, sizeof(*plmn)); + memset(cs, 0, sizeof(*cs)); + plmn->ms = ms; + cs->ms = ms; + + /* set initial state */ + plmn->state = 0; + cs->state = 0; + ms->settings.plmn_mode = PLMN_MODE_AUTO; + + /* init lists */ + INIT_LLIST_HEAD(&plmn->event_queue); + INIT_LLIST_HEAD(&cs->event_queue); + INIT_LLIST_HEAD(&plmn->sorted_plmn); + INIT_LLIST_HEAD(&plmn->forbidden_la); + INIT_LLIST_HEAD(&cs->ba_list); + + /* set supported frequencies in cell selection list */ + for (i = 0; i <= 1023; i++) + if ((ms->support.freq_map[i >> 3] & (1 << (i & 7)))) + cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT; + + /* read BA list */ + sprintf(filename, "/etc/osmocom/%s.ba", ms->name); + fp = fopen(filename, "r"); + if (fp) { + int rc; + + while(!feof(fp)) { + ba = talloc_zero(l23_ctx, struct gsm322_ba_list); + if (!ba) + return -ENOMEM; + rc = fread(buf, 4, 1, fp); + if (!rc) { + talloc_free(ba); + break; + } + ba->mcc = (buf[0] << 8) | buf[1]; + ba->mnc = (buf[2] << 8) | buf[3]; + rc = fread(ba->freq, sizeof(ba->freq), 1, fp); + if (!rc) { + talloc_free(ba); + break; + } + llist_add_tail(&ba->entry, &cs->ba_list); + LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s " + "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + } + fclose(fp); + } else + LOGP(DCS, LOGL_INFO, "No stored BA list\n"); + + return 0; +} + +int gsm322_exit(struct osmocom_ms *ms) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + struct llist_head *lh, *lh2; + struct msgb *msg; + FILE *fp; + char filename[128]; + struct gsm322_ba_list *ba; + uint8_t buf[4]; + int i; + + LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n"); + LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n"); + + /* stop cell selection process (if any) */ + new_c_state(cs, GSM322_C0_NULL); + + /* stop timers */ + stop_cs_timer(cs); + stop_plmn_timer(plmn); + + /* flush sysinfo */ + for (i = 0; i <= 1023; i++) { + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + cs->list[i].flags = 0; + } + + /* store BA list */ + sprintf(filename, "/etc/osmocom/%s.ba", ms->name); + fp = fopen(filename, "w"); + if (fp) { + int rc; + + llist_for_each_entry(ba, &cs->ba_list, entry) { + buf[0] = ba->mcc >> 8; + buf[1] = ba->mcc & 0xff; + buf[2] = ba->mnc >> 8; + buf[3] = ba->mnc & 0xff; + rc = fwrite(buf, 4, 1, fp); + rc = fwrite(ba->freq, sizeof(ba->freq), 1, fp); + LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s " + "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + } + fclose(fp); + } else + LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n"); + + /* free lists */ + while ((msg = msgb_dequeue(&plmn->event_queue))) + msgb_free(msg); + while ((msg = msgb_dequeue(&cs->event_queue))) + msgb_free(msg); + llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) { + llist_del(lh); + talloc_free(lh); + } + llist_for_each_safe(lh, lh2, &plmn->forbidden_la) { + llist_del(lh); + talloc_free(lh); + } + llist_for_each_safe(lh, lh2, &cs->ba_list) { + llist_del(lh); + talloc_free(lh); + } + return 0; +} + + diff --git a/src/host/layer23/src/mobile/gsm48_cc.c b/src/host/layer23/src/mobile/gsm48_cc.c new file mode 100644 index 00000000..d8976eb2 --- /dev/null +++ b/src/host/layer23/src/mobile/gsm48_cc.c @@ -0,0 +1,2161 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <osmocore/msgb.h> +#include <osmocore/utils.h> +#include <osmocore/gsm48.h> +#include <osmocore/talloc.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/mobile/mncc.h> +#include <osmocom/bb/mobile/transaction.h> +#include <osmocom/bb/mobile/gsm48_cc.h> + +extern void *l23_ctx; + +static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg); +static int gsm48_rel_null_free(struct gsm_trans *trans); +int mncc_release_ind(struct osmocom_ms *ms, struct gsm_trans *trans, + u_int32_t callref, int location, int value); +static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg); +static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg); + +/* + * init + */ + +int gsm48_cc_init(struct osmocom_ms *ms) +{ + struct gsm48_cclayer *cc = &ms->cclayer; + + cc->ms = ms; + + if (!cc->mncc_upqueue.next == 0) + return 0; + + LOGP(DCC, LOGL_INFO, "init Call Control\n"); + + INIT_LLIST_HEAD(&cc->mncc_upqueue); + + return 0; +} + +int gsm48_cc_exit(struct osmocom_ms *ms) +{ + struct gsm48_cclayer *cc = &ms->cclayer; + struct gsm_trans *trans, *trans2; + struct msgb *msg; + + LOGP(DCC, LOGL_INFO, "exit Call Control processes for %s\n", ms->name); + + llist_for_each_entry_safe(trans, trans2, &ms->trans_list, entry) { + if (trans->protocol == GSM48_PDISC_CC) + LOGP(DCC, LOGL_NOTICE, "Free pendig CC-transaction.\n"); + trans_free(trans); + } + + while ((msg = msgb_dequeue(&cc->mncc_upqueue))) + msgb_free(msg); + + return 0; +} + +/* + * messages + */ + +/* names of MNCC-SAP */ +static const struct value_string gsm_mncc_names[] = { + { MNCC_SETUP_REQ, "MNCC_SETUP_REQ" }, + { MNCC_SETUP_IND, "MNCC_SETUP_IND" }, + { MNCC_SETUP_RSP, "MNCC_SETUP_RSP" }, + { MNCC_SETUP_CNF, "MNCC_SETUP_CNF" }, + { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" }, + { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" }, + { MNCC_CALL_CONF_IND, "MNCC_CALL_CONF_IND" }, + { MNCC_CALL_PROC_REQ, "MNCC_CALL_PROC_REQ" }, + { MNCC_PROGRESS_REQ, "MNCC_PROGRESS_REQ" }, + { MNCC_ALERT_REQ, "MNCC_ALERT_REQ" }, + { MNCC_ALERT_IND, "MNCC_ALERT_IND" }, + { MNCC_NOTIFY_REQ, "MNCC_NOTIFY_REQ" }, + { MNCC_NOTIFY_IND, "MNCC_NOTIFY_IND" }, + { MNCC_DISC_REQ, "MNCC_DISC_REQ" }, + { MNCC_DISC_IND, "MNCC_DISC_IND" }, + { MNCC_REL_REQ, "MNCC_REL_REQ" }, + { MNCC_REL_IND, "MNCC_REL_IND" }, + { MNCC_REL_CNF, "MNCC_REL_CNF" }, + { MNCC_FACILITY_REQ, "MNCC_FACILITY_REQ" }, + { MNCC_FACILITY_IND, "MNCC_FACILITY_IND" }, + { MNCC_START_DTMF_IND, "MNCC_START_DTMF_IND" }, + { MNCC_START_DTMF_RSP, "MNCC_START_DTMF_RSP" }, + { MNCC_START_DTMF_REJ, "MNCC_START_DTMF_REJ" }, + { MNCC_STOP_DTMF_IND, "MNCC_STOP_DTMF_IND" }, + { MNCC_STOP_DTMF_RSP, "MNCC_STOP_DTMF_RSP" }, + { MNCC_MODIFY_REQ, "MNCC_MODIFY_REQ" }, + { MNCC_MODIFY_IND, "MNCC_MODIFY_IND" }, + { MNCC_MODIFY_RSP, "MNCC_MODIFY_RSP" }, + { MNCC_MODIFY_CNF, "MNCC_MODIFY_CNF" }, + { MNCC_MODIFY_REJ, "MNCC_MODIFY_REJ" }, + { MNCC_HOLD_IND, "MNCC_HOLD_IND" }, + { MNCC_HOLD_CNF, "MNCC_HOLD_CNF" }, + { MNCC_HOLD_REJ, "MNCC_HOLD_REJ" }, + { MNCC_RETRIEVE_IND, "MNCC_RETRIEVE_IND" }, + { MNCC_RETRIEVE_CNF, "MNCC_RETRIEVE_CNF" }, + { MNCC_RETRIEVE_REJ, "MNCC_RETRIEVE_REJ" }, + { MNCC_USERINFO_REQ, "MNCC_USERINFO_REQ" }, + { MNCC_USERINFO_IND, "MNCC_USERINFO_IND" }, + { MNCC_REJ_REQ, "MNCC_REJ_REQ" }, + { MNCC_REJ_IND, "MNCC_REJ_IND" }, + { MNCC_PROGRESS_IND, "MNCC_PROGRESS_IND" }, + { MNCC_CALL_PROC_IND, "MNCC_CALL_PROC_IND" }, + { MNCC_CALL_CONF_REQ, "MNCC_CALL_CONF_REQ" }, + { MNCC_START_DTMF_REQ, "MNCC_START_DTMF_REQ" }, + { MNCC_STOP_DTMF_REQ, "MNCC_STOP_DTMF_REQ" }, + { MNCC_HOLD_REQ, "MNCC_HOLD_REQ " }, + { MNCC_RETRIEVE_REQ, "MNCC_RETRIEVE_REQ" }, + { 0, NULL } +}; + +const char *get_mncc_name(int value) +{ + return get_value_string(gsm_mncc_names, value); +} + +/* push MMCC header and send to MM */ +static int gsm48_cc_to_mm(struct msgb *msg, struct gsm_trans *trans, + int msg_type) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_mmxx_hdr *mmh; + int emergency = 0; + + /* Add protocol type and transaction ID */ + gh->proto_discr = trans->protocol | (trans->transaction_id << 4); + + /* indicate emergency setup to MM layer */ + if (gh->msg_type == GSM48_MT_CC_EMERG_SETUP) + emergency = 1; + + /* push RR header */ + msgb_push(msg, sizeof(struct gsm48_mmxx_hdr)); + mmh = (struct gsm48_mmxx_hdr *)msg->data; + mmh->msg_type = msg_type; + mmh->ref = trans->callref; + mmh->transaction_id = trans->transaction_id; + mmh->emergency = emergency; + + /* send message to MM */ + LOGP(DCC, LOGL_INFO, "Sending '%s' using %s (callref=%x, " + "transaction_id=%d)\n", gsm48_cc_msg_name(gh->msg_type), + get_mmxx_name(msg_type), trans->callref, trans->transaction_id); + return gsm48_mmxx_downmsg(trans->ms, msg); +} + +/* enqueue message to application (MNCC-SAP) */ +static int mncc_recvmsg(struct osmocom_ms *ms, struct gsm_trans *trans, + int msg_type, struct gsm_mncc *mncc) +{ + struct gsm48_cclayer *cc = &ms->cclayer; + struct msgb *msg; + + if (trans) + LOGP(DCC, LOGL_INFO, "(ms %s ti %x) Sending '%s' to MNCC.\n", + ms->name, trans->transaction_id, + get_mncc_name(msg_type)); + else + LOGP(DCC, LOGL_INFO, "(ms %s ti -) Sending '%s' to MNCC.\n", + ms->name, get_mncc_name(msg_type)); + + mncc->msg_type = msg_type; + + msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC"); + if (!msg) + return -ENOMEM; + memcpy(msg->data, mncc, sizeof(struct gsm_mncc)); + msgb_enqueue(&cc->mncc_upqueue, msg); + + return 0; +} + +/* dequeue messages to layer 4 */ +int mncc_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_cclayer *cc = &ms->cclayer; + struct gsm_mncc *mncc; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&cc->mncc_upqueue))) { + mncc = (struct gsm_mncc *)msg->data; + if (cc->mncc_recv) + cc->mncc_recv(ms, mncc->msg_type, mncc); + work = 1; /* work done */ + talloc_free(msg); + } + + return work; +} + + +/* + * state transition + */ + +static void new_cc_state(struct gsm_trans *trans, int state) +{ + if (state > 31 || state < 0) + return; + + DEBUGP(DCC, "new state %s -> %s\n", + gsm48_cc_state_name(trans->cc.state), + gsm48_cc_state_name(state)); + + trans->cc.state = state; +} + +/* + * timers + */ + +/* timeout events of all timers */ +static void gsm48_cc_timeout(void *arg) +{ + struct gsm_trans *trans = arg; + int disconnect = 0, release = 0, abort = 1; + int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER; + int mo_location = GSM48_CAUSE_LOC_PRN_S_LU; + int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; + int l4_location = GSM48_CAUSE_LOC_PRN_S_LU; + struct gsm_mncc mo_rel, l4_rel; + + memset(&mo_rel, 0, sizeof(struct gsm_mncc)); + mo_rel.callref = trans->callref; + memset(&l4_rel, 0, sizeof(struct gsm_mncc)); + l4_rel.callref = trans->callref; + + LOGP(DCC, LOGL_INFO, "Timer T%x has fired.\n", trans->cc.Tcurrent); + + switch(trans->cc.Tcurrent) { + case 0x303: + /* abort if connection is not already esablished */ + if (trans->cc.state == GSM_CSTATE_MM_CONNECTION_PEND) + abort = 1; + else + release = 1; + l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; + break; + case 0x305: + release = 1; + mo_cause = trans->cc.msg.cause.value; + mo_location = trans->cc.msg.cause.location; + break; + case 0x308: + if (!trans->cc.T308_second) { + /* restart T308 a second time */ + gsm48_cc_tx_release(trans, &trans->cc.msg); + trans->cc.T308_second = 1; + break; /* stay in release state */ + } + /* release MM conn, got NULL state, free trans */ + gsm48_rel_null_free(trans); + + return; + case 0x310: + disconnect = 1; + l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; + break; + case 0x313: + disconnect = 1; + /* unknown, did not find it in the specs */ + break; + default: + release = 1; + } + + if ((release || abort) && trans->callref) { + /* process release towards layer 4 */ + mncc_release_ind(trans->ms, trans, trans->callref, + l4_location, l4_cause); + } + + if (disconnect && trans->callref) { + /* process disconnect towards layer 4 */ + mncc_set_cause(&l4_rel, l4_location, l4_cause); + mncc_recvmsg(trans->ms, trans, MNCC_DISC_IND, &l4_rel); + } + + /* process disconnect towards mobile station */ + if (disconnect || release || abort) { + mncc_set_cause(&mo_rel, mo_location, mo_cause); + mo_rel.cause.diag[0] = + ((trans->cc.Tcurrent & 0xf00) >> 8) + '0'; + mo_rel.cause.diag[1] = + ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0'; + mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0'; + mo_rel.cause.diag_len = 3; + + if (disconnect) + gsm48_cc_tx_disconnect(trans, &mo_rel); + if (release) + gsm48_cc_tx_release(trans, &mo_rel); + if (abort) { + /* release MM conn, got NULL state, free trans */ + gsm48_rel_null_free(trans); + } + } +} + +/* start various timers */ +static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, + int sec, int micro) +{ + LOGP(DCC, LOGL_INFO, "starting timer T%x with %d seconds\n", current, + sec); + trans->cc.timer.cb = gsm48_cc_timeout; + trans->cc.timer.data = trans; + bsc_schedule_timer(&trans->cc.timer, sec, micro); + trans->cc.Tcurrent = current; +} + +/* stop various timers */ +static void gsm48_stop_cc_timer(struct gsm_trans *trans) +{ + if (bsc_timer_pending(&trans->cc.timer)) { + LOGP(DCC, LOGL_INFO, "stopping pending timer T%x\n", + trans->cc.Tcurrent); + bsc_del_timer(&trans->cc.timer); + trans->cc.Tcurrent = 0; + } +} + +/* + * process handlers (misc) + */ + +/* Call Control Specific transaction release. + * gets called by trans_free, DO NOT CALL YOURSELF! + */ +void _gsm48_cc_trans_free(struct gsm_trans *trans) +{ + gsm48_stop_cc_timer(trans); + + /* send release to L4, if callref still exists */ + if (trans->callref) { + /* Ressource unavailable */ + mncc_release_ind(trans->ms, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + } + if (trans->cc.state != GSM_CSTATE_NULL) + new_cc_state(trans, GSM_CSTATE_NULL); +} + +/* release MM connection, go NULL state, free transaction */ +static int gsm48_rel_null_free(struct gsm_trans *trans) +{ + struct msgb *nmsg; + + /* release MM connection */ + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_REQ, trans->callref, + trans->transaction_id); + if (!nmsg) + return -ENOMEM; + LOGP(DCC, LOGL_INFO, "Sending MMCC_REL_REQ\n"); + gsm48_mmxx_downmsg(trans->ms, nmsg); + + new_cc_state(trans, GSM_CSTATE_NULL); + + trans->callref = 0; + trans_free(trans); + + return 0; +} + +void mncc_set_cause(struct gsm_mncc *data, int loc, int val) +{ + data->fields |= MNCC_F_CAUSE; + data->cause.coding = 0x3; + data->cause.location = loc; + data->cause.value = val; +} + +/* send release indication to upper layer */ +int mncc_release_ind(struct osmocom_ms *ms, struct gsm_trans *trans, + u_int32_t callref, int location, int value) +{ + struct gsm_mncc rel; + + memset(&rel, 0, sizeof(rel)); + rel.callref = callref; + mncc_set_cause(&rel, location, value); + return mncc_recvmsg(ms, trans, MNCC_REL_IND, &rel); +} + +/* sending status message in response to unknown message */ +static int gsm48_cc_tx_status(struct gsm_trans *trans, int cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + uint8_t *cause_ie, *call_state_ie; + + LOGP(DCC, LOGL_INFO, "sending STATUS (cause %d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_STATUS; + + cause_ie = msgb_put(nmsg, 3); + cause_ie[0] = 2; + cause_ie[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_PRN_S_LU; + cause_ie[2] = 0x80 | cause; + + call_state_ie = msgb_put(nmsg, 1); + call_state_ie[0] = 0xc0 | trans->cc.state; + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* reply status enquiry */ +static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg) +{ + LOGP(DCC, LOGL_INFO, "received STATUS ENQUIREY\n"); + + return gsm48_cc_tx_status(trans, GSM48_CC_CAUSE_RESP_STATUS_INQ); +} + +static int gsm48_cc_rx_status(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc_cause cause; + + if (payload_len < 1 || payload_len < gh->data[0] + 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of status message " + "error.\n"); + return -EINVAL; + } + gsm48_decode_cause(&cause, gh->data); + + LOGP(DCC, LOGL_INFO, "received STATUS (cause %d)\n", cause.value); + + return 0; +} + +/* + * process handlers (mobile originating call establish) + */ + +/* on SETUP request from L4, init MM connection */ +static int gsm48_cc_init_mm(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + struct gsm_mncc *data = arg; + struct gsm48_mmxx_hdr *nmmh; + + /* store setup message */ + memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); + + new_cc_state(trans, GSM_CSTATE_MM_CONNECTION_PEND); + + /* establish MM connection */ + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_EST_REQ, trans->callref, + trans->transaction_id); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *) nmsg->data; + if (data->emergency) + nmmh->emergency = 1; + LOGP(DCC, LOGL_INFO, "Sending MMCC_EST_REQ\n"); + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +/* abort connection prior SETUP */ +static int gsm48_cc_abort_mm(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + + /* abort MM connection */ + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_REQ, trans->callref, + trans->transaction_id); + if (!nmsg) + return -ENOMEM; + LOGP(DCC, LOGL_INFO, "Sending MMCC_REL_REQ\n"); + gsm48_mmxx_downmsg(trans->ms, nmsg); + + new_cc_state(trans, GSM_CSTATE_NULL); + + trans->callref = 0; + trans_free(trans); + + return 0; +} + +/* setup message from upper layer */ +static int gsm48_cc_tx_setup(struct gsm_trans *trans) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm_mncc *setup = &trans->cc.msg; + int rc, transaction_id; + uint8_t *ie; + + LOGP(DCC, LOGL_INFO, "sending SETUP\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + /* transaction id must not be assigned */ + if (trans->transaction_id != 0xff) { /* unasssigned */ + LOGP(DCC, LOGL_NOTICE, "TX Setup with assigned transaction. " + "This is not allowed!\n"); + /* Temporarily out of order */ + rc = mncc_release_ind(trans->ms, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_NORMAL_UNSPEC); + trans->callref = 0; + trans_free(trans); + return rc; + } + + /* Get free transaction_id */ + transaction_id = trans_assign_trans_id(trans->ms, GSM48_PDISC_CC, 0); + if (transaction_id < 0) { + /* no free transaction ID */ + rc = mncc_release_ind(trans->ms, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + trans->callref = 0; + trans_free(trans); + return rc; + } + trans->transaction_id = transaction_id; + + gh->msg_type = (setup->emergency) ? GSM48_MT_CC_EMERG_SETUP : + GSM48_MT_CC_SETUP; + + /* actually we have to start it when CM SERVICE REQUEST has been sent, + * but there is no primitive for that defined. i think it is ok to + * do it here rather than inventing MMCC-NOTIFY-IND. + */ + gsm48_start_cc_timer(trans, 0x303, GSM48_T303_MS); + + /* bearer capability (optional for emergency calls only) */ + if (setup->fields & MNCC_F_BEARER_CAP) + gsm48_encode_bearer_cap(nmsg, 0, &setup->bearer_cap); + if (!setup->emergency) { + /* facility */ + if (setup->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &setup->facility); + /* called party BCD number */ + if (setup->fields & MNCC_F_CALLED) + gsm48_encode_called(nmsg, &setup->called); + /* user-user */ + if (setup->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &setup->useruser); + /* ss version */ + if (setup->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &setup->ssversion); + /* CLIR suppression */ + if (setup->clir.sup) { + ie = msgb_put(nmsg, 1); + ie[0] = GSM48_IE_CLIR_SUPP; + } + /* CLIR invocation */ + if (setup->clir.inv) { + ie = msgb_put(nmsg, 1); + ie[0] = GSM48_IE_CLIR_INVOC; + } + /* cc cap */ + if (setup->fields & MNCC_F_CCCAP) + gsm48_encode_cccap(nmsg, &setup->cccap); + } + + /* actually MM CONNECTION PENDING */ + new_cc_state(trans, GSM_CSTATE_INITIATED); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* progress is received from lower layer */ +static int gsm48_cc_rx_progress(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc progress; + + LOGP(DCC, LOGL_INFO, "received PROGRESS\n"); + + memset(&progress, 0, sizeof(struct gsm_mncc)); + progress.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_PROGR_IND, 0); + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + progress.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&progress.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + /* store last progress indicator */ + trans->cc.prog_ind = progress.progress.descr; + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + progress.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&progress.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + return mncc_recvmsg(trans->ms, trans, MNCC_PROGRESS_IND, &progress); +} + +/* call proceeding is received from lower layer */ +static int gsm48_cc_rx_call_proceeding(struct gsm_trans *trans, + struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc call_proc; + + LOGP(DCC, LOGL_INFO, "sending CALL PROCEEDING\n"); + + gsm48_stop_cc_timer(trans); + + memset(&call_proc, 0, sizeof(struct gsm_mncc)); + call_proc.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); +#if 0 + /* repeat */ + if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR)) + call_conf.repeat = 1; + if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ)) + call_conf.repeat = 2; +#endif + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + call_proc.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&call_proc.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + call_proc.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&call_proc.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + call_proc.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&call_proc.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + /* store last progress indicator */ + trans->cc.prog_ind = call_proc.progress.descr; + } + + /* start T310, if last progress indicator was 1 or 2 or 64 */ + if (trans->cc.prog_ind == 1 + || trans->cc.prog_ind == 2 + || trans->cc.prog_ind == 64) + gsm48_start_cc_timer(trans, 0x310, GSM48_T310_MS); + + new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); + + return mncc_recvmsg(trans->ms, trans, MNCC_CALL_PROC_IND, + &call_proc); +} + +/* alerting is received by the lower layer */ +static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc alerting; + + LOGP(DCC, LOGL_INFO, "received ALERTING\n"); + + gsm48_stop_cc_timer(trans); + /* no T301 in MS call control */ + + memset(&alerting, 0, sizeof(struct gsm_mncc)); + alerting.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + alerting.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&alerting.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + alerting.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&alerting.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + alerting.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&alerting.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); + + return mncc_recvmsg(trans->ms, trans, MNCC_ALERT_IND, + &alerting); +} + +/* connect is received from lower layer */ +static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc connect; + + LOGP(DCC, LOGL_INFO, "received CONNECT\n"); + + gsm48_stop_cc_timer(trans); + + memset(&connect, 0, sizeof(struct gsm_mncc)); + connect.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + connect.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&connect.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* connected */ + if (TLVP_PRESENT(&tp, GSM48_IE_CONN_BCD)) { + connect.fields |= MNCC_F_CONNECTED; + gsm48_decode_connected(&connect.connected, + TLVP_VAL(&tp, GSM48_IE_CONN_BCD)-1); + } + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + connect.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&connect.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + connect.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&connect.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + /* ACTIVE state is set during this: */ + gsm48_cc_tx_connect_ack(trans, NULL); + + return mncc_recvmsg(trans->ms, trans, MNCC_SETUP_CNF, &connect); +} + +/* connect ack message from upper layer */ +static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending CONNECT ACKNOWLEDGE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CONNECT_ACK; + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* + * process handlers (mobile terminating call establish) + */ + +/* setup is received from lower layer */ +static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc setup; + + LOGP(DCC, LOGL_INFO, "received SETUP\n"); + + memset(&setup, 0, sizeof(struct gsm_mncc)); + setup.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + setup.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&setup.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + setup.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&setup.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + setup.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&setup.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* signal */ + if (TLVP_PRESENT(&tp, GSM48_IE_SIGNAL)) { + setup.fields |= MNCC_F_SIGNAL; + gsm48_decode_signal(&setup.signal, + TLVP_VAL(&tp, GSM48_IE_SIGNAL)-1); + } + /* calling party bcd number */ + if (TLVP_PRESENT(&tp, GSM48_IE_CALLING_BCD)) { + setup.fields |= MNCC_F_CALLING; + gsm48_decode_calling(&setup.calling, + TLVP_VAL(&tp, GSM48_IE_CALLING_BCD)-1); + } + /* called party bcd number */ + if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { + setup.fields |= MNCC_F_CALLED; + gsm48_decode_called(&setup.called, + TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); + } + /* redirecting party bcd number */ + if (TLVP_PRESENT(&tp, GSM48_IE_REDIR_BCD)) { + setup.fields |= MNCC_F_REDIRECTING; + gsm48_decode_redirecting(&setup.redirecting, + TLVP_VAL(&tp, GSM48_IE_REDIR_BCD)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + setup.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&setup.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + new_cc_state(trans, GSM_CSTATE_CALL_PRESENT); + + /* indicate setup to MNCC */ + mncc_recvmsg(trans->ms, trans, MNCC_SETUP_IND, &setup); + + return 0; +} + +/* call conf message from upper layer */ +static int gsm48_cc_tx_call_conf(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *confirm = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending CALL CONFIRMED (proceeding)\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CALL_CONF; + + new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); + + /* bearer capability */ + if (confirm->fields & MNCC_F_BEARER_CAP) + gsm48_encode_bearer_cap(nmsg, 0, &confirm->bearer_cap); + /* cause */ + if (confirm->fields & MNCC_F_CAUSE) + gsm48_encode_cause(nmsg, 0, &confirm->cause); + /* cc cap */ + if (confirm->fields & MNCC_F_CCCAP) + gsm48_encode_cccap(nmsg, &confirm->cccap); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* alerting message from upper layer */ +static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *alerting = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending ALERTING\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_ALERTING; + + /* facility */ + if (alerting->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &alerting->facility); + /* user-user */ + if (alerting->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &alerting->useruser); + /* ss version */ + if (alerting->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &alerting->ssversion); + + new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* connect message from upper layer */ +static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *connect = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending CONNECT\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x313, GSM48_T313_MS); + + /* facility */ + if (connect->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &connect->facility); + /* user-user */ + if (connect->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &connect->useruser); + /* ss version */ + if (connect->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &connect->ssversion); + + new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* connect ack is received from lower layer */ +static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc connect_ack; + + LOGP(DCC, LOGL_INFO, "received CONNECT ACKNOWLEDGE\n"); + + gsm48_stop_cc_timer(trans); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + memset(&connect_ack, 0, sizeof(struct gsm_mncc)); + connect_ack.callref = trans->callref; + return mncc_recvmsg(trans->ms, trans, MNCC_SETUP_COMPL_IND, + &connect_ack); +} + +/* + * process handlers (during active state) + */ + +/* notify message from upper layer */ +static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *notify = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending NOTIFY\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_NOTIFY; + + /* notify */ + gsm48_encode_notify(nmsg, notify->notify); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* notify is received from lower layer */ +static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc notify; + + LOGP(DCC, LOGL_INFO, "received NOTIFY\n"); + + memset(¬ify, 0, sizeof(struct gsm_mncc)); + notify.callref = trans->callref; + /* notify */ + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of notify message error.\n"); + return -EINVAL; + } + gsm48_decode_notify(¬ify.notify, gh->data); + + return mncc_recvmsg(trans->ms, trans, MNCC_NOTIFY_IND, ¬ify); +} + +/* start dtmf message from upper layer */ +static int gsm48_cc_tx_start_dtmf(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *dtmf = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending START DTMF\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_START_DTMF; + + /* keypad */ + gsm48_encode_keypad(nmsg, dtmf->keypad); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* start dtmf ack is received from lower layer */ +static int gsm48_cc_rx_start_dtmf_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc dtmf; + + LOGP(DCC, LOGL_INFO, "received START DTMF ACKNOWLEDGE\n"); + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* keypad facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { + dtmf.fields |= MNCC_F_KEYPAD; + gsm48_decode_keypad(&dtmf.keypad, + TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); + } + + return mncc_recvmsg(trans->ms, trans, MNCC_START_DTMF_RSP, &dtmf); +} + +/* start dtmf rej is received from lower layer */ +static int gsm48_cc_rx_start_dtmf_rej(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc dtmf; + + LOGP(DCC, LOGL_INFO, "received START DTMF REJECT\n"); + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + /* cause */ + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of dtmf reject message " + "error.\n"); + return -EINVAL; + } + gsm48_decode_cause(&dtmf.cause, gh->data); + + return mncc_recvmsg(trans->ms, trans, MNCC_START_DTMF_REJ, &dtmf); +} + +/* stop dtmf message from upper layer */ +static int gsm48_cc_tx_stop_dtmf(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending STOP DTMF\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_STOP_DTMF; + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* stop dtmf ack is received from lower layer */ +static int gsm48_cc_rx_stop_dtmf_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc dtmf; + + LOGP(DCC, LOGL_INFO, "received STOP DTMF ACKNOWLEDGE\n"); + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + + return mncc_recvmsg(trans->ms, trans, MNCC_STOP_DTMF_RSP, &dtmf); +} + +/* hold message from upper layer */ +static int gsm48_cc_tx_hold(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending HOLD\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_HOLD; + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* hold ack is received from lower layer */ +static int gsm48_cc_rx_hold_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc hold; + + LOGP(DCC, LOGL_INFO, "received HOLD ACKNOWLEDGE\n"); + + memset(&hold, 0, sizeof(struct gsm_mncc)); + hold.callref = trans->callref; + + return mncc_recvmsg(trans->ms, trans, MNCC_HOLD_CNF, &hold); +} + +/* hold rej is received from lower layer */ +static int gsm48_cc_rx_hold_rej(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc hold; + + LOGP(DCC, LOGL_INFO, "received HOLD REJECT\n"); + + memset(&hold, 0, sizeof(struct gsm_mncc)); + hold.callref = trans->callref; + /* cause */ + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of hold reject message " + "error.\n"); + return -EINVAL; + } + gsm48_decode_cause(&hold.cause, gh->data); + + return mncc_recvmsg(trans->ms, trans, MNCC_HOLD_REJ, &hold); +} + +/* retrieve message from upper layer */ +static int gsm48_cc_tx_retrieve(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending RETRIEVE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RETR; + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* retrieve ack is received from lower layer */ +static int gsm48_cc_rx_retrieve_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc retrieve; + + LOGP(DCC, LOGL_INFO, "received RETRIEVE ACKNOWLEDGE\n"); + + memset(&retrieve, 0, sizeof(struct gsm_mncc)); + retrieve.callref = trans->callref; + + return mncc_recvmsg(trans->ms, trans, MNCC_RETRIEVE_CNF, &retrieve); +} + +/* retrieve rej is received from lower layer */ +static int gsm48_cc_rx_retrieve_rej(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc retrieve; + + LOGP(DCC, LOGL_INFO, "received RETRIEVE REJECT\n"); + + memset(&retrieve, 0, sizeof(struct gsm_mncc)); + retrieve.callref = trans->callref; + /* cause */ + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of retrieve reject message " + "error.\n"); + return -EINVAL; + } + gsm48_decode_cause(&retrieve.cause, gh->data); + + return mncc_recvmsg(trans->ms, trans, MNCC_RETRIEVE_REJ, &retrieve); +} + +/* facility message from upper layer or from timer event */ +static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *fac = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending FACILITY\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_FACILITY; + + /* facility */ + gsm48_encode_facility(nmsg, 1, &fac->facility); + /* ss version */ + if (fac->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &fac->ssversion); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* facility is received from lower layer */ +static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc fac; + + LOGP(DCC, LOGL_INFO, "received FACILITY\n"); + + memset(&fac, 0, sizeof(struct gsm_mncc)); + fac.callref = trans->callref; + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of facility message " + "error.\n"); + return -EINVAL; + } + /* facility */ + gsm48_decode_facility(&fac.facility, gh->data); + + return mncc_recvmsg(trans->ms, trans, MNCC_FACILITY_IND, &fac); +} + +/* user info message from upper layer or from timer event */ +static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *user = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending USERINFO\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_USER_INFO; + + /* user-user */ + if (user->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 1, &user->useruser); + /* more data */ + if (user->more) + gsm48_encode_more(nmsg); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* user info is received from lower layer */ +static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc user; + + LOGP(DCC, LOGL_INFO, "received USERINFO\n"); + + memset(&user, 0, sizeof(struct gsm_mncc)); + user.callref = trans->callref; + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of userinfo message " + "error.\n"); + return -EINVAL; + } + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_USER_USER, 0); + /* user-user */ + gsm48_decode_useruser(&user.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + /* more data */ + if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) + user.more = 1; + + return mncc_recvmsg(trans->ms, trans, MNCC_USERINFO_IND, &user); +} + +/* modify message from upper layer or from timer event */ +static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending MODIFY\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY; + + gsm48_start_cc_timer(trans, 0x323, GSM48_T323_MS); + + /* bearer capability */ + gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* modify complete is received from lower layer */ +static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, + struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc modify; + + LOGP(DCC, LOGL_INFO, "received MODIFY COMPLETE\n"); + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of modify complete message " + "error.\n"); + return -EINVAL; + } + /* bearer capability */ + gsm48_decode_bearer_cap(&modify.bearer_cap, gh->data); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_CNF, &modify); +} + +/* modify reject is received from lower layer */ +static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + LOGP(DCC, LOGL_INFO, "received MODIFY REJECT\n"); + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of modify reject message " + "error.\n"); + return -EINVAL; + } + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + modify.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&modify.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_REJ, &modify); +} + +/* modify is received from lower layer */ +static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc modify; + + LOGP(DCC, LOGL_INFO, "received MODIFY\n"); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of modify message error.\n"); + return -EINVAL; + } + /* bearer capability */ + gsm48_decode_bearer_cap(&modify.bearer_cap, gh->data); + + new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); + + return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_IND, &modify); +} + +/* modify complete message from upper layer or from timer event */ +static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending MODIFY COMPLETE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; + + /* bearer capability */ + gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* modify reject message from upper layer or from timer event */ +static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending MODIFY REJECT\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; + + /* bearer capability */ + gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap); + /* cause */ + gsm48_encode_cause(nmsg, 1, &modify->cause); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* + * process handlers (call clearing) + */ + +static struct gsm_mncc_cause default_cause = { + .location = GSM48_CAUSE_LOC_PRN_S_LU, + .coding = 0, + .rec = 0, + .rec_val = 0, + .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, + .diag_len = 0, + .diag = { 0 }, +}; + +/* disconnect message from upper layer or from timer event */ +static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *disc = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending DISCONNECT\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_DISCONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x305, GSM48_T305_MS); + + /* cause */ + if (disc->fields & MNCC_F_CAUSE) + gsm48_encode_cause(nmsg, 1, &disc->cause); + else + gsm48_encode_cause(nmsg, 1, &default_cause); + + /* facility */ + if (disc->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &disc->facility); + /* progress */ + if (disc->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(nmsg, 0, &disc->progress); + /* user-user */ + if (disc->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &disc->useruser); + /* ss version */ + if (disc->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &disc->ssversion); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* release message from upper layer or from timer event */ +static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending RELEASE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RELEASE; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x308, GSM48_T308_MS); + + /* cause */ + if (rel->fields & MNCC_F_CAUSE) + gsm48_encode_cause(nmsg, 0, &rel->cause); + /* facility */ + if (rel->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &rel->facility); + /* user-user */ + if (rel->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &rel->useruser); + /* ss version */ + if (rel->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &rel->ssversion); + + trans->cc.T308_second = 0; + memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc)); + + if (trans->cc.state != GSM_CSTATE_RELEASE_REQ) + new_cc_state(trans, GSM_CSTATE_RELEASE_REQ); + + gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); + +#if 0 + /* release without sending MMCC_REL_REQ */ + new_cc_state(trans, GSM_CSTATE_NULL); + trans->callref = 0; + trans_free(trans); +#endif + + return 0; +} + +/* reject message from upper layer */ +static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending RELEASE COMPLETE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; + + gsm48_stop_cc_timer(trans); + + /* cause */ + if (rel->fields & MNCC_F_CAUSE) + gsm48_encode_cause(nmsg, 0, &rel->cause); + /* facility */ + if (rel->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &rel->facility); + /* user-user */ + if (rel->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &rel->useruser); + /* ss version */ + if (rel->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &rel->ssversion); + + /* release without sending MMCC_REL_REQ */ + new_cc_state(trans, GSM_CSTATE_NULL); + trans->callref = 0; + trans_free(trans); + + return 0; +} + +/* disconnect is received from lower layer */ +static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc disc; + + LOGP(DCC, LOGL_INFO, "received DISCONNECT\n"); + + gsm48_stop_cc_timer(trans); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); + + memset(&disc, 0, sizeof(struct gsm_mncc)); + disc.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_CAUSE, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + disc.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&disc.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + disc.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&disc.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + disc.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&disc.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + disc.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&disc.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + /* store disconnect cause for T305 expiry */ + memcpy(&trans->cc.msg, &disc, sizeof(struct gsm_mncc)); + + return mncc_recvmsg(trans->ms, trans, MNCC_DISC_IND, &disc); +} + +/* release is received from lower layer */ +static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc rel; + + LOGP(DCC, LOGL_INFO, "received RELEASE\n"); + + gsm48_stop_cc_timer(trans); + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + rel.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&rel.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + rel.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&rel.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + rel.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&rel.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + /* in case we receive a relase, when we are already in NULL state */ + if (trans->cc.state == GSM_CSTATE_NULL) { + LOGP(DCC, LOGL_INFO, "ignoring RELEASE in NULL state\n"); + /* release MM conn, free trans */ + return gsm48_rel_null_free(trans); + } + if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) { + /* release collision 5.4.5 */ + mncc_recvmsg(trans->ms, trans, MNCC_REL_CNF, &rel); + } else { + struct msgb *nmsg; + + /* forward cause only */ + LOGP(DCC, LOGL_INFO, "sending RELEASE COMPLETE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; + + if (rel.fields & MNCC_F_CAUSE) + gsm48_encode_cause(nmsg, 0, &rel.cause); + + gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); + + /* release indication */ + mncc_recvmsg(trans->ms, trans, MNCC_REL_IND, &rel); + } + + /* release MM conn, got NULL state, free trans */ + return gsm48_rel_null_free(trans); +} + +/* release complete is received from lower layer */ +static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc rel; + + LOGP(DCC, LOGL_INFO, "received RELEASE COMPLETE\n"); + + gsm48_stop_cc_timer(trans); + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + rel.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&rel.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + rel.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&rel.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + rel.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&rel.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + if (trans->callref) { + switch (trans->cc.state) { + case GSM_CSTATE_CALL_PRESENT: + mncc_recvmsg(trans->ms, trans, + MNCC_REJ_IND, &rel); + break; + case GSM_CSTATE_RELEASE_REQ: + mncc_recvmsg(trans->ms, trans, + MNCC_REL_CNF, &rel); + break; + default: + mncc_recvmsg(trans->ms, trans, + MNCC_REL_IND, &rel); + } + } + + /* release MM conn, got NULL state, free trans */ + return gsm48_rel_null_free(trans); +} + +/* + * state machines + */ + +/* state trasitions for MNCC messages (upper layer) */ +static struct downstate { + u_int32_t states; + int type; + int (*rout) (struct gsm_trans *trans, void *arg); +} downstatelist[] = { + /* mobile originating call establishment */ + {SBIT(GSM_CSTATE_NULL), /* 5.2.1 */ + MNCC_SETUP_REQ, gsm48_cc_init_mm}, + + {SBIT(GSM_CSTATE_MM_CONNECTION_PEND), /* 5.2.1 */ + MNCC_REL_REQ, gsm48_cc_abort_mm}, + + /* mobile terminating call establishment */ + {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.1 */ + MNCC_CALL_CONF_REQ, gsm48_cc_tx_call_conf}, + + {SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* 5.2.2.3.2 */ + MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, + + {SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | + SBIT(GSM_CSTATE_CALL_RECEIVED), /* 5.2.2.5 */ + MNCC_SETUP_RSP, gsm48_cc_tx_connect}, + + /* signalling during call */ + {SBIT(GSM_CSTATE_ACTIVE), /* 5.3.1 */ + MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, + + {ALL_STATES, /* 5.5.7.1 */ + MNCC_START_DTMF_REQ, gsm48_cc_tx_start_dtmf}, + + {ALL_STATES, /* 5.5.7.3 */ + MNCC_STOP_DTMF_REQ, gsm48_cc_tx_stop_dtmf}, + + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_HOLD_REQ, gsm48_cc_tx_hold}, + + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_RETRIEVE_REQ, gsm48_cc_tx_retrieve}, + + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), + MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, + + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, + + /* clearing */ + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - + SBIT(GSM_CSTATE_RELEASE_REQ) - + SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.3.1 */ + MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, + + {SBIT(GSM_CSTATE_INITIATED), + MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, + + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - + SBIT(GSM_CSTATE_RELEASE_REQ), /* ??? */ + MNCC_REL_REQ, gsm48_cc_tx_release}, + + /* modify */ + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, + + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, + + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, +}; + +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct downstate)) + +int mncc_send(struct osmocom_ms *ms, int msg_type, void *arg) +{ + struct gsm_mncc *data = arg; + struct gsm_trans *trans; + int i, rc; + + /* Find callref */ + trans = trans_find_by_callref(ms, data->callref); + + if (!trans) { + /* check for SETUP message */ + if (msg_type != MNCC_SETUP_REQ) { + /* Invalid call reference */ + LOGP(DCC, LOGL_NOTICE, "transaction not found\n"); + return mncc_release_ind(ms, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_TRANS_ID); + } + if (data->callref >= 0x40000000) { + LOGP(DCC, LOGL_FATAL, "MNCC ref wrong.\n"); + return mncc_release_ind(ms, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_TRANS_ID); + } + + /* Create transaction */ + trans = trans_alloc(ms, GSM48_PDISC_CC, 0xff, data->callref); + if (!trans) { + /* No memory or whatever */ + return mncc_release_ind(ms, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + } + } + + switch (msg_type) { + case GSM_TCHF_FRAME: + printf("TCH/F frame ignored!\n"); + return -EINVAL; + } + + /* Find function for current state and message */ + for (i = 0; i < DOWNSLLEN; i++) + if ((msg_type == downstatelist[i].type) + && ((1 << trans->cc.state) & downstatelist[i].states)) + break; + if (i == DOWNSLLEN) { + LOGP(DCC, LOGL_NOTICE, "Message unhandled at this " + "state.\n"); + return 0; + } + + rc = downstatelist[i].rout(trans, arg); + + return rc; +} + +/* state trasitions for call control messages (lower layer) */ +static struct datastate { + u_int32_t states; + int type; + int (*rout) (struct gsm_trans *trans, struct msgb *msg); +} datastatelist[] = { + /* mobile originating call establishment */ + {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.3 */ + GSM48_MT_CC_CALL_PROC, gsm48_cc_rx_call_proceeding}, + + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | + SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.4.1 */ + GSM48_MT_CC_PROGRESS, gsm48_cc_rx_progress}, + + {SBIT(GSM_CSTATE_INITIATED) | + SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.5 */ + GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting}, + + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | + SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.6 */ + GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect}, + + /* mobile terminating call establishment */ + {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ + GSM48_MT_CC_SETUP, gsm48_cc_rx_setup}, + + {SBIT(GSM_CSTATE_CONNECT_REQUEST), /* 5.2.2.6 */ + GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack}, + + /* signalling during call */ + {SBIT(GSM_CSTATE_ACTIVE), /* 5.3.1 */ + GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify}, + + {ALL_STATES, /* 8.4 */ + GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq}, + + {ALL_STATES, + GSM48_MT_CC_STATUS, gsm48_cc_rx_status}, + + {ALL_STATES, /* 5.5.7.2 */ + GSM48_MT_CC_START_DTMF_ACK, gsm48_cc_rx_start_dtmf_ack}, + + {ALL_STATES, /* 5.5.7.2 */ + GSM48_MT_CC_START_DTMF_REJ, gsm48_cc_rx_start_dtmf_rej}, + + {ALL_STATES, /* 5.5.7.4 */ + GSM48_MT_CC_STOP_DTMF_ACK, gsm48_cc_rx_stop_dtmf_ack}, + + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_HOLD_ACK, gsm48_cc_rx_hold_ack}, + + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_HOLD_REJ, gsm48_cc_rx_hold_rej}, + + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_RETR_ACK, gsm48_cc_rx_retrieve_ack}, + + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_RETR_REJ, gsm48_cc_rx_retrieve_rej}, + + {ALL_STATES - SBIT(GSM_CSTATE_NULL), + GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility}, + + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo}, + + /* clearing */ + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ) - + SBIT(GSM_CSTATE_DISCONNECT_IND), /* 5.4.4.1.1 */ + GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect}, + + {ALL_STATES, /* 5.4.3.3 & 5.4.5!!!*/ + GSM48_MT_CC_RELEASE, gsm48_cc_rx_release}, + + {ALL_STATES, /* 5.4.4.1.3 */ + GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl}, + + /* modify */ + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify}, + + {SBIT(GSM_CSTATE_MO_TERM_MODIFY), + GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete}, + + {SBIT(GSM_CSTATE_MO_TERM_MODIFY), + GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject}, +}; + +#define DATASLLEN \ + (sizeof(datastatelist) / sizeof(struct datastate)) + +static int gsm48_cc_data_ind(struct gsm_trans *trans, struct msgb *msg) +{ + struct osmocom_ms *ms = trans->ms; + struct gsm48_hdr *gh = msgb_l3(msg); + int msg_type = gh->msg_type & 0xbf; + uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; + /* flip */ + int msg_supported = 0; /* determine, if message is supported at all */ + int i, rc; + + /* set transaction ID, if not already */ + trans->transaction_id = transaction_id; + + /* pull the MMCC header */ + msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr)); + + LOGP(DCC, LOGL_INFO, "(ms %s) Received '%s' in CC state %s\n", ms->name, + gsm48_cc_msg_name(msg_type), + gsm48_cc_state_name(trans->cc.state)); + + /* find function for current state and message */ + for (i = 0; i < DATASLLEN; i++) { + if (msg_type == datastatelist[i].type) + msg_supported = 1; + if ((msg_type == datastatelist[i].type) + && ((1 << trans->cc.state) & datastatelist[i].states)) + break; + } + if (i == DATASLLEN) { + if (msg_supported) { + LOGP(DCC, LOGL_NOTICE, "Message unhandled at this " + "state.\n"); + return gsm48_cc_tx_status(trans, + GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE); + } else { + LOGP(DCC, LOGL_NOTICE, "Message not supported.\n"); + return gsm48_cc_tx_status(trans, + GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED); + } + } + + rc = datastatelist[i].rout(trans, msg); + + return rc; +} + +/* receive message from MM layer */ +int gsm48_rcv_cc(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + int msg_type = mmh->msg_type; + struct gsm_trans *trans; + int rc = 0; + + trans = trans_find_by_callref(ms, mmh->ref); + if (!trans) { + trans = trans_alloc(ms, GSM48_PDISC_CC, mmh->transaction_id, + mmh->ref); + if (!trans) + return -ENOMEM; + } + + LOGP(DCC, LOGL_INFO, "(ms %s) Received '%s' in CC state %s\n", ms->name, + get_mmxx_name(msg_type), + gsm48_cc_state_name(trans->cc.state)); + + switch (msg_type) { + case GSM48_MMCC_EST_IND: + /* data included */ + rc = gsm48_cc_data_ind(trans, msg); + break; + case GSM48_MMCC_EST_CNF: + /* send setup after confirm */ + if (trans->cc.state == GSM_CSTATE_MM_CONNECTION_PEND) + rc = gsm48_cc_tx_setup(trans); + else + LOGP(DCC, LOGL_ERROR, "Oops, MMCC-EST-CONF in state " + "%d?\n", trans->cc.state); + break; + case GSM48_MMCC_ERR_IND: /* no supporting re-establishment */ + case GSM48_MMCC_REL_IND: + /* release L4, release transaction */ + mncc_release_ind(trans->ms, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, mmh->cause); + /* release without sending MMCC_REL_REQ */ + new_cc_state(trans, GSM_CSTATE_NULL); + trans->callref = 0; + trans_free(trans); + break; + case GSM48_MMCC_DATA_IND: + rc = gsm48_cc_data_ind(trans, msg); + break; + case GSM48_MMCC_UNIT_DATA_IND: + break; + case GSM48_MMCC_SYNC_IND: + break; + default: + LOGP(DCC, LOGL_NOTICE, "Message unhandled.\n"); + rc = -ENOTSUP; + } + + return rc; +} + diff --git a/src/host/layer23/src/mobile/gsm48_mm.c b/src/host/layer23/src/mobile/gsm48_mm.c new file mode 100644 index 00000000..15bbd27c --- /dev/null +++ b/src/host/layer23/src/mobile/gsm48_mm.c @@ -0,0 +1,4162 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <arpa/inet.h> + +#include <osmocore/msgb.h> +#include <osmocore/utils.h> +#include <osmocore/gsm48.h> +#include <osmocore/talloc.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/networks.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/mobile/gsm48_cc.h> +#include <osmocom/bb/mobile/app_mobile.h> + +extern void *l23_ctx; + +void mm_conn_free(struct gsm48_mm_conn *conn); +static int gsm48_rcv_rr(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_rcv_mmr(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg); +static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type); +static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms); +static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_conn_go_dedic(struct osmocom_ms *ms); +static int gsm48_mm_init_mm_reject(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg); +static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate); +static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_loc_upd_periodic(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); + +/* + * notes + */ + +/* + * Notes on IMSI detach procedure: + * + * At the end of the procedure, the state of MM, RR, cell selection: No SIM. + * + * In MM IDLE state, cell available: RR is establised, IMSI detach specific + * procedure is performed. + * + * In MM IDLE state, no cell: State is silently changed to No SIM. + * + * During any MM connection state, or Wait for network command: All MM + * connections (if any) are released locally, and IMSI detach specific + * procedure is performed. + * + * During IMSI detach processing: Request of IMSI detach is ignored. + * + * Any other state: The special 'delay_detach' flag is set only. If set, at any + * state transition we will clear the flag and restart the procedure again. + * + * The procedure is not spec conform, but always succeeds. + * + */ + +/* Notes on Service states: + * + * There are two PLMN search states: + * + * - PLMN SEARCH NORMAL + * - PLMN SEARCH + * + * They are entered, if: (4.2.1.2) + * - ME is switched on + * - SIM is inserted + * - user has asked PLMN selection in certain Service states + * - coverage is lost in certain Service states + * - roaming is denied + * - (optionally see 4.2.1.2) + * + * PLMN SEARCH NORMAL state is then entered, if all these conditions are met: + * - SIM is valid + * - SIM state is U1 + * - SIM LAI valid + * - cell selected + * - cell == SIM LAI + * + * Otherwhise PLMN SEARCH is entered. + * + * During PLMN SEARCH NORMAL state: (4.2.2.5) + * - on expirery of T3211 or T3213: Perform location update, when back + * to NORMAL SERVICE state. + * - on expirery of T3212: Perform periodic location update, when back + * to NORMAL SERVICE state. + * - perform IMSI detach + * - perform MM connections + * - respond to paging (if possible) + * + * During PLMN SEARCH state: (4.2.2.6) + * - reject MM connection except for emergency calls + * + * + * The NO CELL AVAILABLE state is entered, if: + * - no cell found during PLMN search + * + * During NO CELL AVAILABLE state: + * - reject any MM connection + * + * + * The NO IMSI state is entered if: + * - SIM is invalid + * - and cell is selected during PLMN SEARCH states + * + * During NO IMSO state: (4.2.2.4) + * - reject MM connection except for emergency calls + * + * + * The LIMITED SERVICE state is entered if: + * - SIM is valid + * - and SIM state is U3 + * - and cell is selected + * + * During LIMITED SERVICE state: (4.2.2.3) + * - reject MM connection except for emergency calls + * - perform location update, if new LAI is entered + * + * + * The LOCATION UPDATE NEEDED state is entered if: + * - SIM is valid + * - and location update must be performed for any reason + * + * During LOCATION UPDATE NEEDED state: + * - reject MM connection except for emergency calls + * + * This state is left if location update is possible and directly enter + * state ATTEMPTING TO UPDATE and trigger location update. + * The function gsm48_mm_loc_upd_possible() is used to check this on state + * change. + * + * + * The ATTEMPTING TO UPDATE state is entered if: + * - SIM is valid + * - and SIM state is U2 + * - and cell is selected + * + * During ATTEMPTING TO UPDATE state: (4.2.2.2) + * - on expirery of T3211 or T3213: Perform location updated + * - on expirery of T3212: Perform location updated + * - on change of LAI: Perform location update + * - (abnormal cases unsupported) + * - accept MM connection for emergency calls + * - trigger location update on any other MM connection + * - respond to paging (with IMSI only, because in U2 TMSI is not valid) + * + * + * The NORMAL SERVICE state is entered if: + * - SIM is valid + * - and SIM state is U1 + * - and cell is selected + * - and SIM LAI == cell + * + * During NORMAL SERVICE state: (4.2.2.1) + * - on expirery of T3211 or T3213: Perform location updated + * - on expirery of T3212: Perform location updated + * - on change of LAI: Perform location update + * - perform IMSI detach + * - perform MM connections + * - respond to paging + * + * + * gsm48_mm_set_plmn_search() is used enter PLMN SEARCH or PLMN SEARCH NORMAL + * state. Depending on the conditions above, the appropiate state is selected. + * + * + * gsm48_mm_return_idle() is used to select the Service state when returning + * to MM IDLE state after cell reselection. + * + * + * If cell selection process indicates NO_CELL_FOUND: + * + * - NO CELL AVAILABLE state is entered, if not already. + * + * gsm48_mm_no_cell_found() is used to select the Service state. + * + * + * If cell selection process indicates CELL_SELECTED: + * + * - NO IMSI state is entered, if no SIM valid. + * - Otherwise NORMAL SERVICES state is entered, if + * SIM state is U1, SIM LAI == cell, IMSI is attached, T3212 not expired. + * - Otherwise NORMAL SERVICES state is entered, if + * SIM state is U1, SIM LAI == cell, attach not required, T3212 not expired. + * - Otherwise LIMITED SERVICE state is entered, if + * CS mode is automatic, cell is forbidden PLMN or forbidden LA. + * - Otherwise LIMITED SERVICE state is entered, if + * CS mode is manual, cell is not the selected one. + * - Otherwise LOCATION UPDATE NEEDED state is entered. + * + * gsm48_mm_cell_selected() is used to select the Service state. + * + */ + +/* + * support functions + */ + +/* decode network name */ +static int decode_network_name(char *name, int name_len, + const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + int length, padding; + + name[0] = '\0'; + if (in_len < 1) + return -EINVAL; + + /* must be CB encoded */ + if ((lv[1] & 0x70) != 0x00) + return -ENOTSUP; + + padding = lv[1] & 0x03; + length = ((in_len - 1) * 8 - padding) / 7; + if (length <= 0) + return 0; + if (length >= name_len) + length = name_len - 1; + gsm_7bit_decode(name, lv + 2, length); + name[length] = '\0'; + + return length; +} + +/* encode 'mobile identity' */ +int gsm48_encode_mi(uint8_t *buf, struct msgb *msg, struct osmocom_ms *ms, + uint8_t mi_type) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_settings *set = &ms->settings; + uint8_t *ie; + + switch(mi_type) { + case GSM_MI_TYPE_TMSI: + gsm48_generate_mid_from_tmsi(buf, subscr->tmsi); + break; + case GSM_MI_TYPE_IMSI: + gsm48_generate_mid_from_imsi(buf, subscr->imsi); + break; + case GSM_MI_TYPE_IMEI: + gsm48_generate_mid_from_imsi(buf, set->imei); + break; + case GSM_MI_TYPE_IMEISV: + gsm48_generate_mid_from_imsi(buf, set->imeisv); + break; + case GSM_MI_TYPE_NONE: + default: + buf[0] = GSM48_IE_MOBILE_ID; + buf[1] = 1; + buf[2] = 0xf0; + break; + } + /* alter MI type */ + buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | mi_type; + + if (msg) { + /* MI as LV */ + ie = msgb_put(msg, 1 + buf[1]); + memcpy(ie, buf + 1, 1 + buf[1]); + } + + return 0; +} + +/* encode 'classmark 1' */ +int gsm48_encode_classmark1(struct gsm48_classmark1 *cm, uint8_t rev_lev, + uint8_t es_ind, uint8_t a5_1, uint8_t pwr_lev) +{ + memset(cm, 0, sizeof(*cm)); + cm->rev_lev = rev_lev; + cm->es_ind = es_ind; + cm->a5_1 = !a5_1; + cm->pwr_lev = pwr_lev; + + return 0; +} + +/* + * timers + */ + +static void timeout_mm_t3210(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DMM, LOGL_INFO, "timer T3210 (loc. upd. timeout) has fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3210, NULL); +} + +static void timeout_mm_t3211(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DSUM, LOGL_INFO, "Location update retry\n"); + LOGP(DMM, LOGL_INFO, "timer T3211 (loc. upd. retry delay) has fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3211, NULL); +} + +static void timeout_mm_t3212(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DSUM, LOGL_INFO, "Periodic location update\n"); + LOGP(DMM, LOGL_INFO, "timer T3212 (periodic loc. upd. delay) has " + "fired\n"); + + /* reset attempt counter when attempting to update (4.4.4.5) */ + if (mm->state == GSM48_MM_ST_MM_IDLE + && mm->substate == GSM48_MM_SST_ATTEMPT_UPDATE) + mm->lupd_attempt = 0; + + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3212, NULL); +} + +static void timeout_mm_t3213(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DSUM, LOGL_INFO, "Location update retry\n"); + LOGP(DMM, LOGL_INFO, "timer T3213 (delay after RA failure) has " + "fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3213, NULL); +} + +static void timeout_mm_t3230(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DMM, LOGL_INFO, "timer T3230 (MM connection timeout) has " + "fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3230, NULL); +} + +static void timeout_mm_t3220(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DMM, LOGL_INFO, "timer T3220 (IMSI detach keepalive) has " + "fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3220, NULL); +} + +static void timeout_mm_t3240(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DMM, LOGL_INFO, "timer T3240 (RR release timeout) has fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3240, NULL); +} + +static void start_mm_t3210(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3210 (loc. upd. timeout) with %d.%d " + "seconds\n", GSM_T3210_MS); + mm->t3210.cb = timeout_mm_t3210; + mm->t3210.data = mm; + bsc_schedule_timer(&mm->t3210, GSM_T3210_MS); +} + +static void start_mm_t3211(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3211 (loc. upd. retry delay) with " + "%d.%d seconds\n", GSM_T3211_MS); + mm->t3211.cb = timeout_mm_t3211; + mm->t3211.data = mm; + bsc_schedule_timer(&mm->t3211, GSM_T3211_MS); +} + +static void start_mm_t3212(struct gsm48_mmlayer *mm, int sec) +{ + /* don't start, if is not available */ + if (!sec) + return; + + LOGP(DMM, LOGL_INFO, "starting T3212 (periodic loc. upd. delay) with " + "%d seconds\n", sec); + mm->t3212.cb = timeout_mm_t3212; + mm->t3212.data = mm; + bsc_schedule_timer(&mm->t3212, sec, 0); +} + +static void start_mm_t3213(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3213 (delay after RA failure) with " + "%d.%d seconds\n", GSM_T3213_MS); + mm->t3213.cb = timeout_mm_t3213; + mm->t3213.data = mm; + bsc_schedule_timer(&mm->t3213, GSM_T3213_MS); +} + +static void start_mm_t3220(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3220 (IMSI detach keepalive) with " + "%d.%d seconds\n", GSM_T3220_MS); + mm->t3220.cb = timeout_mm_t3220; + mm->t3220.data = mm; + bsc_schedule_timer(&mm->t3220, GSM_T3220_MS); +} + +static void start_mm_t3230(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3230 (MM connection timeout) with " + "%d.%d seconds\n", GSM_T3230_MS); + mm->t3230.cb = timeout_mm_t3230; + mm->t3230.data = mm; + bsc_schedule_timer(&mm->t3230, GSM_T3230_MS); +} + +static void start_mm_t3240(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3240 (RR release timeout) with %d.%d " + "seconds\n", GSM_T3240_MS); + mm->t3240.cb = timeout_mm_t3240; + mm->t3240.data = mm; + bsc_schedule_timer(&mm->t3240, GSM_T3240_MS); +} + +static void stop_mm_t3210(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3210)) { + LOGP(DMM, LOGL_INFO, "stopping pending (loc. upd. timeout) " + "timer T3210\n"); + bsc_del_timer(&mm->t3210); + } +} + +static void stop_mm_t3211(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3211)) { + LOGP(DMM, LOGL_INFO, "stopping pending (loc. upd. retry " + "delay) timer T3211\n"); + bsc_del_timer(&mm->t3211); + } +} + +static void stop_mm_t3212(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3212)) { + LOGP(DMM, LOGL_INFO, "stopping pending (periodic loc. upd. " + "delay) timer T3212\n"); + bsc_del_timer(&mm->t3212); + } +} + +static void stop_mm_t3213(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3213)) { + LOGP(DMM, LOGL_INFO, "stopping pending (delay after RA " + "failure) timer T3213\n"); + bsc_del_timer(&mm->t3213); + } +} + +static void stop_mm_t3220(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3220)) { + LOGP(DMM, LOGL_INFO, "stopping pending (IMSI detach keepalive) " + "timer T3220\n"); + bsc_del_timer(&mm->t3220); + } +} + +static void stop_mm_t3230(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3230)) { + LOGP(DMM, LOGL_INFO, "stopping pending (MM connection timeout) " + "timer T3230\n"); + bsc_del_timer(&mm->t3230); + } +} + +static void stop_mm_t3240(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3240)) { + LOGP(DMM, LOGL_INFO, "stopping pending (RR release timeout) " + "timer T3240\n"); + bsc_del_timer(&mm->t3240); + } +} + +static void stop_mm_t3241(struct gsm48_mmlayer *mm) +{ + /* not implemented, not required */ +} + +/* + * messages + */ + +/* names of MM events */ +static const struct value_string gsm48_mmevent_names[] = { + { GSM48_MM_EVENT_CELL_SELECTED, "MM_EVENT_CELL_SELECTED" }, + { GSM48_MM_EVENT_NO_CELL_FOUND, "MM_EVENT_NO_CELL_FOUND" }, + { GSM48_MM_EVENT_TIMEOUT_T3210, "MM_EVENT_TIMEOUT_T3210" }, + { GSM48_MM_EVENT_TIMEOUT_T3211, "MM_EVENT_TIMEOUT_T3211" }, + { GSM48_MM_EVENT_TIMEOUT_T3212, "MM_EVENT_TIMEOUT_T3212" }, + { GSM48_MM_EVENT_TIMEOUT_T3213, "MM_EVENT_TIMEOUT_T3213" }, + { GSM48_MM_EVENT_TIMEOUT_T3220, "MM_EVENT_TIMEOUT_T3220" }, + { GSM48_MM_EVENT_TIMEOUT_T3230, "MM_EVENT_TIMEOUT_T3230" }, + { GSM48_MM_EVENT_TIMEOUT_T3240, "MM_EVENT_TIMEOUT_T3240" }, + { GSM48_MM_EVENT_IMSI_DETACH, "MM_EVENT_IMSI_DETACH" }, + { GSM48_MM_EVENT_PAGING, "MM_EVENT_PAGING" }, + { GSM48_MM_EVENT_AUTH_RESPONSE, "MM_EVENT_AUTH_RESPONSE" }, + { GSM48_MM_EVENT_SYSINFO, "MM_EVENT_SYSINFO" }, + { GSM48_MM_EVENT_USER_PLMN_SEL, "MM_EVENT_USER_PLMN_SEL" }, + { 0, NULL } +}; + +const char *get_mmevent_name(int value) +{ + return get_value_string(gsm48_mmevent_names, value); +} + +/* names of MM-SAP */ +static const struct value_string gsm48_mm_msg_names[] = { + { GSM48_MT_MM_IMSI_DETACH_IND, "MT_MM_IMSI_DETACH_IND" }, + { GSM48_MT_MM_LOC_UPD_ACCEPT, "MT_MM_LOC_UPD_ACCEPT" }, + { GSM48_MT_MM_LOC_UPD_REJECT, "MT_MM_LOC_UPD_REJECT" }, + { GSM48_MT_MM_LOC_UPD_REQUEST, "MT_MM_LOC_UPD_REQUEST" }, + { GSM48_MT_MM_AUTH_REJ, "MT_MM_AUTH_REJ" }, + { GSM48_MT_MM_AUTH_REQ, "MT_MM_AUTH_REQ" }, + { GSM48_MT_MM_AUTH_RESP, "MT_MM_AUTH_RESP" }, + { GSM48_MT_MM_ID_REQ, "MT_MM_ID_REQ" }, + { GSM48_MT_MM_ID_RESP, "MT_MM_ID_RESP" }, + { GSM48_MT_MM_TMSI_REALL_CMD, "MT_MM_TMSI_REALL_CMD" }, + { GSM48_MT_MM_TMSI_REALL_COMPL, "MT_MM_TMSI_REALL_COMPL" }, + { GSM48_MT_MM_CM_SERV_ACC, "MT_MM_CM_SERV_ACC" }, + { GSM48_MT_MM_CM_SERV_REJ, "MT_MM_CM_SERV_REJ" }, + { GSM48_MT_MM_CM_SERV_ABORT, "MT_MM_CM_SERV_ABORT" }, + { GSM48_MT_MM_CM_SERV_REQ, "MT_MM_CM_SERV_REQ" }, + { GSM48_MT_MM_CM_SERV_PROMPT, "MT_MM_CM_SERV_PROMPT" }, + { GSM48_MT_MM_CM_REEST_REQ, "MT_MM_CM_REEST_REQ" }, + { GSM48_MT_MM_ABORT, "MT_MM_ABORT" }, + { GSM48_MT_MM_NULL, "MT_MM_NULL" }, + { GSM48_MT_MM_STATUS, "MT_MM_STATUS" }, + { GSM48_MT_MM_INFO, "MT_MM_INFO" }, + { 0, NULL } +}; + +const char *get_mm_name(int value) +{ + return get_value_string(gsm48_mm_msg_names, value); +} + +/* names of MMxx-SAP */ +static const struct value_string gsm48_mmxx_msg_names[] = { + { GSM48_MMCC_EST_REQ, "MMCC_EST_REQ" }, + { GSM48_MMCC_EST_IND, "MMCC_EST_IND" }, + { GSM48_MMCC_EST_CNF, "MMCC_EST_CNF" }, + { GSM48_MMCC_REL_REQ, "MMCC_REL_REQ" }, + { GSM48_MMCC_REL_IND, "MMCC_REL_IND" }, + { GSM48_MMCC_DATA_REQ, "MMCC_DATA_REQ" }, + { GSM48_MMCC_DATA_IND, "MMCC_DATA_IND" }, + { GSM48_MMCC_UNIT_DATA_REQ, "MMCC_UNIT_DATA_REQ" }, + { GSM48_MMCC_UNIT_DATA_IND, "MMCC_UNIT_DATA_IND" }, + { GSM48_MMCC_SYNC_IND, "MMCC_SYNC_IND" }, + { GSM48_MMCC_REEST_REQ, "MMCC_REEST_REQ" }, + { GSM48_MMCC_REEST_CNF, "MMCC_REEST_CNF" }, + { GSM48_MMCC_ERR_IND, "MMCC_ERR_IND" }, + { GSM48_MMCC_PROMPT_IND, "MMCC_PROMPT_IND" }, + { GSM48_MMCC_PROMPT_REJ, "MMCC_PROMPT_REJ" }, + { GSM48_MMSS_EST_REQ, "MMSS_EST_REQ" }, + { GSM48_MMSS_EST_IND, "MMSS_EST_IND" }, + { GSM48_MMSS_EST_CNF, "MMSS_EST_CNF" }, + { GSM48_MMSS_REL_REQ, "MMSS_REL_REQ" }, + { GSM48_MMSS_REL_IND, "MMSS_REL_IND" }, + { GSM48_MMSS_DATA_REQ, "MMSS_DATA_REQ" }, + { GSM48_MMSS_DATA_IND, "MMSS_DATA_IND" }, + { GSM48_MMSS_UNIT_DATA_REQ, "MMSS_UNIT_DATA_REQ" }, + { GSM48_MMSS_UNIT_DATA_IND, "MMSS_UNIT_DATA_IND" }, + { GSM48_MMSS_REEST_REQ, "MMSS_REEST_REQ" }, + { GSM48_MMSS_REEST_CNF, "MMSS_REEST_CNF" }, + { GSM48_MMSS_ERR_IND, "MMSS_ERR_IND" }, + { GSM48_MMSS_PROMPT_IND, "MMSS_PROMPT_IND" }, + { GSM48_MMSS_PROMPT_REJ, "MMSS_PROMPT_REJ" }, + { GSM48_MMSMS_EST_REQ, "MMSMS_EST_REQ" }, + { GSM48_MMSMS_EST_IND, "MMSMS_EST_IND" }, + { GSM48_MMSMS_EST_CNF, "MMSMS_EST_CNF" }, + { GSM48_MMSMS_REL_REQ, "MMSMS_REL_REQ" }, + { GSM48_MMSMS_REL_IND, "MMSMS_REL_IND" }, + { GSM48_MMSMS_DATA_REQ, "MMSMS_DATA_REQ" }, + { GSM48_MMSMS_DATA_IND, "MMSMS_DATA_IND" }, + { GSM48_MMSMS_UNIT_DATA_REQ, "MMSMS_UNIT_DATA_REQ" }, + { GSM48_MMSMS_UNIT_DATA_IND, "MMSMS_UNIT_DATA_IND" }, + { GSM48_MMSMS_REEST_REQ, "MMSMS_REEST_REQ" }, + { GSM48_MMSMS_REEST_CNF, "MMSMS_REEST_CNF" }, + { GSM48_MMSMS_ERR_IND, "MMSMS_ERR_IND" }, + { GSM48_MMSMS_PROMPT_IND, "MMSMS_PROMPT_IND" }, + { GSM48_MMSMS_PROMPT_REJ, "MMSMS_PROMPT_REJ" }, + { 0, NULL } +}; + +const char *get_mmxx_name(int value) +{ + return get_value_string(gsm48_mmxx_msg_names, value); +} + +/* names of MMR-SAP */ +static const struct value_string gsm48_mmr_msg_names[] = { + { GSM48_MMR_REG_REQ, "MMR_REG_REQ" }, + { GSM48_MMR_REG_CNF, "MMR_REG_CNF" }, + { GSM48_MMR_NREG_REQ, "MMR_NREG_REQ" }, + { GSM48_MMR_NREG_IND, "MMR_NREG_IND" }, + { 0, NULL } +}; + +const char *get_mmr_name(int value) +{ + return get_value_string(gsm48_mmr_msg_names, value); +} + +/* allocate GSM 04.08 message (MMxx-SAP) */ +struct msgb *gsm48_mmxx_msgb_alloc(int msg_type, uint32_t ref, + uint8_t transaction_id) +{ + struct msgb *msg; + struct gsm48_mmxx_hdr *mmh; + + msg = msgb_alloc_headroom(MMXX_ALLOC_SIZE+MMXX_ALLOC_HEADROOM, + MMXX_ALLOC_HEADROOM, "GSM 04.08 MMxx"); + if (!msg) + return NULL; + + mmh = (struct gsm48_mmxx_hdr *)msgb_put(msg, sizeof(*mmh)); + mmh->msg_type = msg_type; + mmh->ref = ref; + mmh->transaction_id = transaction_id; + + return msg; +} + +/* allocate MM event message */ +struct msgb *gsm48_mmevent_msgb_alloc(int msg_type) +{ + struct msgb *msg; + struct gsm48_mm_event *mme; + + msg = msgb_alloc_headroom(sizeof(*mme), 0, "GSM 04.08 MM event"); + if (!msg) + return NULL; + + mme = (struct gsm48_mm_event *)msgb_put(msg, sizeof(*mme)); + mme->msg_type = msg_type; + + return msg; +} + +/* allocate MMR message */ +struct msgb *gsm48_mmr_msgb_alloc(int msg_type) +{ + struct msgb *msg; + struct gsm48_mmr *mmr; + + msg = msgb_alloc_headroom(sizeof(*mmr), 0, "GSM 04.08 MMR"); + if (!msg) + return NULL; + + mmr = (struct gsm48_mmr *)msgb_put(msg, sizeof(*mmr)); + mmr->msg_type = msg_type; + + return msg; +} + +/* queue message (MMxx-SAP) */ +int gsm48_mmxx_upmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + msgb_enqueue(&mm->mmxx_upqueue, msg); + + return 0; +} + +/* queue message (MMR-SAP) */ +int gsm48_mmr_downmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + msgb_enqueue(&mm->mmr_downqueue, msg); + + return 0; +} + +/* queue MM event message */ +int gsm48_mmevent_msg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + msgb_enqueue(&mm->event_queue, msg); + + return 0; +} + +/* dequeue messages (MMxx-SAP) */ +int gsm48_mmxx_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *msg; + struct gsm48_mmxx_hdr *mmh; + int work = 0; + + while ((msg = msgb_dequeue(&mm->mmxx_upqueue))) { + mmh = (struct gsm48_mmxx_hdr *) msg->data; + switch (mmh->msg_type & GSM48_MMXX_MASK) { + case GSM48_MMCC_CLASS: + gsm48_rcv_cc(ms, msg); + break; +#if 0 + case GSM48_MMSS_CLASS: + gsm48_rcv_ss(ms, msg); + break; + case GSM48_MMSMS_CLASS: + gsm48_rcv_sms(ms, msg); + break; +#endif + } + msgb_free(msg); + work = 1; /* work done */ + } + + return work; +} + +/* dequeue messages (MMR-SAP) */ +int gsm48_mmr_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *msg; + struct gsm48_mmr *mmr; + int work = 0; + + while ((msg = msgb_dequeue(&mm->mmr_downqueue))) { + mmr = (struct gsm48_mmr *) msg->data; + gsm48_rcv_mmr(ms, msg); + msgb_free(msg); + work = 1; /* work done */ + } + + return work; +} + +/* dequeue messages (RR-SAP) */ +int gsm48_rr_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&mm->rr_upqueue))) { + /* msg is freed there */ + gsm48_rcv_rr(ms, msg); + work = 1; /* work done */ + } + + return work; +} + +/* dequeue MM event messages */ +int gsm48_mmevent_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_event *mme; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&mm->event_queue))) { + mme = (struct gsm48_mm_event *) msg->data; + gsm48_mm_ev(ms, mme->msg_type, msg); + msgb_free(msg); + work = 1; /* work done */ + } + + return work; +} + +/* push RR header and send to RR */ +static int gsm48_mm_to_rr(struct osmocom_ms *ms, struct msgb *msg, + int msg_type, uint8_t cause) +{ + struct gsm48_rr_hdr *rrh; + + /* push RR header */ + msgb_push(msg, sizeof(struct gsm48_rr_hdr)); + rrh = (struct gsm48_rr_hdr *) msg->data; + rrh->msg_type = msg_type; + rrh->cause = cause; + + /* send message to RR */ + return gsm48_rr_downmsg(ms, msg); +} + +/* + * state transition + */ + +const char *gsm48_mm_state_names[] = { + "NULL", + "undefined 1", + "undefined 2", + "location updating initiated", + "undefined 4", + "wait for outgoing MM connection", + "MM connection active", + "IMSI detach initiated", + "process CM service prompt", + "wait for network command", + "location updating reject", + "undefined 11", + "undefined 12", + "wait for RR connection (location updating)", + "wait for RR connection (MM connection)", + "wait for RR connection (IMSI detach)", + "undefined 16", + "wait for re-establishment", + "wait for RR connection active", + "MM idle", + "wait for additional outgoing MM connection", + "MM_CONN_ACTIVE_VGCS", + "WAIT_RR_CONN_VGCS", + "location updating pending", + "IMSI detach pending", + "RR connection release not allowed" +}; + +const char *gsm48_mm_substate_names[] = { + "NULL", + "normal service", + "attempting to update", + "limited service", + "no IMSI", + "no cell available", + "location updating needed", + "PLMN search", + "PLMN search (normal)", + "RX_VGCS_NORMAL", + "RX_VGCS_LIMITED" +}; + +/* change state from LOCATION UPDATE NEEDED to ATTEMPTING TO UPDATE */ +static int gsm48_mm_loc_upd_possible(struct gsm48_mmlayer *mm) +{ + // TODO: check if really possible + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_ATTEMPT_UPDATE); + return gsm48_mm_loc_upd_normal(mm->ms, NULL); +} + +/* Set new MM state, also new substate in case of MM IDLE state. */ +static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate) +{ + /* IDLE -> IDLE */ + if (mm->state == GSM48_MM_ST_MM_IDLE && state == mm->state) + LOGP(DMM, LOGL_INFO, "new MM IDLE state %s -> %s\n", + gsm48_mm_substate_names[mm->substate], + gsm48_mm_substate_names[substate]); + /* IDLE -> non-IDLE */ + else if (mm->state == GSM48_MM_ST_MM_IDLE) + LOGP(DMM, LOGL_INFO, "new state MM IDLE, %s -> %s\n", + gsm48_mm_substate_names[mm->substate], + gsm48_mm_state_names[state]); + /* non-IDLE -> IDLE */ + else if (state == GSM48_MM_ST_MM_IDLE) + LOGP(DMM, LOGL_INFO, "new state %s -> MM IDLE, %s\n", + gsm48_mm_state_names[mm->state], + gsm48_mm_substate_names[substate]); + /* non-IDLE -> non-IDLE */ + else + LOGP(DMM, LOGL_INFO, "new state %s -> %s\n", + gsm48_mm_state_names[mm->state], + gsm48_mm_state_names[state]); + + /* remember most recent substate */ + if (mm->state == GSM48_MM_ST_MM_IDLE) + mm->mr_substate = mm->substate; + + mm->state = state; + mm->substate = substate; + + /* resend detach event, if flag is set */ + if (state == GSM48_MM_ST_MM_IDLE && mm->delay_detach) { + struct msgb *nmsg; + + mm->delay_detach = 0; + + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_IMSI_DETACH); + if (!nmsg) + return; + gsm48_mmevent_msg(mm->ms, nmsg); + } + + /* 4.4.2 start T3212 in MM IDLE mode if not started or has expired */ + if (state == GSM48_MM_ST_MM_IDLE + && (substate == GSM48_MM_SST_NORMAL_SERVICE + || substate == GSM48_MM_SST_ATTEMPT_UPDATE)) { + /* start periodic location update timer */ + if (!bsc_timer_pending(&mm->t3212)) + start_mm_t3212(mm, mm->t3212_value); + /* perform pending location update */ + if (mm->lupd_retry) { + LOGP(DMM, LOGL_INFO, "Loc. upd. pending (type %d)\n", + mm->lupd_type); + mm->lupd_retry = 0; + gsm48_mm_loc_upd(mm->ms, NULL); + /* must exit, because this function can be called + * recursively + */ + return; + } + if (mm->lupd_periodic) { + struct gsm48_sysinfo *s = &mm->ms->cellsel.sel_si; + + LOGP(DMM, LOGL_INFO, "Periodic loc. upd. pending " + "(type %d)\n", mm->lupd_type); + mm->lupd_periodic = 0; + if (s->t3212) + gsm48_mm_loc_upd_periodic(mm->ms, NULL); + else + LOGP(DMM, LOGL_INFO, "but not requred\n"); + /* must exit, because this function can be called + * recursively + */ + return; + } + } + + /* check if location update is possible */ + if (state == GSM48_MM_ST_MM_IDLE + && substate == GSM48_MM_SST_LOC_UPD_NEEDED) { + gsm48_mm_loc_upd_possible(mm); + /* must exit, because this function can be called recursively */ + return; + } +} + +/* return PLMN SEARCH or PLMN SEARCH NORMAL state */ +static int gsm48_mm_set_plmn_search(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_cellsel *cs = &ms->cellsel; + + /* SIM not inserted */ + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " + "no SIM.\n"); + return GSM48_MM_SST_PLMN_SEARCH; + } + + /* SIM not updated */ + if (subscr->ustate != GSM_SIM_U1_UPDATED) { + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " + "SIM not updated.\n"); + return GSM48_MM_SST_PLMN_SEARCH; + } + if (subscr->lac == 0x0000 || subscr->lac >= 0xfffe) { + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " + "LAI in SIM not valid.\n"); + return GSM48_MM_SST_PLMN_SEARCH; + } + + /* no cell selected */ + if (!cs->selected) { + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " + "no cell selected.\n"); + return GSM48_MM_SST_PLMN_SEARCH; + } + + /* selected cell's LAI not equal to LAI stored on the sim */ + if (cs->sel_mcc != subscr->mcc + || cs->sel_mnc != subscr->mnc + || cs->sel_lac != subscr->lac) { + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " + "LAI of selected cell (MCC %s MNC %s LAC 0x%04x) " + "!= LAI in SIM (MCC %s MNC %s LAC 0x%04x).\n", + gsm_print_mcc(cs->sel_mcc), gsm_print_mnc(cs->sel_mnc), + cs->sel_lac, gsm_print_mcc(subscr->mcc), + gsm_print_mnc(subscr->mnc), subscr->lac); + return GSM48_MM_SST_PLMN_SEARCH; + } + + /* SIM is updated in this LA */ + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH NORMAL state.\n"); + return GSM48_MM_SST_PLMN_SEARCH_NORMAL; +} + +/* 4.2.3 when returning to MM IDLE state, this function is called */ +static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + + /* 4.4.4.9 start T3211 when RR is released */ + if (mm->start_t3211) { + LOGP(DMM, LOGL_INFO, "Starting T3211 after RR release.\n"); + mm->start_t3211 = 0; + start_mm_t3211(mm); + } + + /* return from location update with "Roaming not allowed" */ + if (mm->state == GSM48_MM_ST_LOC_UPD_REJ && mm->lupd_rej_cause == 13) { + LOGP(DMM, LOGL_INFO, "Roaming not allowed as returning to " + "MM IDLE\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + gsm48_mm_set_plmn_search(ms)); + + return 0; + } + + /* no SIM present or invalid */ + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_INFO, "SIM invalid as returning to MM IDLE\n"); + + /* stop periodic location updating */ + mm->lupd_pending = 0; + stop_mm_t3212(mm); /* 4.4.2 */ + + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NO_IMSI); + + return 0; + } + + /* selected cell equals the registered LAI */ + if (subscr->lac /* valid */ + && cs->sel_mcc == subscr->mcc + && cs->sel_mnc == subscr->mnc + && cs->sel_lac == subscr->lac) { + LOGP(DMM, LOGL_INFO, "We are in registered LAI as returning " + "to MM IDLE\n"); + /* if SIM not updated (abnormal case as described in 4.4.4.9) */ + if (subscr->ustate != GSM_SIM_U1_UPDATED) + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_ATTEMPT_UPDATE); + else + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_NORMAL_SERVICE); + + return 0; + } + + /* location update allowed */ + if (cs->state == GSM322_C3_CAMPED_NORMALLY) { + LOGP(DMM, LOGL_INFO, "We are camping normally as returning to " + "MM IDLE\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_LOC_UPD_NEEDED); + } else { /* location update not allowed */ + LOGP(DMM, LOGL_INFO, "We are camping on any cell as returning " + "to MM IDLE\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_LIMITED_SERVICE); + } + + return 0; +} + +/* 4.2.1.1 Service state PLMN SEARCH (NORMAL) is left if no cell found */ +static int gsm48_mm_no_cell_found(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NO_CELL_AVAIL); + + return 0; +} + +/* 4.2.1.1 Service state PLMN SEARCH (NORMAL) / NO CELL AVAILABLE is left + * if cell selected + */ +static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &cs->sel_si; + struct gsm_settings *set = &ms->settings; + + /* no SIM is inserted */ + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_INFO, "SIM invalid as cell is selected.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NO_IMSI); + + return 0; + } + + /* SIM not updated in this LA */ + if (subscr->ustate == GSM_SIM_U1_UPDATED + && subscr->lac /* valid */ + && cs->sel_mcc == subscr->mcc + && cs->sel_mnc == subscr->mnc + && cs->sel_lac == subscr->lac + && !mm->lupd_periodic) { + if (subscr->imsi_attached) { + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Valid in location area.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_NORMAL_SERVICE); + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + if (!s->att_allowed) { + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Attachment not required.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_NORMAL_SERVICE); + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + /* else, continue */ + } + + /* PLMN mode auto and selected cell is forbidden */ + if (set->plmn_mode == PLMN_MODE_AUTO + && (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc) + || gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc, + cs->sel_lac))) { + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Selected cell is forbidden.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_LIMITED_SERVICE); + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_ROAMING_NA); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* PLMN mode manual and selected cell not selected PLMN */ + if (set->plmn_mode == PLMN_MODE_MANUAL + && (plmn->mcc != cs->sel_mcc + || plmn->mnc != cs->sel_mnc)) { + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Selected cell not found.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_LIMITED_SERVICE); + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* other cases */ + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LOC_UPD_NEEDED); + + return 0; +} + +/* 4.2.1.2 Service state PLMN SEARCH (NORMAL) is entered */ +static int gsm48_mm_plmn_search(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, gsm48_mm_set_plmn_search(ms)); + + return 0; +} + +/* + * init and exit + */ + +/* initialize Mobility Management process */ +int gsm48_mm_init(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + memset(mm, 0, sizeof(*mm)); + mm->ms = ms; + + LOGP(DMM, LOGL_INFO, "init Mobility Management process\n"); + + /* 4.2.1.1 */ + mm->state = GSM48_MM_ST_MM_IDLE; + mm->substate = gsm48_mm_set_plmn_search(ms); + + /* init lists */ + INIT_LLIST_HEAD(&mm->mm_conn); + INIT_LLIST_HEAD(&mm->rr_upqueue); + INIT_LLIST_HEAD(&mm->mmxx_upqueue); + INIT_LLIST_HEAD(&mm->mmr_downqueue); + INIT_LLIST_HEAD(&mm->event_queue); + + return 0; +} + +/* exit MM process */ +int gsm48_mm_exit(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_conn *conn; + struct msgb *msg; + + LOGP(DMM, LOGL_INFO, "exit Mobility Management process\n"); + + /* flush lists */ + while (!llist_empty(&mm->mm_conn)) { + conn = llist_entry(mm->mm_conn.next, + struct gsm48_mm_conn, list); + mm_conn_free(conn); + } + while ((msg = msgb_dequeue(&mm->rr_upqueue))) + msgb_free(msg); + while ((msg = msgb_dequeue(&mm->mmxx_upqueue))) + msgb_free(msg); + while ((msg = msgb_dequeue(&mm->mmr_downqueue))) + msgb_free(msg); + while ((msg = msgb_dequeue(&mm->event_queue))) + msgb_free(msg); + + /* stop timers */ + stop_mm_t3210(mm); + stop_mm_t3211(mm); + stop_mm_t3212(mm); + stop_mm_t3213(mm); + stop_mm_t3220(mm); + stop_mm_t3230(mm); + stop_mm_t3240(mm); + + return 0; +} + +/* + * MM connection management + */ + +static const char *gsm48_mmxx_state_names[] = { + "IDLE", + "CONN_PEND", + "DEDICATED", + "CONN_SUSP", + "REESTPEND" +}; + +uint32_t mm_conn_new_ref = 0x80000001; + +/* new MM connection state */ +static void new_conn_state(struct gsm48_mm_conn *conn, int state) +{ + LOGP(DMM, LOGL_INFO, "(ref %x) new state %s -> %s\n", conn->ref, + gsm48_mmxx_state_names[conn->state], + gsm48_mmxx_state_names[state]); + conn->state = state; +} + +/* find MM connection by protocol+ID */ +struct gsm48_mm_conn *mm_conn_by_id(struct gsm48_mmlayer *mm, + uint8_t proto, uint8_t transaction_id) +{ + struct gsm48_mm_conn *conn; + + llist_for_each_entry(conn, &mm->mm_conn, list) { + if (conn->protocol == proto && + conn->transaction_id == transaction_id) + return conn; + } + return NULL; +} + +/* find MM connection by reference */ +struct gsm48_mm_conn *mm_conn_by_ref(struct gsm48_mmlayer *mm, + uint32_t ref) +{ + struct gsm48_mm_conn *conn; + + llist_for_each_entry(conn, &mm->mm_conn, list) { + if (conn->ref == ref) + return conn; + } + return NULL; +} + +/* create MM connection instance */ +static struct gsm48_mm_conn* mm_conn_new(struct gsm48_mmlayer *mm, + int proto, uint8_t transaction_id, uint32_t ref) +{ + struct gsm48_mm_conn *conn = talloc_zero(l23_ctx, struct gsm48_mm_conn); + + if (!conn) + return NULL; + + LOGP(DMM, LOGL_INFO, "New MM Connection (proto 0x%02x trans_id %d " + "ref %d)\n", proto, transaction_id, ref); + + conn->mm = mm; + conn->state = GSM48_MMXX_ST_IDLE; + conn->transaction_id = transaction_id; + conn->protocol = proto; + conn->ref = ref; + + llist_add(&conn->list, &mm->mm_conn); + + return conn; +} + +/* destroy MM connection instance */ +void mm_conn_free(struct gsm48_mm_conn *conn) +{ + LOGP(DMM, LOGL_INFO, "Freeing MM Connection\n"); + + new_conn_state(conn, GSM48_MMXX_ST_IDLE); + + llist_del(&conn->list); + + talloc_free(conn); +} + +/* support function to release pending/all ongoing MM connections */ +static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any, + uint8_t cause, int error) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_conn *conn, *conn2; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + if (abort_any) + LOGP(DMM, LOGL_INFO, "Release any MM Connection\n"); + else + LOGP(DMM, LOGL_INFO, "Release pending MM Connections\n"); + + /* release MM connection(s) */ + llist_for_each_entry_safe(conn, conn2, &mm->mm_conn, list) { + /* abort any OR the pending connection */ + if (abort_any || conn->state == GSM48_MMXX_ST_CONN_PEND) { + /* send MMxx-REL-IND */ + nmsg = NULL; + switch(conn->protocol) { + case GSM48_PDISC_CC: + nmsg = gsm48_mmxx_msgb_alloc( + error ? GSM48_MMCC_ERR_IND + : GSM48_MMCC_REL_IND, conn->ref, + conn->transaction_id); + break; + case GSM48_PDISC_NC_SS: + nmsg = gsm48_mmxx_msgb_alloc( + error ? GSM48_MMSS_ERR_IND + : GSM48_MMSS_REL_IND, conn->ref, + conn->transaction_id); + break; + case GSM48_PDISC_SMS: + nmsg = gsm48_mmxx_msgb_alloc( + error ? GSM48_MMSMS_ERR_IND + : GSM48_MMSMS_REL_IND, conn->ref, + conn->transaction_id); + break; + } + if (!nmsg) { + /* this should not happen */ + mm_conn_free(conn); + continue; /* skip if not of CC type */ + } + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = cause; + gsm48_mmxx_upmsg(ms, nmsg); + + mm_conn_free(conn); + } + } + return 0; +} + +/* + * process handlers (Common procedures) + */ + +/* sending MM STATUS message */ +static int gsm48_mm_tx_mm_status(struct osmocom_ms *ms, uint8_t cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *ngh; + uint8_t *reject_cause; + + LOGP(DMM, LOGL_INFO, "MM STATUS (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + reject_cause = msgb_put(nmsg, 1); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_STATUS; + *reject_cause = cause; + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0); +} + +/* 4.3.1.2 sending TMSI REALLOCATION COMPLETE message */ +static int gsm48_mm_tx_tmsi_reall_cpl(struct osmocom_ms *ms) +{ + struct msgb *nmsg; + struct gsm48_hdr *ngh; + + LOGP(DMM, LOGL_INFO, "TMSI REALLOCATION COMPLETE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_TMSI_REALL_COMPL; + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0); +} + +/* 4.3.1 TMSI REALLOCATION COMMAND is received */ +static int gsm48_mm_rx_tmsi_realloc_cmd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm48_loc_area_id *lai = (struct gsm48_loc_area_id *) gh->data; + uint8_t mi_type, *mi; + uint32_t tmsi; + + if (payload_len < sizeof(struct gsm48_loc_area_id) + 2) { + short_read: + LOGP(DMM, LOGL_NOTICE, "Short read of TMSI REALLOCATION " + "COMMAND message error.\n"); + return -EINVAL; + } + /* LAI */ + gsm48_decode_lai(lai, &subscr->mcc, &subscr->mnc, &subscr->lac); + /* MI */ + mi = gh->data + sizeof(struct gsm48_loc_area_id); + mi_type = mi[1] & GSM_MI_TYPE_MASK; + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + if (payload_len + sizeof(struct gsm48_loc_area_id) < 6 + || mi[0] < 5) + goto short_read; + memcpy(&tmsi, mi+2, 4); + subscr->tmsi = ntohl(tmsi); + LOGP(DMM, LOGL_INFO, "TMSI 0x%08x (%u) assigned.\n", + subscr->tmsi, subscr->tmsi); + gsm48_mm_tx_tmsi_reall_cpl(ms); + break; + case GSM_MI_TYPE_IMSI: + subscr->tmsi = 0xffffffff; + LOGP(DMM, LOGL_INFO, "TMSI removed.\n"); + gsm48_mm_tx_tmsi_reall_cpl(ms); + break; + default: + subscr->tmsi = 0xffffffff; + LOGP(DMM, LOGL_NOTICE, "TMSI reallocation with unknown MI " + "type %d.\n", mi_type); + gsm48_mm_tx_mm_status(ms, GSM48_REJECT_INCORRECT_MESSAGE); + } + + /* store LOCI on sim */ + gsm_subscr_write_loci(ms); + + return 0; +} + +/* 4.3.2.2 AUTHENTICATION REQUEST is received */ +static int gsm48_mm_rx_auth_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_settings *set = &ms->settings; + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm48_auth_req *ar = (struct gsm48_auth_req *) gh->data; + uint8_t no_sim = 0; + + if (payload_len < sizeof(struct gsm48_auth_req)) { + LOGP(DMM, LOGL_NOTICE, "Short read of AUTHENTICATION REQUEST " + "message error.\n"); + return -EINVAL; + } + + /* SIM is not available */ + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_INFO, "AUTHENTICATION REQUEST without SIM\n"); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_NOT_COMPATIBLE); + } + + LOGP(DMM, LOGL_INFO, "AUTHENTICATION REQUEST (seq %d)\n", ar->key_seq); + + /* key_seq and random + * in case of test card, there is a dummy response. + * authentication request is possible during emergency call, if + * IMSI is known to the network. in case of emergency IMSI, we need to + * send a dummy response also. + */ + if (mm->est_cause == RR_EST_CAUSE_EMERGENCY && set->emergency_imsi[0]) + no_sim = 1; + gsm_subscr_generate_kc(ms, ar->key_seq, ar->rand, no_sim); + + /* wait for auth response event from SIM */ + return 0; +} + +/* 4.3.2.2 sending AUTHENTICATION RESPONSE */ +static int gsm48_mm_tx_auth_rsp(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mm_event *mme = (struct gsm48_mm_event *) msg->data; + struct msgb *nmsg; + struct gsm48_hdr *ngh; + uint8_t *sres; + + LOGP(DMM, LOGL_INFO, "AUTHENTICATION RESPONSE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_AUTH_RESP; + + /* SRES */ + sres = msgb_put(nmsg, 4); + memcpy(sres, mme->sres, 4); + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0); +} + +/* 4.3.2.5 AUTHENTICATION REJECT is received */ +static int gsm48_mm_rx_auth_rej(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmlayer *mm = &ms->mmlayer; + + LOGP(DMM, LOGL_INFO, "AUTHENTICATION REJECT\n"); + + stop_mm_t3212(mm); /* 4.4.2 */ + + /* SIM invalid */ + subscr->sim_valid = 0; + + /* TMSI and LAI invalid */ + subscr->tmsi = 0xffffffff; + subscr->lac = 0x0000; + + /* key is invalid */ + subscr->key_seq = 7; + + /* update status */ + new_sim_ustate(subscr, GSM_SIM_U3_ROAMING_NA); + + /* store LOCI on sim */ + gsm_subscr_write_loci(ms); + + /* abort IMSI detach procedure */ + if (mm->state == GSM48_MM_ST_IMSI_DETACH_INIT) { + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + /* abort RR connection */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_REQ); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *) nmsg->data; + nrrh->cause = GSM48_RR_CAUSE_NORMAL; + gsm48_rr_downmsg(ms, nmsg); + + /* CS process will trigger: return to MM IDLE / No SIM */ + return 0; + } + + return 0; +} + +/* 4.3.3.1 IDENTITY REQUEST is received */ +static int gsm48_mm_rx_id_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + uint8_t mi_type; + + if (payload_len < 1) { + LOGP(DMM, LOGL_NOTICE, "Short read of IDENTITY REQUEST message " + "error.\n"); + return -EINVAL; + } + + /* id type */ + mi_type = *gh->data; + LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST (mi_type %d)\n", mi_type); + + /* check if request can be fulfilled */ + if (!subscr->sim_valid && mi_type != GSM_MI_TYPE_IMEI + && mi_type != GSM_MI_TYPE_IMEISV) { + LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST without SIM\n"); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_NOT_COMPATIBLE); + } + if (mi_type == GSM_MI_TYPE_TMSI && subscr->tmsi == 0xffffffff) { + LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST of TMSI, but we have no " + "TMSI\n"); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_NOT_COMPATIBLE); + } + + return gsm48_mm_tx_id_rsp(ms, mi_type); +} + +/* send IDENTITY RESPONSE message */ +static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type) +{ + struct msgb *nmsg; + struct gsm48_hdr *ngh; + uint8_t buf[11]; + + LOGP(DMM, LOGL_INFO, "IDENTITY RESPONSE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_ID_RESP; + + /* MI */ + gsm48_encode_mi(buf, nmsg, ms, mi_type); + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0); +} + +/* 4.3.4.1 sending IMSI DETACH INDICATION message */ +static int gsm48_mm_tx_imsi_detach(struct osmocom_ms *ms, int rr_prim) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_support *sup = &ms->support; + struct gsm_settings *set = &ms->settings; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_hdr *ngh; + uint8_t pwr_lev; + uint8_t buf[11]; + struct gsm48_classmark1 cm; + + + LOGP(DMM, LOGL_INFO, "IMSI DETACH INDICATION\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_IMSI_DETACH_IND; + + /* classmark 1 */ + if (rr->cd_now.arfcn >= 512 && rr->cd_now.arfcn <= 885) + pwr_lev = sup->class_dcs - 1; + else + pwr_lev = sup->class_900 - 1; + gsm48_encode_classmark1(&cm, sup->rev_lev, sup->es_ind, set->a5_1, + pwr_lev); + msgb_v_put(nmsg, *((uint8_t *)&cm)); + /* MI */ + if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */ + gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_TMSI); + LOGP(DMM, LOGL_INFO, " using TMSI 0x%08x\n", subscr->tmsi); + } else { + gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_IMSI); + LOGP(DMM, LOGL_INFO, " using IMSI %s\n", subscr->imsi); + } + + /* push RR header and send down */ + mm->est_cause = RR_EST_CAUSE_OTHER_SDCCH; + return gsm48_mm_to_rr(ms, nmsg, rr_prim, mm->est_cause); +} + +/* detach has ended */ +static int gsm48_mm_imsi_detach_end(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "IMSI has been detached.\n"); + + /* stop IMSI detach timer (if running) */ + stop_mm_t3220(mm); + + + /* SIM invalid */ + subscr->sim_valid = 0; + + /* wait for RR idle and then power off when IMSI is detached */ + if (ms->shutdown) { + if (mm->state == GSM48_MM_ST_MM_IDLE) { + mobile_exit(ms, 1); + return 0; + } + /* power off when MM idle */ + mm->power_off_idle = 1; + + return 0; + } + + /* send SIM remove event to gsm322 */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + /* CS process will trigger return to MM IDLE / No SIM */ + return 0; +} + +/* start an IMSI detach in MM IDLE */ +static int gsm48_mm_imsi_detach_start(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + + /* we may silently finish IMSI detach */ + if (!s->att_allowed || !subscr->imsi_attached) { + LOGP(DMM, LOGL_INFO, "IMSI detach not required.\n"); + + return gsm48_mm_imsi_detach_end(ms, msg); + } + LOGP(DMM, LOGL_INFO, "IMSI detach started (MM IDLE)\n"); + + new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_IMSI_D, 0); + + /* establish RR and send IMSI detach */ + return gsm48_mm_tx_imsi_detach(ms, GSM48_RR_EST_REQ); +} + +/* IMSI detach has been sent, wait for RR release */ +static int gsm48_mm_imsi_detach_sent(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* start T3220 (4.3.4.1) */ + start_mm_t3220(mm); + + LOGP(DMM, LOGL_INFO, "IMSI detach started (Wait for RR release)\n"); + + new_mm_state(mm, GSM48_MM_ST_IMSI_DETACH_INIT, 0); + + return 0; +} + +/* release MM connection and proceed with IMSI detach */ +static int gsm48_mm_imsi_detach_release(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + /* release all connections */ + gsm48_mm_release_mm_conn(ms, 1, 16, 0); + + /* wait for release of RR */ + if (!s->att_allowed || !subscr->imsi_attached) { + LOGP(DMM, LOGL_INFO, "IMSI detach not required.\n"); + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + + /* power off */ + if (ms->shutdown) { + mobile_exit(ms, 1); + return 0; + } + + return 0; + } + + /* send IMSI detach */ + gsm48_mm_tx_imsi_detach(ms, GSM48_RR_DATA_REQ); + + /* go to sent state */ + return gsm48_mm_imsi_detach_sent(ms, msg); +} + +/* ignore ongoing IMSI detach */ +static int gsm48_mm_imsi_detach_ignore(struct osmocom_ms *ms, struct msgb *msg) +{ + return 0; +} + +/* delay until state change (and then retry) */ +static int gsm48_mm_imsi_detach_delay(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + LOGP(DMM, LOGL_INFO, "IMSI detach delayed.\n"); + + /* remember to detach later */ + mm->delay_detach = 1; + + return 0; +} + +/* 4.3.5.2 ABORT is received */ +static int gsm48_mm_rx_abort(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + uint8_t reject_cause; + + if (payload_len < 1) { + LOGP(DMM, LOGL_NOTICE, "Short read of ABORT message error.\n"); + return -EINVAL; + } + + reject_cause = *gh->data; + + if (llist_empty(&mm->mm_conn)) { + LOGP(DMM, LOGL_NOTICE, "ABORT (cause #%d) while no MM " + "connection is established.\n", reject_cause); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_NOT_COMPATIBLE); + } else { + LOGP(DMM, LOGL_NOTICE, "ABORT (cause #%d) while MM connection " + "is established.\n", reject_cause); + /* stop MM connection timer */ + stop_mm_t3230(mm); + + gsm48_mm_release_mm_conn(ms, 1, 16, 0); + } + + if (reject_cause == GSM48_REJECT_ILLEGAL_ME) { + /* SIM invalid */ + subscr->sim_valid = 0; + + /* TMSI and LAI invalid */ + subscr->tmsi = 0xffffffff; + subscr->lac = 0x0000; + + /* key is invalid */ + subscr->key_seq = 7; + + /* update status */ + new_sim_ustate(subscr, GSM_SIM_U3_ROAMING_NA); + + /* store LOCI on sim */ + gsm_subscr_write_loci(ms); + + /* CS process will trigger: return to MM IDLE / No SIM */ + return 0; + } + + return 0; +} + +/* 4.3.6.2 MM INFORMATION is received */ +static int gsm48_mm_rx_info(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + + if (payload_len < 0) { + LOGP(DMM, LOGL_NOTICE, "Short read of MM INFORMATION message " + "error.\n"); + return -EINVAL; + } + tlv_parse(&tp, &gsm48_mm_att_tlvdef, gh->data, payload_len, 0, 0); + + /* long name */ + if (TLVP_PRESENT(&tp, GSM48_IE_NAME_LONG)) { + decode_network_name(mm->name_long, sizeof(mm->name_long), + TLVP_VAL(&tp, GSM48_IE_NAME_LONG)-1); + } + /* short name */ + if (TLVP_PRESENT(&tp, GSM48_IE_NAME_SHORT)) { + decode_network_name(mm->name_short, sizeof(mm->name_short), + TLVP_VAL(&tp, GSM48_IE_NAME_SHORT)-1); + } + + return 0; +} + +/* + * process handlers for Location Update + IMSI attach (MM specific procedures) + */ + +/* 4.4.2 received sysinfo change event */ +static int gsm48_mm_sysinfo(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + + /* t3212 not changed in these states */ + if (mm->state == GSM48_MM_ST_MM_IDLE + && (mm->substate == GSM48_MM_SST_NO_CELL_AVAIL + || mm->substate == GSM48_MM_SST_LIMITED_SERVICE + || mm->substate == GSM48_MM_SST_PLMN_SEARCH + || mm->substate == GSM48_MM_SST_PLMN_SEARCH_NORMAL)) + return 0; + + /* new periodic location update timer timeout */ + if (s->t3212 && s->t3212 != mm->t3212_value) { + if (bsc_timer_pending(&mm->t3212)) { + int t; + struct timeval current_time; + + /* get rest time */ + gettimeofday(¤t_time, NULL); + t = mm->t3212.timeout.tv_sec - current_time.tv_sec; + if (t < 0) + t = 0; + LOGP(DMM, LOGL_INFO, "New T3212 while timer is running " + "(value %d rest %d)\n", s->t3212, t); + + /* rest time modulo given value */ + mm->t3212.timeout.tv_sec = current_time.tv_sec + + (t % s->t3212); + } else { + uint32_t rand = random(); + + LOGP(DMM, LOGL_INFO, "New T3212 while timer is not " + "running (value %d)\n", s->t3212); + + /* value between 0 and given value */ + start_mm_t3212(mm, rand % (s->t3212 + 1)); + } + mm->t3212_value = s->t3212; + } + + return 0; +} + +/* 4.4.4.1 (re)start location update + * + * this function is called by + * - normal location update + * - periodic location update + * - imsi attach (normal loc. upd. function) + * - retry timers (T3211 and T3213) + */ +static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &cs->sel_si; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_settings *set = &ms->settings; + struct msgb *nmsg; + int msg_type; + + /* (re)start only if we still require location update */ + if (!mm->lupd_pending) { + LOGP(DMM, LOGL_INFO, "No loc. upd. pending.\n"); + return 0; + } + + /* must camp normally */ + if (cs->state != GSM322_C3_CAMPED_NORMALLY) { + LOGP(DMM, LOGL_INFO, "Loc. upd. not camping normally.\n"); + msg_type = GSM322_EVENT_REG_FAILED; + stop: + LOGP(DSUM, LOGL_INFO, "Location updating not possible\n"); + _stop: + mm->lupd_pending = 0; + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(msg_type); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + return 0; + } + + /* deny network, if disabled */ + if (set->no_lupd) { + LOGP(DMM, LOGL_INFO, "Loc. upd. disabled, adding " + "forbidden PLMN.\n"); + LOGP(DSUM, LOGL_INFO, "Location updating is disabled by " + "configuration\n"); + gsm_subscr_add_forbidden_plmn(subscr, cs->sel_mcc, + cs->sel_mnc, GSM48_REJECT_PLMN_NOT_ALLOWED); + msg_type = GSM322_EVENT_ROAMING_NA; + goto _stop; + } + + /* if LAI is forbidden, don't start */ + if (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc)) { + LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed PLMN.\n"); + msg_type = GSM322_EVENT_ROAMING_NA; + goto stop; + } + if (gsm322_is_forbidden_la(ms, cs->sel_mcc, + cs->sel_mnc, cs->sel_lac)) { + LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed LA.\n"); + msg_type = GSM322_EVENT_ROAMING_NA; + goto stop; + } + + /* 4.4.4.9 if cell is barred, don't start */ + if ((!subscr->acc_barr && s->cell_barr) + || (!subscr->acc_barr && !((subscr->acc_class & 0xfbff) & + (s->class_barr ^ 0xffff)))) { + LOGP(DMM, LOGL_INFO, "Loc. upd. no access.\n"); + msg_type = GSM322_EVENT_ROAMING_NA; + goto stop; + } + + mm->lupd_mcc = cs->sel_mcc; + mm->lupd_mnc = cs->sel_mnc; + mm->lupd_lac = cs->sel_lac; + + LOGP(DSUM, LOGL_INFO, "Perform location update (MCC %s, MNC %s " + "LAC 0x%04x)\n", gsm_print_mcc(mm->lupd_mcc), + gsm_print_mnc(mm->lupd_mnc), mm->lupd_lac); + + return gsm48_mm_tx_loc_upd_req(ms); +} + +/* initiate a normal location update / imsi attach */ +static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &cs->sel_si; + struct msgb *nmsg; + + /* in case we already have a location update going on */ + if (mm->lupd_pending) { + LOGP(DMM, LOGL_INFO, "Loc. upd. already pending.\n"); + + return -EBUSY; + } + + /* no location update, if limited service */ + if (cs->state != GSM322_C3_CAMPED_NORMALLY) { + LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed.\n"); + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* if location update is not required */ + if (subscr->ustate == GSM_SIM_U1_UPDATED + && cs->selected + && cs->sel_mcc == subscr->mcc + && cs->sel_mnc == subscr->mnc + && cs->sel_lac == subscr->lac + && (subscr->imsi_attached + || !s->att_allowed)) { + LOGP(DMM, LOGL_INFO, "Loc. upd. not required.\n"); + subscr->imsi_attached = 1; + + /* go straight to normal service state */ + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_NORMAL_SERVICE); + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* 4.4.3 is attachment required? */ + if (subscr->ustate == GSM_SIM_U1_UPDATED + && cs->selected + && cs->sel_mcc == subscr->mcc + && cs->sel_mnc == subscr->mnc + && cs->sel_lac == subscr->lac + && !subscr->imsi_attached + && s->att_allowed) { + /* do location update for IMSI attach */ + LOGP(DMM, LOGL_INFO, "Do Loc. upd. for IMSI attach.\n"); + mm->lupd_type = 2; + } else { + /* do normal location update */ + LOGP(DMM, LOGL_INFO, "Do normal Loc. upd.\n"); + mm->lupd_type = 0; + } + + /* start location update */ + mm->lupd_attempt = 0; + mm->lupd_pending = 1; + mm->lupd_ra_failure = 0; + + return gsm48_mm_loc_upd(ms, msg); +} + +/* initiate a periodic location update */ +static int gsm48_mm_loc_upd_periodic(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* in case we already have a location update going on */ + if (mm->lupd_pending) { + LOGP(DMM, LOGL_INFO, "Loc. upd. already pending.\n"); + return -EBUSY; + } + + /* start periodic location update */ + mm->lupd_type = 1; + mm->lupd_pending = 1; + mm->lupd_ra_failure = 0; + + return gsm48_mm_loc_upd(ms, msg); +} + +/* ignore location update */ +static int gsm48_mm_loc_upd_ignore(struct osmocom_ms *ms, struct msgb *msg) +{ + return 0; +} + +/* 9.2.15 send LOCATION UPDATING REQUEST message */ +static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_settings *set = &ms->settings; + struct gsm_support *sup = &ms->support; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_hdr *ngh; + struct gsm48_loc_upd_req *nlu; /* NOTE: mi_len is part of struct */ + uint8_t pwr_lev; + uint8_t buf[11]; + + LOGP(DMM, LOGL_INFO, "LOCATION UPDATING REQUEST\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + nlu = (struct gsm48_loc_upd_req *)msgb_put(nmsg, sizeof(*nlu)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_LOC_UPD_REQUEST; + + /* location updating type */ + nlu->type = mm->lupd_type; + /* cipering key */ + nlu->key_seq = subscr->key_seq; + /* LAI (last SIM stored LAI) + * + * NOTE: The TMSI is only valid within a LAI! + */ + gsm48_encode_lai(&nlu->lai, subscr->mcc, subscr->mnc, subscr->lac); + LOGP(DMM, LOGL_INFO, " using LAI (mcc %s mnc %s " "lac 0x%04x)\n", + gsm_print_mcc(subscr->mcc), + gsm_print_mnc(subscr->mnc), subscr->lac); + /* classmark 1 */ + if (rr->cd_now.arfcn >= 512 && rr->cd_now.arfcn <= 885) + pwr_lev = sup->class_dcs - 1; + else + pwr_lev = sup->class_900 - 1; + gsm48_encode_classmark1(&nlu->classmark1, sup->rev_lev, sup->es_ind, + set->a5_1, pwr_lev); + /* MI */ + if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */ + gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI); + LOGP(DMM, LOGL_INFO, " using TMSI 0x%08x\n", subscr->tmsi); + } else { + gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI); + LOGP(DMM, LOGL_INFO, " using IMSI %s\n", subscr->imsi); + } + msgb_put(nmsg, buf[1]); /* length is part of nlu */ + memcpy(&nlu->mi_len, buf + 1, 1 + buf[1]); + + new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_LUPD, 0); + + /* push RR header and send down */ + mm->est_cause = RR_EST_CAUSE_LOC_UPD; + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_EST_REQ, mm->est_cause); +} + +/* 4.4.4.1 RR is esablised during location update */ +static int gsm48_mm_est_loc_upd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* start location update timer */ + start_mm_t3210(mm); + + new_mm_state(mm, GSM48_MM_ST_LOC_UPD_INIT, 0); + + return 0; +} + +/* 4.4.4.6 LOCATION UPDATING ACCEPT is received */ +static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_loc_area_id *lai = (struct gsm48_loc_area_id *) gh->data; + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct msgb *nmsg; + + if (payload_len < sizeof(struct gsm48_loc_area_id)) { + short_read: + LOGP(DMM, LOGL_NOTICE, "Short read of LOCATION UPDATING ACCEPT " + "message error.\n"); + return -EINVAL; + } + tlv_parse(&tp, &gsm48_mm_att_tlvdef, + gh->data + sizeof(struct gsm48_loc_area_id), + payload_len - sizeof(struct gsm48_loc_area_id), 0, 0); + + /* update has finished */ + mm->lupd_pending = 0; + + /* RA was successfull */ + mm->lupd_ra_failure = 0; + + /* stop periodic location updating timer */ + stop_mm_t3212(mm); /* 4.4.2 */ + + /* LAI */ + gsm48_decode_lai(lai, &subscr->mcc, &subscr->mnc, &subscr->lac); + + /* stop location update timer */ + stop_mm_t3210(mm); + + /* reset attempt counter */ + mm->lupd_attempt = 0; + + /* mark SIM as attached */ + subscr->imsi_attached = 1; + + /* set the status in the sim to updated */ + new_sim_ustate(subscr, GSM_SIM_U1_UPDATED); + + /* store LOCI on sim */ + gsm_subscr_write_loci(ms); + + /* set last registered PLMN */ + if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { + subscr->plmn_valid = 1; + subscr->plmn_mcc = subscr->mcc; + subscr->plmn_mnc = subscr->mnc; + } + + LOGP(DSUM, LOGL_INFO, "Location update accepted\n"); + LOGP(DMM, LOGL_INFO, "LOCATION UPDATING ACCEPT (mcc %s mnc %s " + "lac 0x%04x)\n", gsm_print_mcc(subscr->mcc), + gsm_print_mnc(subscr->mnc), subscr->lac); + + /* remove LA from forbidden list */ + gsm322_del_forbidden_la(ms, subscr->mcc, subscr->mnc, subscr->lac); + + /* MI */ + if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) { + const uint8_t *mi; + uint8_t mi_type; + uint32_t tmsi; + + mi = TLVP_VAL(&tp, GSM48_IE_MOBILE_ID)-1; + if (mi[0] < 1) + goto short_read; + mi_type = mi[1] & GSM_MI_TYPE_MASK; + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + if (payload_len + sizeof(struct gsm48_loc_area_id) < 6 + || mi[0] < 5) + goto short_read; + memcpy(&tmsi, mi+2, 4); + subscr->tmsi = ntohl(tmsi); + LOGP(DMM, LOGL_INFO, "got TMSI 0x%08x (%u)\n", + subscr->tmsi, subscr->tmsi); + + /* store LOCI on sim */ + gsm_subscr_write_loci(ms); + + /* send TMSI REALLOCATION COMPLETE */ + gsm48_mm_tx_tmsi_reall_cpl(ms); + break; + case GSM_MI_TYPE_IMSI: + LOGP(DMM, LOGL_INFO, "TMSI removed\n"); + subscr->tmsi = 0xffffffff; + + /* store LOCI on sim */ + gsm_subscr_write_loci(ms); + + /* send TMSI REALLOCATION COMPLETE */ + gsm48_mm_tx_tmsi_reall_cpl(ms); + break; + default: + LOGP(DMM, LOGL_NOTICE, "TMSI reallocation with unknown " + "MI type %d.\n", mi_type); + } + } + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + /* follow on proceed */ + if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) + LOGP(DMM, LOGL_NOTICE, "follow-on proceed not supported.\n"); + + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + + return 0; +} + +/* 4.4.4.7 LOCATION UPDATING REJECT is received */ +static int gsm48_mm_rx_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + + if (payload_len < 1) { + LOGP(DMM, LOGL_NOTICE, "Short read of LOCATION UPDATING REJECT " + "message error.\n"); + return -EINVAL; + } + + /* RA was successfull */ + mm->lupd_ra_failure = 0; + + /* stop periodic location updating timer */ + stop_mm_t3212(mm); /* 4.4.2 */ + + /* stop location update timer */ + stop_mm_t3210(mm); + + /* store until RR is released */ + mm->lupd_rej_cause = *gh->data; + + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_LOC_UPD_REJ, 0); + + return 0; +} + +/* 4.4.4.7 RR is released after location update reject */ +static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct gsm322_msg *ngm; + + LOGP(DMM, LOGL_INFO, "Loc. upd. rejected (cause %d)\n", + mm->lupd_rej_cause); + + /* stop RR release timer */ + stop_mm_t3240(mm); + + /* new status */ + switch (mm->lupd_rej_cause) { + case GSM48_REJECT_IMSI_UNKNOWN_IN_HLR: + case GSM48_REJECT_ILLEGAL_MS: + case GSM48_REJECT_ILLEGAL_ME: + /* reset attempt counter */ + mm->lupd_attempt = 0; + + /* SIM invalid */ + subscr->sim_valid = 0; + + // fall through + case GSM48_REJECT_PLMN_NOT_ALLOWED: + case GSM48_REJECT_LOC_NOT_ALLOWED: + case GSM48_REJECT_ROAMING_NOT_ALLOWED: + /* TMSI and LAI invalid */ + subscr->tmsi = 0xffffffff; + subscr->lac = 0x0000; + + /* key is invalid */ + subscr->key_seq = 7; + + /* update status */ + new_sim_ustate(subscr, GSM_SIM_U3_ROAMING_NA); + + /* store LOCI on sim */ + gsm_subscr_write_loci(ms); + + /* update has finished */ + mm->lupd_pending = 0; + } + + /* send event to PLMN search process */ + switch(mm->lupd_rej_cause) { + case GSM48_REJECT_ROAMING_NOT_ALLOWED: + nmsg = gsm322_msgb_alloc(GSM322_EVENT_ROAMING_NA); + break; + case GSM48_REJECT_IMSI_UNKNOWN_IN_HLR: + case GSM48_REJECT_ILLEGAL_MS: + case GSM48_REJECT_ILLEGAL_ME: + nmsg = gsm322_msgb_alloc(GSM322_EVENT_INVALID_SIM); + break; + default: + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); + } + if (!nmsg) + return -ENOMEM; + ngm = (struct gsm322_msg *)nmsg->data; + ngm->reject = mm->lupd_rej_cause; + gsm322_plmn_sendmsg(ms, nmsg); + + /* forbidden list */ + switch (mm->lupd_rej_cause) { + case GSM48_REJECT_IMSI_UNKNOWN_IN_HLR: + LOGP(DSUM, LOGL_INFO, "Location update failed (IMSI unknown " + "in HLR)\n"); + break; + case GSM48_REJECT_ILLEGAL_MS: + LOGP(DSUM, LOGL_INFO, "Location update failed (Illegal MS)\n"); + break; + case GSM48_REJECT_ILLEGAL_ME: + LOGP(DSUM, LOGL_INFO, "Location update failed (Illegal ME)\n"); + break; + case GSM48_REJECT_PLMN_NOT_ALLOWED: + gsm_subscr_add_forbidden_plmn(subscr, mm->lupd_mcc, + mm->lupd_mnc, mm->lupd_rej_cause); + LOGP(DSUM, LOGL_INFO, "Location update failed (PLMN not " + "allowed)\n"); + break; + case GSM48_REJECT_LOC_NOT_ALLOWED: + case GSM48_REJECT_ROAMING_NOT_ALLOWED: + gsm322_add_forbidden_la(ms, mm->lupd_mcc, mm->lupd_mnc, + mm->lupd_lac, mm->lupd_rej_cause); + LOGP(DSUM, LOGL_INFO, "Location update failed (LAI not " + "allowed)\n"); + break; + default: + /* 4.4.4.9 continue with failure handling */ + return gsm48_mm_loc_upd_failed(ms, NULL); + } + + /* CS proc triggers: return to IDLE, case 13 is also handled there */ + return 0; +} + +/* 4.2.2 delay a location update */ +static int gsm48_mm_loc_upd_delay_per(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + LOGP(DMM, LOGL_INFO, "Schedule a pending periodic loc. upd.\n"); + mm->lupd_periodic = 1; + + return 0; +} + +/* delay a location update retry */ +static int gsm48_mm_loc_upd_delay_retry(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + LOGP(DMM, LOGL_INFO, "Schedule a pending periodic loc. upd.\n"); + mm->lupd_retry = 1; + + return 0; +} + +/* process failues as described in the lower part of 4.4.4.9 */ +static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + + LOGP(DSUM, LOGL_INFO, "Location update failed\n"); + + /* stop location update timer, if running */ + stop_mm_t3210(mm); + + if (subscr->ustate == GSM_SIM_U1_UPDATED + && mm->lupd_mcc == subscr->mcc + && mm->lupd_mnc == subscr->mnc + && mm->lupd_lac == subscr->lac) { + if (mm->lupd_attempt < 4) { + LOGP(DSUM, LOGL_INFO, "Try location update later\n"); + LOGP(DMM, LOGL_INFO, "Loc. upd. failed, retry #%d\n", + mm->lupd_attempt); + + /* start update retry timer */ + start_mm_t3211(mm); + + /* CS process will trigger: return to MM IDLE */ + return 0; + } else + LOGP(DMM, LOGL_INFO, "Loc. upd. failed too often.\n"); + } + + /* TMSI and LAI invalid */ + subscr->tmsi = 0xffffffff; + subscr->lac = 0x0000; + + /* key is invalid */ + subscr->key_seq = 7; + + /* update status */ + new_sim_ustate(subscr, GSM_SIM_U2_NOT_UPDATED); + + /* store LOCI on sim */ + gsm_subscr_write_loci(ms); + + /* start update retry timer (RR connection is released) */ + if (mm->lupd_attempt < 4) { + mm->start_t3211 = 1; + LOGP(DSUM, LOGL_INFO, "Try location update later\n"); + } + + /* CS process will trigger: return to MM IDLE */ + return 0; +} + +/* abort a location update due to radio failure or release */ +static int gsm48_mm_rel_loc_upd_abort(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + + /* stop RR release timer */ + stop_mm_t3240(mm); + + if (rrh->msg_type == GSM48_RR_REL_IND) { + LOGP(DMM, LOGL_INFO, "RR link released after loc. upd.\n"); + + /* continue with failure handling */ + return gsm48_mm_loc_upd_failed(ms, NULL); + } + + LOGP(DMM, LOGL_INFO, "Loc. upd. aborted by radio (cause #%d)\n", + rrh->cause); + + /* random access failure, but not two successive failures */ + if (rrh->cause == RR_REL_CAUSE_RA_FAILURE && !mm->lupd_ra_failure) { + mm->lupd_ra_failure = 1; + + /* start RA failure timer */ + start_mm_t3213(mm); + + return 0; + } + + /* RA was successfull or sent twice */ + mm->lupd_ra_failure = 0; + + /* continue with failure handling */ + return gsm48_mm_loc_upd_failed(ms, NULL); +} + +/* location update has timed out */ +static int gsm48_mm_loc_upd_timeout(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + /* abort RR connection */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_REQ); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *) nmsg->data; + nrrh->cause = GSM48_RR_CAUSE_ABNORMAL_TIMER; + gsm48_rr_downmsg(ms, nmsg); + + /* continue with failure handling */ + return gsm48_mm_loc_upd_failed(ms, NULL); +} + +/* + * process handlers for MM connections + */ + +/* cm reestablish request message from upper layer */ +static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim, + uint8_t cm_serv) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_settings *set = &ms->settings; + struct msgb *nmsg; + struct gsm48_hdr *ngh; + struct gsm48_service_request *nsr; /* NOTE: includes MI length */ + uint8_t *cm2lv; + uint8_t buf[11]; + + LOGP(DMM, LOGL_INFO, "CM SERVICE REQUEST (cause %d)\n", mm->est_cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + nsr = (struct gsm48_service_request *)msgb_put(nmsg, sizeof(*nsr)); + cm2lv = (uint8_t *)&nsr->classmark; + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_CM_SERV_REQ; + + /* type and key */ + nsr->cm_service_type = cm_serv; + nsr->cipher_key_seq = subscr->key_seq; + /* classmark 2 */ + cm2lv[0] = sizeof(struct gsm48_classmark2); + gsm48_rr_enc_cm2(ms, (struct gsm48_classmark2 *)(cm2lv + 1)); + /* MI */ + if (mm->est_cause == RR_EST_CAUSE_EMERGENCY && set->emergency_imsi[0]) { + LOGP(DMM, LOGL_INFO, "-> Using IMSI %s for emergency\n", + set->emergency_imsi); + gsm48_generate_mid_from_imsi(buf, set->emergency_imsi); + } else + if (!subscr->sim_valid) { /* have no SIM ? */ + LOGP(DMM, LOGL_INFO, "-> Using IMEI %s\n", + set->imei); + gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMEI); + } else + if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */ + gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI); + LOGP(DMM, LOGL_INFO, "-> Using TMSI\n"); + } else { + gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI); + LOGP(DMM, LOGL_INFO, "-> Using IMSI %s\n", + subscr->imsi); + } + msgb_put(nmsg, buf[1]); /* length is part of nsr */ + memcpy(&nsr->mi_len, buf + 1, 1 + buf[1]); + /* prio is optional for eMLPP */ + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, rr_prim, mm->est_cause); +} + +/* cm service abort message from upper layer + * NOTE: T3240 is started by the calling function + */ +static int gsm48_mm_tx_cm_service_abort(struct osmocom_ms *ms) +{ + struct msgb *nmsg; + struct gsm48_hdr *ngh; + + LOGP(DMM, LOGL_INFO, "CM SERVICE ABORT\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_CM_SERV_ABORT; + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0); +} + +/* cm service acknowledge is received from lower layer */ +static int gsm48_mm_rx_cm_service_acc(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + + return gsm48_mm_conn_go_dedic(ms); +} + +/* 9.2.6 CM SERVICE REJECT message received */ +static int gsm48_mm_rx_cm_service_rej(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + uint8_t abort_any = 0; + uint8_t reject_cause; + + if (payload_len < 1) { + LOGP(DMM, LOGL_NOTICE, "Short read of cm service reject " + "message error.\n"); + return -EINVAL; + } + + /* reject cause */ + reject_cause = *gh->data; + + LOGP(DMM, LOGL_INFO, "CM SERVICE REJECT (cause %d)\n", reject_cause); + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + /* selection action on cause value */ + switch (reject_cause) { + case GSM48_REJECT_IMSI_UNKNOWN_IN_VLR: + case GSM48_REJECT_ILLEGAL_ME: + abort_any = 1; + + /* TMSI and LAI invalid */ + subscr->tmsi = 0xffffffff; + subscr->lac = 0x0000; + + /* key is invalid */ + subscr->key_seq = 7; + + /* update status */ + new_sim_ustate(subscr, GSM_SIM_U2_NOT_UPDATED); + + /* store LOCI on sim */ + gsm_subscr_write_loci(ms); + + /* change to WAIT_NETWORK_CMD state impied by abort_any == 1 */ + + if (reject_cause == GSM48_REJECT_ILLEGAL_ME) + subscr->sim_valid = 0; + + break; + default: + /* state implied by the number of remaining connections */ + ; + } + + /* release MM connection(s) */ + gsm48_mm_release_mm_conn(ms, abort_any, 16, 0); + + /* state depends on the existance of remaining MM connections */ + if (llist_empty(&mm->mm_conn)) + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + else + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + + return 0; +} + +/* initiate an MM connection 4.5.1.1 + * + * this function is called when: + * - no RR connection exists + * - an RR connection exists, but this is the first MM connection + * - an RR connection exists, and there are already MM connection(s) + */ +static int gsm48_mm_init_mm(struct osmocom_ms *ms, struct msgb *msg, + int rr_prim) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + int msg_type = mmh->msg_type; + int emergency = 0; + uint8_t cause = 0, cm_serv = 0, proto = 0; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + struct gsm48_mm_conn *conn, *conn_found = NULL; + + /* reset loc. upd. counter on CM service request */ + mm->lupd_attempt = 0; + + /* find if there is already a pending connection */ + llist_for_each_entry(conn, &mm->mm_conn, list) { + if (conn->state == GSM48_MMXX_ST_CONN_PEND) { + conn_found = conn; + break; + } + } + + /* if pending connection */ + if (conn_found) { + LOGP(DMM, LOGL_INFO, "Init MM Connection, but already have " + "pending MM Connection.\n"); + cause = 17; + reject: + nmsg = NULL; + switch(msg_type) { + case GSM48_MMCC_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND, + mmh->ref, mmh->transaction_id); + break; + case GSM48_MMSS_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND, + mmh->ref, mmh->transaction_id); + break; + case GSM48_MMSMS_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND, + mmh->ref, mmh->transaction_id); + break; + } + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = cause; + gsm48_mmxx_upmsg(ms, nmsg); + + return -EBUSY; + } + /* in case of an emergency setup */ + if (msg_type == GSM48_MMCC_EST_REQ && mmh->emergency) + emergency = 1; + + /* if sim is not updated */ + if (!emergency && subscr->ustate != GSM_SIM_U1_UPDATED) { + LOGP(DMM, LOGL_INFO, "Init MM Connection, but SIM not " + "updated.\n"); + cause = 21; + goto reject; + } + + if (mm->state == GSM48_MM_ST_MM_IDLE) { + /* current MM idle state */ + switch (mm->substate) { + case GSM48_MM_SST_NORMAL_SERVICE: + case GSM48_MM_SST_PLMN_SEARCH_NORMAL: + LOGP(DMM, LOGL_INFO, "Init MM Connection.\n"); + break; /* allow when normal */ + case GSM48_MM_SST_LOC_UPD_NEEDED: + case GSM48_MM_SST_ATTEMPT_UPDATE: + /* store mm request if attempting to update */ + if (!emergency) { + LOGP(DMM, LOGL_INFO, "Init MM Connection, but " + "attempting to update.\n"); + cause = 21; + goto reject; + /* TODO: implement delay and start loc upd. */ + } + break; + default: + /* reject if not emergency */ + if (!emergency) { + LOGP(DMM, LOGL_INFO, "Init MM Connection, not " + "in normal state.\n"); + cause = 21; + goto reject; + } + break; + } + } else + LOGP(DMM, LOGL_INFO, "Init another MM Connection.\n"); + + /* set cause, service, proto */ + switch(msg_type) { + case GSM48_MMCC_EST_REQ: + if (emergency) { + cause = RR_EST_CAUSE_EMERGENCY; + cm_serv = GSM48_CMSERV_EMERGENCY; + } else { + cause = RR_EST_CAUSE_ORIG_TCHF; + cm_serv = GSM48_CMSERV_MO_CALL_PACKET; + } + proto = GSM48_PDISC_CC; + break; + case GSM48_MMSS_EST_REQ: + cause = RR_EST_CAUSE_OTHER_SDCCH; + cm_serv = GSM48_CMSERV_SUP_SERV; + proto = GSM48_PDISC_NC_SS; + break; + case GSM48_MMSMS_EST_REQ: + cause = RR_EST_CAUSE_OTHER_SDCCH; + cm_serv = GSM48_CMSERV_SMS; + proto = GSM48_PDISC_SMS; + break; + } + + /* create MM connection instance */ + conn = mm_conn_new(mm, proto, mmh->transaction_id, mmh->ref); + if (!conn) + return -ENOMEM; + + new_conn_state(conn, GSM48_MMXX_ST_CONN_PEND); + + /* send CM SERVICE REQUEST */ + if (rr_prim) { + mm->est_cause = cause; + return gsm48_mm_tx_cm_serv_req(ms, rr_prim, cm_serv); + } else + return 0; +} + +/* 4.5.1.1 a) MM connection request triggers RR connection */ +static int gsm48_mm_init_mm_no_rr(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + int rc; + + /* start MM connection by requesting RR connection */ + rc = gsm48_mm_init_mm(ms, msg, GSM48_RR_EST_REQ); + if (rc) + return rc; + + new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_MM_CON, 0); + + return 0; +} + +/* 4.5.1.1 a) RR is esablised during mm connection, wait for CM accepted */ +static int gsm48_mm_est_mm_con(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* 4.5.1.7 if there is no more MM connection */ + if (llist_empty(&mm->mm_conn)) { + LOGP(DMM, LOGL_INFO, "MM Connection, are already gone.\n"); + + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + + /* send abort */ + return gsm48_mm_tx_cm_service_abort(ms); + } + + /* start MM connection timer */ + start_mm_t3230(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_OUT_MM_CONN, 0); + + return 0; +} + +/* 4.5.1.1 b) MM connection request on existing RR connection */ +static int gsm48_mm_init_mm_first(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + int rc; + + /* start MM connection by sending data */ + rc = gsm48_mm_init_mm(ms, msg, GSM48_RR_DATA_REQ); + if (rc) + return rc; + + /* stop "RR connection release not allowed" timer */ + stop_mm_t3241(mm); + + /* start MM connection timer */ + start_mm_t3230(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_OUT_MM_CONN, 0); + + return 0; +} + +/* 4.5.1.1 b) another MM connection request on existing RR connection */ +static int gsm48_mm_init_mm_more(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + int rc; + + /* start MM connection by sending data */ + rc = gsm48_mm_init_mm(ms, msg, GSM48_RR_DATA_REQ); + if (rc) + return rc; + + /* start MM connection timer */ + start_mm_t3230(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_ADD_OUT_MM_CON, 0); + + return 0; +} + +/* 4.5.1.1 b) delay on WAIT FOR NETWORK COMMAND state */ +static int gsm48_mm_init_mm_wait(struct osmocom_ms *ms, struct msgb *msg) +{ + /* reject */ + gsm48_mm_init_mm_reject(ms, msg); +#if 0 + this requires handling when leaving this state... + + struct gsm48_mmlayer *mm = &ms->mmlayer; + int rc; + + /* just create the MM connection in pending state */ + rc = gsm48_mm_init_mm(ms, msg, 0); + if (rc) + return rc; + + /* start MM connection timer */ + start_mm_t3230(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_ADD_OUT_MM_CON, 0); +#endif + + return 0; +} + +/* initiate an mm connection other cases */ +static int gsm48_mm_init_mm_reject(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + int msg_type = mmh->msg_type; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + /* reject */ + nmsg = NULL; + switch(msg_type) { + case GSM48_MMCC_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND, mmh->ref, + mmh->transaction_id); + break; + case GSM48_MMSS_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND, mmh->ref, + mmh->transaction_id); + break; + case GSM48_MMSMS_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND, mmh->ref, + mmh->transaction_id); + break; + } + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = 17; + gsm48_mmxx_upmsg(ms, nmsg); + + return 0; +} + +/* accepting pending connection, got dedicated mode + * + * this function is called: + * - when ciphering command is received + * - when cm service is accepted + */ +static int gsm48_mm_conn_go_dedic(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_conn *conn, *conn_found = NULL; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + /* the first and only pending connection is the recent requested */ + llist_for_each_entry(conn, &mm->mm_conn, list) { + if (conn->state == GSM48_MMXX_ST_CONN_PEND) { + conn_found = conn; + break; + } + } + + /* if no pending connection (anymore) */ + if (!conn_found) { + LOGP(DMM, LOGL_INFO, "No pending MM Connection.\n"); + + return 0; + } + + new_conn_state(conn, GSM48_MMXX_ST_DEDICATED); + + /* send establishment confirm */ + nmsg = NULL; + switch(conn_found->protocol) { + case GSM48_PDISC_CC: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_EST_CNF, conn->ref, + conn->transaction_id); + break; + case GSM48_PDISC_NC_SS: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_EST_CNF, conn->ref, + conn->transaction_id); + break; + case GSM48_PDISC_SMS: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_EST_CNF, conn->ref, + conn->transaction_id); + break; + } + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = 17; + gsm48_mmxx_upmsg(ms, nmsg); + + return 0; +} + +/* a RR-SYNC-IND is received during MM connection establishment */ +static int gsm48_mm_sync_ind_wait(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + + if (rrh->cause != RR_SYNC_CAUSE_CIPHERING) { + LOGP(DMM, LOGL_NOTICE, "Ignore sync indication, not waiting " + "for CM service\n"); + return -EINVAL; + } + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + + return gsm48_mm_conn_go_dedic(ms); +} + +/* a RR-SYNC-IND is received during MM connection active */ +static int gsm48_mm_sync_ind_active(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_conn *conn; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + /* broadcast all MMCC connection(s) */ + llist_for_each_entry(conn, &mm->mm_conn, list) { + /* send MMCC-SYNC-IND */ + nmsg = NULL; + switch(conn->protocol) { + case GSM48_PDISC_CC: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_SYNC_IND, + conn->ref, conn->transaction_id); + break; + } + if (!nmsg) + continue; /* skip if not of CC type */ + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = 17; + /* copy L3 message */ + nmsg->l3h = msgb_put(nmsg, msgb_l3len(msg)); + memcpy(nmsg->l3h, msg->l3h, msgb_l3len(msg)); + gsm48_mmxx_upmsg(ms, nmsg); + } + + return 0; +} + +/* 4.5.1.2 RR abort/release is received during MM connection establishment */ +static int gsm48_mm_abort_mm_con(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + int cause; + + /* stop RR release timer */ + stop_mm_t3240(mm); + + /* this conversion is not of any standard */ + switch(rrh->cause) { + case RR_REL_CAUSE_NOT_AUTHORIZED: + case RR_REL_CAUSE_EMERGENCY_ONLY: + case RR_REL_CAUSE_TRY_LATER: + cause = 21; + break; + case RR_REL_CAUSE_NORMAL: + cause = 16; + break; + default: + cause = 47; + } + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + /* release all connections */ + gsm48_mm_release_mm_conn(ms, 1, cause, 1); + + /* no RR connection, so we return to MM IDLE */ + if (mm->state == GSM48_MM_ST_WAIT_RR_CONN_MM_CON) + return gsm48_mm_return_idle(ms, NULL); + + /* CS process will trigger: return to MM IDLE */ + return 0; +} + +/* 4.5.1.2 timeout is received during MM connection establishment */ +static int gsm48_mm_timeout_mm_con(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* release pending connection */ + gsm48_mm_release_mm_conn(ms, 0, 102, 0); + + /* state depends on the existance of remaining MM connections */ + if (llist_empty(&mm->mm_conn)) { + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + } else + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + + return 0; +} + +/* respond to paging */ +static int gsm48_mm_est(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + mm->est_cause = RR_EST_CAUSE_ANS_PAG_ANY; + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + + return 0; +} + +/* send CM data */ +static int gsm48_mm_data(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct gsm48_mm_conn *conn; + int msg_type = mmh->msg_type; + + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (!conn) { + LOGP(DMM, LOGL_INFO, "MMXX_DATA_REQ with unknown (already " + "released) ref=%x, sending MMXX_REL_IND\n", mmh->ref); + switch(msg_type & GSM48_MMXX_MASK) { + case GSM48_MMCC_CLASS: + mmh->msg_type = GSM48_MMCC_REL_IND; + break; + case GSM48_MMSS_CLASS: + mmh->msg_type = GSM48_MMSS_REL_IND; + break; + case GSM48_MMSMS_CLASS: + mmh->msg_type = GSM48_MMSMS_REL_IND; + break; + } + mmh->cause = 31; + + /* mirror message with REL_IND + cause */ + return gsm48_mmxx_upmsg(ms, msg); + } + + /* pull MM header */ + msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr)); + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, msg, GSM48_RR_DATA_REQ, 0); +} + +/* release of MM connection (active state) */ +static int gsm48_mm_release_active(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct gsm48_mm_conn *conn; + + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (conn) + mm_conn_free(conn); + + /* state depends on the existance of remaining MM connections */ + if (llist_empty(&mm->mm_conn)) { + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + } else + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + + return 0; +} + +/* release of MM connection (wait for additional state) */ +static int gsm48_mm_release_wait_add(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct gsm48_mm_conn *conn; + + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (conn) + mm_conn_free(conn); + + return 0; +} + +/* release of MM connection (wait for active state) */ +static int gsm48_mm_release_wait_active(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct gsm48_mm_conn *conn; + + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (conn) + mm_conn_free(conn); + + /* 4.5.1.7 if there is no MM connection during wait for active state */ + if (llist_empty(&mm->mm_conn)) { + LOGP(DMM, LOGL_INFO, "No MM Connection during 'wait for " + "active' state.\n"); + + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + + /* send abort */ + return gsm48_mm_tx_cm_service_abort(ms); + } + + return 0; +} + +/* release of MM connection (wait for RR state) */ +static int gsm48_mm_release_wait_rr(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct gsm48_mm_conn *conn; + + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (conn) + mm_conn_free(conn); + + /* later, if RR connection is established, the CM SERIVE ABORT + * message will be sent + */ + return 0; +} + +/* abort RR connection (due to T3240) */ +static int gsm48_mm_abort_rr(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + /* send abort to RR */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_REQ); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *) nmsg->data; + nrrh->cause = GSM48_RR_CAUSE_ABNORMAL_TIMER; + gsm48_rr_downmsg(ms, nmsg); + + /* CS process will trigger: return to MM IDLE / No SIM */ + return 0; +} + +/* + * other processes + */ + +/* RR is released in other states */ +static int gsm48_mm_rel_other(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* stop RR release timer (if running) */ + stop_mm_t3240(mm); + + /* CS process will trigger: return to MM IDLE */ + return 0; +} + +/* + * state machines + */ + +/* state trasitions for MMxx-SAP messages from upper layers */ +static struct downstate { + uint32_t states; + uint32_t substates; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} downstatelist[] = { + /* 4.2.2.1 Normal service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr}, + + /* 4.2.2.2 Attempt to update / Loc. Upd. needed */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | + SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, /* emergency only */ + + /* 4.2.2.3 Limited service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + /* 4.2.2.4 No IMSI */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + /* 4.2.2.5 PLMN search, normal service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr}, + + /* 4.2.2.6 PLMN search */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + /* 4.5.1.1 MM Connection (EST) */ + {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES, + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_first}, + + {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES, + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_first}, + + {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES, + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_first}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_more}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_more}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_more}, + + {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_wait}, + + {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_wait}, + + {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_wait}, + + {ALL_STATES, ALL_STATES, + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_reject}, + + {ALL_STATES, ALL_STATES, + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_reject}, + + {ALL_STATES, ALL_STATES, + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_reject}, + + /* 4.5.2.1 MM Connection (DATA) */ + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMCC_DATA_REQ, gsm48_mm_data}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMSS_DATA_REQ, gsm48_mm_data}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMSMS_DATA_REQ, gsm48_mm_data}, + + /* 4.5.2.1 MM Connection (REL) */ + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMCC_REL_REQ, gsm48_mm_release_active}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMSS_REL_REQ, gsm48_mm_release_active}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMSMS_REL_REQ, gsm48_mm_release_active}, + + {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_add}, + + {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMSS_REL_REQ, gsm48_mm_release_wait_add}, + + {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_add}, + + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES, + GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_active}, + + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES, + GSM48_MMSS_REL_REQ, gsm48_mm_release_wait_active}, + + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES, + GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_active}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES, + GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_rr}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES, + GSM48_MMSS_REL_REQ, gsm48_mm_release_wait_rr}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES, + GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_rr}, +}; + +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct downstate)) + +int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + int msg_type = mmh->msg_type; + struct gsm48_mm_conn *conn; + int i, rc; + + /* keep up to date with the transaction ID */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (conn) + conn->transaction_id = mmh->transaction_id; + + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event in state %s\n", + ms->name, get_mmxx_name(msg_type), + gsm48_mm_state_names[mm->state]); + if (mm->state == GSM48_MM_ST_MM_IDLE) + LOGP(DMM, LOGL_INFO, "-> substate %s\n", + gsm48_mm_substate_names[mm->substate]); + LOGP(DMM, LOGL_INFO, "-> callref %x, transaction_id %d\n", + mmh->ref, mmh->transaction_id); + + /* Find function for current state and message */ + for (i = 0; i < DOWNSLLEN; i++) + if ((msg_type == downstatelist[i].type) + && ((1 << mm->state) & downstatelist[i].states) + && ((1 << mm->substate) & downstatelist[i].substates)) + break; + if (i == DOWNSLLEN) { + LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n"); + msgb_free(msg); + return 0; + } + + rc = downstatelist[i].rout(ms, msg); + + if (downstatelist[i].rout != gsm48_mm_data) + msgb_free(msg); + + return rc; +} + +/* state trasitions for radio ressource messages (lower layer) */ +static struct rrdatastate { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} rrdatastatelist[] = { + /* paging */ + {SBIT(GSM48_MM_ST_MM_IDLE), + GSM48_RR_EST_IND, gsm48_mm_est}, + + /* imsi detach */ + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D), /* 4.3.4.4 */ + GSM48_RR_EST_CNF, gsm48_mm_imsi_detach_sent}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D), /* 4.3.4.4 (unsuc.) */ + GSM48_RR_REL_IND, gsm48_mm_imsi_detach_end}, + /* also this may happen if SABM is ackwnowledged with DISC */ + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D), /* 4.3.4.4 (lost) */ + GSM48_RR_ABORT_IND, gsm48_mm_imsi_detach_end}, + + {SBIT(GSM48_MM_ST_IMSI_DETACH_INIT), /* 4.3.4.4 (unsuc.) */ + GSM48_RR_REL_IND, gsm48_mm_imsi_detach_end}, + + {SBIT(GSM48_MM_ST_IMSI_DETACH_INIT), /* 4.3.4.4 (lost) */ + GSM48_RR_ABORT_IND, gsm48_mm_imsi_detach_end}, + + /* location update */ + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_LUPD), /* 4.4.4.1 */ + GSM48_RR_EST_CNF, gsm48_mm_est_loc_upd}, + + {SBIT(GSM48_MM_ST_LOC_UPD_INIT) | + SBIT(GSM48_MM_ST_WAIT_RR_CONN_LUPD), /* 4.4.4.9 */ + GSM48_RR_REL_IND, gsm48_mm_rel_loc_upd_abort}, + + {SBIT(GSM48_MM_ST_LOC_UPD_INIT) | + SBIT(GSM48_MM_ST_WAIT_RR_CONN_LUPD), /* 4.4.4.9 */ + GSM48_RR_ABORT_IND, gsm48_mm_rel_loc_upd_abort}, + + {SBIT(GSM48_MM_ST_LOC_UPD_REJ), /* 4.4.4.7 */ + GSM48_RR_REL_IND, gsm48_mm_rel_loc_upd_rej}, + + {SBIT(GSM48_MM_ST_LOC_UPD_REJ), /* 4.4.4.7 */ + GSM48_RR_ABORT_IND, gsm48_mm_rel_loc_upd_rej}, + + /* MM connection (EST) */ + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), /* 4.5.1.1 */ + GSM48_RR_EST_CNF, gsm48_mm_est_mm_con}, + + /* MM connection (DATA) */ + {ALL_STATES, + GSM48_RR_DATA_IND, gsm48_mm_data_ind}, + + /* MM connection (SYNC) */ + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* 4.5.1.1 */ + GSM48_RR_SYNC_IND, gsm48_mm_sync_ind_wait}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), + GSM48_RR_SYNC_IND, gsm48_mm_sync_ind_active}, + + /* MM connection (REL/ABORT) */ + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON) | + SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* 4.5.1.2 */ + GSM48_RR_REL_IND, gsm48_mm_abort_mm_con}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON) | + SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* 4.5.1.2 */ + GSM48_RR_ABORT_IND, gsm48_mm_abort_mm_con}, + + /* MM connection (REL/ABORT with re-establishment possibility) */ + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), /* not supported */ + GSM48_RR_REL_IND, gsm48_mm_abort_mm_con}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* not supported */ + GSM48_RR_ABORT_IND, gsm48_mm_abort_mm_con}, + + /* other (also wait for network command) */ + {ALL_STATES, + GSM48_RR_REL_IND, gsm48_mm_rel_other}, + + {ALL_STATES, + GSM48_RR_ABORT_IND, gsm48_mm_rel_other}, +}; + +#define RRDATASLLEN \ + (sizeof(rrdatastatelist) / sizeof(struct rrdatastate)) + +static int gsm48_rcv_rr(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + int msg_type = rrh->msg_type; + int i, rc; + + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' from RR in state %s\n", + ms->name, get_rr_name(msg_type), + gsm48_mm_state_names[mm->state]); + + /* find function for current state and message */ + for (i = 0; i < RRDATASLLEN; i++) + if ((msg_type == rrdatastatelist[i].type) + && ((1 << mm->state) & rrdatastatelist[i].states)) + break; + if (i == RRDATASLLEN) { + LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n"); + msgb_free(msg); + return 0; + } + + rc = rrdatastatelist[i].rout(ms, msg); + + if (rrdatastatelist[i].rout != gsm48_mm_data_ind) + msgb_free(msg); + + return rc; +} + +/* state trasitions for mobile managemnt messages (lower layer) */ +static struct mmdatastate { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} mmdatastatelist[] = { + {ALL_STATES, /* 4.3.1.2 */ + GSM48_MT_MM_TMSI_REALL_CMD, gsm48_mm_rx_tmsi_realloc_cmd}, + + {ALL_STATES, /* 4.3.2.2 */ + GSM48_MT_MM_AUTH_REQ, gsm48_mm_rx_auth_req}, + + {ALL_STATES, /* 4.3.2.5 */ + GSM48_MT_MM_AUTH_REJ, gsm48_mm_rx_auth_rej}, + + {ALL_STATES, /* 4.3.3.2 */ + GSM48_MT_MM_ID_REQ, gsm48_mm_rx_id_req}, + + {ALL_STATES, /* 4.3.5.2 */ + GSM48_MT_MM_ABORT, gsm48_mm_rx_abort}, + + {ALL_STATES, /* 4.3.6.2 */ + GSM48_MT_MM_INFO, gsm48_mm_rx_info}, + + {SBIT(GSM48_MM_ST_LOC_UPD_INIT), /* 4.4.4.6 */ + GSM48_MT_MM_LOC_UPD_ACCEPT, gsm48_mm_rx_loc_upd_acc}, + + {SBIT(GSM48_MM_ST_LOC_UPD_INIT), /* 4.4.4.7 */ + GSM48_MT_MM_LOC_UPD_REJECT, gsm48_mm_rx_loc_upd_rej}, + + {ALL_STATES, /* 4.5.1.1 */ + GSM48_MT_MM_CM_SERV_ACC, gsm48_mm_rx_cm_service_acc}, + + {ALL_STATES, /* 4.5.1.1 */ + GSM48_MT_MM_CM_SERV_REJ, gsm48_mm_rx_cm_service_rej}, +}; + +#define MMDATASLLEN \ + (sizeof(mmdatastatelist) / sizeof(struct mmdatastate)) + +static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc = gh->proto_discr & 0x0f; + uint8_t msg_type = gh->msg_type & 0xbf; + struct gsm48_mmxx_hdr *mmh; + int msg_supported = 0; /* determine, if message is supported at all */ + int rr_prim = -1, rr_est = -1; /* no prim set */ + uint8_t skip_ind; + int i, rc; + + /* 9.2.19 */ + if (msg_type == GSM48_MT_MM_NULL) + return 0; + + if (mm->state == GSM48_MM_ST_IMSI_DETACH_INIT) { + LOGP(DMM, LOGL_NOTICE, "DATA IND ignored during IMSI " + "detach.\n"); + return 0; + } + /* pull the RR header */ + msgb_pull(msg, sizeof(struct gsm48_rr_hdr)); + + /* create transaction (if not exists) and push message */ + switch (pdisc) { + case GSM48_PDISC_CC: + rr_prim = GSM48_MMCC_DATA_IND; + rr_est = GSM48_MMCC_EST_IND; + break; +#if 0 + case GSM48_PDISC_NC_SS: + rr_prim = GSM48_MMSS_DATA_IND; + rr_est = GSM48_MMSS_EST_IND; + break; + case GSM48_PDISC_SMS: + rr_prim = GSM48_MMSMS_DATA_IND; + rr_est = GSM48_MMSMS_EST_IND; + break; +#endif + } + if (rr_prim != -1) { + uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; + /* flip */ + struct gsm48_mm_conn *conn; + + /* find transaction, if any */ + conn = mm_conn_by_id(mm, pdisc, transaction_id); + + /* create MM connection instance */ + if (!conn) { + conn = mm_conn_new(mm, pdisc, transaction_id, + mm_conn_new_ref++); + rr_prim = rr_est; + } + if (!conn) + return -ENOMEM; + + /* push new header */ + msgb_push(msg, sizeof(struct gsm48_mmxx_hdr)); + mmh = (struct gsm48_mmxx_hdr *)msg->data; + mmh->msg_type = rr_prim; + mmh->ref = conn->ref; + + /* go MM CONN ACTIVE state */ + if (mm->state == GSM48_MM_ST_WAIT_NETWORK_CMD + || mm->state == GSM48_MM_ST_RR_CONN_RELEASE_NA) { + /* stop RR release timer */ + stop_mm_t3240(mm); + + /* stop "RR connection release not allowed" timer */ + stop_mm_t3241(mm); + + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + } + } + + /* forward message */ + switch (pdisc) { + case GSM48_PDISC_MM: + skip_ind = (gh->proto_discr & 0xf0) >> 4; + + /* ignore if skip indicator is not B'0000' */ + if (skip_ind) + return 0; + break; /* follow the selection proceedure below */ + + case GSM48_PDISC_CC: + return gsm48_rcv_cc(ms, msg); + +#if 0 + case GSM48_PDISC_NC_SS: + return gsm48_rcv_ss(ms, msg); + + case GSM48_PDISC_SMS: + return gsm48_rcv_sms(ms, msg); +#endif + + default: + LOGP(DMM, LOGL_NOTICE, "Protocol type 0x%02x unsupported.\n", + pdisc); + msgb_free(msg); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED); + } + + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' in MM state %s\n", ms->name, + get_mm_name(msg_type), gsm48_mm_state_names[mm->state]); + + stop_mm_t3212(mm); /* 4.4.2 */ + + /* 11.2 re-start pending RR release timer */ + if (bsc_timer_pending(&mm->t3240)) { + stop_mm_t3240(mm); + start_mm_t3240(mm); + } + + /* find function for current state and message */ + for (i = 0; i < MMDATASLLEN; i++) { + if (msg_type == mmdatastatelist[i].type) + msg_supported = 1; + if ((msg_type == mmdatastatelist[i].type) + && ((1 << mm->state) & mmdatastatelist[i].states)) + break; + } + if (i == MMDATASLLEN) { + msgb_free(msg); + if (msg_supported) { + LOGP(DMM, LOGL_NOTICE, "Message unhandled at this " + "state.\n"); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE); + } else { + LOGP(DMM, LOGL_NOTICE, "Message not supported.\n"); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED); + } + } + + rc = mmdatastatelist[i].rout(ms, msg); + + msgb_free(msg); + + return rc; +} + +/* state trasitions for mobile management events */ +static struct eventstate { + uint32_t states; + uint32_t substates; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} eventstatelist[] = { + /* 4.2.3 return to MM IDLE */ + {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_no_cell_found}, + + {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_return_idle}, + + /* 4.2.2.1 Normal service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_loc_upd_normal}, /* change */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_TIMEOUT_T3211, gsm48_mm_loc_upd}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_periodic}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_start}, + + /* 4.2.2.2 Attempt to update / Loc. upd. needed */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | + SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), + GSM48_MM_EVENT_USER_PLMN_SEL, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | + SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | + SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_loc_upd_normal}, /* change */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE), + GSM48_MM_EVENT_TIMEOUT_T3211, gsm48_mm_loc_upd}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE), + GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_periodic}, + + /* 4.2.2.3 Limited service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MM_EVENT_USER_PLMN_SEL, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_loc_upd_normal}, /* if allow. */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */ + + /* 4.2.2.4 No IMSI */ + /* 4.2.2.5 PLMN search, normal service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_no_cell_found}, /* 4.2.1.1 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_cell_selected}, /* 4.2.1.1 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_TIMEOUT_T3211, gsm48_mm_loc_upd_delay_retry}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd_delay_retry}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_start}, + + /* 4.2.2.6 PLMN search */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_no_cell_found}, /* 4.2.1.1 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_cell_selected}, /* 4.2.1.1 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */ + + /* No cell available */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_CELL_AVAIL), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_cell_selected}, /* 4.2.1.1 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_CELL_AVAIL), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */ + + /* IMSI detach in other cases */ + {SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, /* silently detach */ + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_end}, + + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_PROCESS_CM_SERV_P) | + SBIT(GSM48_MM_ST_WAIT_REEST) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) | + SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, /* we can release */ + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_release}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D) | + SBIT(GSM48_MM_ST_IMSI_DETACH_INIT) | + SBIT(GSM48_MM_ST_IMSI_DETACH_PEND), ALL_STATES, /* ignore */ + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_ignore}, + + {ALL_STATES, ALL_STATES, + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_delay}, + + {GSM48_MM_ST_IMSI_DETACH_INIT, ALL_STATES, + GSM48_MM_EVENT_TIMEOUT_T3220, gsm48_mm_imsi_detach_end}, + + /* location update in other cases */ + {ALL_STATES, ALL_STATES, + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_ignore}, + + {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, + GSM48_MM_EVENT_TIMEOUT_T3210, gsm48_mm_loc_upd_timeout}, + + {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, + GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd_failed}, + /* 4.4.4.9 c) (but without retry) */ + + /* SYSINFO event */ + {ALL_STATES, ALL_STATES, + GSM48_MM_EVENT_SYSINFO, gsm48_mm_sysinfo}, + + /* T3240 timed out */ + {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD) | + SBIT(GSM48_MM_ST_LOC_UPD_REJ), ALL_STATES, /* 4.4.4.8 */ + GSM48_MM_EVENT_TIMEOUT_T3240, gsm48_mm_abort_rr}, + + /* T3230 timed out */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_TIMEOUT_T3230, gsm48_mm_timeout_mm_con}, + + /* SIM reports SRES */ + {ALL_STATES, ALL_STATES, /* 4.3.2.2 */ + GSM48_MM_EVENT_AUTH_RESPONSE, gsm48_mm_tx_auth_rsp}, + +#if 0 + /* change in classmark is reported */ + {ALL_STATES, ALL_STATES, + GSM48_MM_EVENT_CLASSMARK_CHG, gsm48_mm_classm_chg}, +#endif +}; + +#define EVENTSLLEN \ + (sizeof(eventstatelist) / sizeof(struct eventstate)) + +static int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + int i, rc; + + if (mm->state == GSM48_MM_ST_MM_IDLE) { + if (msg_type != GSM48_MM_EVENT_SYSINFO) + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event in " + "state MM IDLE, %s\n", ms->name, + get_mmevent_name(msg_type), + gsm48_mm_substate_names[mm->substate]); + } else + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event in state " + "%s\n", ms->name, get_mmevent_name(msg_type), + gsm48_mm_state_names[mm->state]); + + /* Find function for current state and message */ + for (i = 0; i < EVENTSLLEN; i++) + if ((msg_type == eventstatelist[i].type) + && ((1 << mm->state) & eventstatelist[i].states) + && ((1 << mm->substate) & eventstatelist[i].substates)) + break; + if (i == EVENTSLLEN) { + LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n"); + return 0; + } + + rc = eventstatelist[i].rout(ms, msg); + + return rc; +} + +/* + * MM Register (SIM insert and remove) + */ + +/* register new SIM card and trigger attach */ +static int gsm48_mmr_reg_req(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *nmsg; + + /* schedule insertion of SIM */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_INSERT); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + /* 4.2.1.2 SIM is inserted in state NO IMSI */ + if (mm->state == GSM48_MM_ST_MM_IDLE + && mm->substate == GSM48_MM_SST_NO_IMSI) + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + gsm48_mm_set_plmn_search(ms)); + + return 0; +} + +/* trigger detach of sim card */ +static int gsm48_mmr_nreg_req(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *nmsg; + + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_IMSI_DETACH); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(mm->ms, nmsg); + + return 0; +} + +static int gsm48_rcv_mmr(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmr *mmr = (struct gsm48_mmr *)msg->data; + int msg_type = mmr->msg_type; + int rc = 0; + + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event\n", ms->name, + get_mmr_name(msg_type)); + switch(msg_type) { + case GSM48_MMR_REG_REQ: + rc = gsm48_mmr_reg_req(ms); + break; + case GSM48_MMR_NREG_REQ: + rc = gsm48_mmr_nreg_req(ms); + break; + default: + LOGP(DMM, LOGL_NOTICE, "Message unhandled.\n"); + } + + return rc; +} + + diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c new file mode 100644 index 00000000..dc2226a7 --- /dev/null +++ b/src/host/layer23/src/mobile/gsm48_rr.c @@ -0,0 +1,5065 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* Very short description of some of the procedures: + * + * A radio ressource request causes sendig a channel request on RACH. + * After receiving of an immediate assignment the link will be establised. + * After the link is established, the dedicated mode is entered and confirmed. + * + * A Paging request also triggers the channel request as above... + * After the link is established, the dedicated mode is entered and indicated. + * + * During dedicated mode, messages are transferred. + * + * When an assignment command or a handover command is received, the current + * link is released. After release, the new channel is activated and the + * link is established again. After link is establised, pending messages from + * radio ressource are sent. + * + * When the assignment or handover fails, the old channel is activate and the + * link is established again. Also pending messages are sent. + * + */ + +/* Testing delayed (immediate) assigment / handover + * + * When enabled, the starting time will be set by given frames in the future. + * If a starting time is given by the network, this time is ignored. + */ +//#define TEST_STARTING_TIMER 140 + +/* Testing if frequency modification works correctly "after time". + * + * When enabled, the starting time will be set in the future. + * A wrong channel is defined "before time", so noise is received until + * starting time elapses. + * If a starting time is given by the network, this time is ignored. + * Also channel definitions "before time" are ignored. + * + * NOTE: TEST_STARTING_TIMER MUST be defined also. + */ +//#define TEST_FREQUENCY_MOD + +#include <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <arpa/inet.h> + +#include <osmocore/msgb.h> +#include <osmocore/utils.h> +#include <osmocore/rsl.h> +#include <osmocore/gsm48.h> +#include <osmocore/bitvec.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/l1l2_interface.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/networks.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/mobile/vty.h> + +static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro); +static void stop_rr_t_starting(struct gsm48_rrlayer *rr); +static void stop_rr_t3124(struct gsm48_rrlayer *rr); +static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_rr_dl_est(struct osmocom_ms *ms); +static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms); +static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, + uint8_t mode); +static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg); + +/* + * support + */ + +#define MIN(a, b) ((a < b) ? a : b) + +int gsm48_encode_lai(struct gsm48_loc_area_id *lai, uint16_t mcc, + uint16_t mnc, uint16_t lac) +{ + lai->digits[0] = (mcc >> 8) | (mcc & 0xf0); + lai->digits[1] = (mcc & 0x0f) | (mnc << 4); + lai->digits[2] = (mnc >> 8) | (mnc & 0xf0); + lai->lac = htons(lac); + + return 0; +} + +/* decode "Power Command" (10.5.2.28) and (10.5.2.28a) */ +static int gsm48_decode_power_cmd_acc(struct gsm48_power_cmd *pc, + uint8_t *power_level, uint8_t *atc) +{ + *power_level = pc->power_level; + if (atc) /* only in case of 10.5.2.28a */ + *atc = pc->atc; + + return 0; +} + +/* 10.5.2.38 decode Starting time IE */ +static int gsm48_decode_start_time(struct gsm48_rr_cd *cd, + struct gsm48_start_time *st) +{ + cd->start = 1; + cd->start_tm.t1 = st->t1; + cd->start_tm.t2 = st->t2; + cd->start_tm.t3 = (st->t3_high << 3) | st->t3_low; + cd->start_tm.fn = gsm_gsmtime2fn(&cd->start_tm); + + return 0; +} + +/* decode "BA Range" (10.5.2.1a) */ +static int gsm48_decode_ba_range(const uint8_t *ba, uint8_t ba_len, + uint32_t *range, uint8_t *ranges, int max_ranges) +{ + /* ba = pointer to IE without IE type and length octets + * ba_len = number of octets + * range = pointer to store decoded range + * ranges = number of ranges decoded + * max_ranges = maximum number of decoded ranges that can be stored + */ + uint16_t lower, higher; + int i, n, required_octets; + + /* find out how much ba ranges will be decoded */ + n = *ba++; + ba_len --; + required_octets = 5 * (n >> 1) + 3 * (n & 1); + if (required_octets > ba_len) { + LOGP(DRR, LOGL_NOTICE, "BA range IE too short: %d ranges " + "require %d octets, but only %d octets remain.\n", + n, required_octets, ba_len); + *ranges = 0; + return -EINVAL; + } + if (max_ranges > n) + LOGP(DRR, LOGL_NOTICE, "BA range %d exceed the maximum number " + "of ranges supported by this mobile (%d).\n", + n, max_ranges); + n = max_ranges; + + /* decode ranges */ + for (i = 0; i < n; i++) { + if (!(i & 1)) { + /* decode even range number */ + lower = *ba++ << 2; + lower |= (*ba >> 6); + higher = (*ba++ & 0x3f) << 4; + higher |= *ba >> 4; + } else { + lower = (*ba++ & 0x0f) << 6; + lower |= *ba >> 2; + higher = (*ba++ & 0x03) << 8; + higher |= *ba++; + /* decode odd range number */ + } + *range++ = (higher << 16) | lower; + } + *ranges = n; + + return 0; +} + +/* decode "Cell Description" (10.5.2.2) */ +static int gsm48_decode_cell_desc(struct gsm48_cell_desc *cd, uint16_t *arfcn, + uint8_t *ncc, uint8_t *bcc) +{ + *arfcn = (cd->arfcn_hi << 8) + cd->arfcn_lo; + *ncc = cd->ncc; + *bcc = cd->bcc; + + return 0; +} + +/* decode "Synchronization Indication" (10.5.2.39) */ +static int gsm48_decode_sync_ind(struct gsm48_rrlayer *rr, + struct gsm48_sync_ind *si) +{ + rr->hando_sync_ind = si->si; + rr->hando_rot = si->rot; + rr->hando_nci = si->nci; + + return 0; +} + +/* 3.1.4.3 set sequence number and increment */ +static int gsm48_apply_v_sd(struct gsm48_rrlayer *rr, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc = gh->proto_discr & 0x0f; + uint8_t v_sd; + + switch (pdisc) { + case GSM48_PDISC_MM: + case GSM48_PDISC_CC: + case GSM48_PDISC_NC_SS: + /* all thre pdiscs share the same V(SD) */ + pdisc = GSM48_PDISC_MM; + // fall through + case GSM48_PDISC_GROUP_CC: + case GSM48_PDISC_BCAST_CC: + case GSM48_PDISC_PDSS1: + case GSM48_PDISC_PDSS2: + /* extract v_sd(pdisc) */ + v_sd = (rr->v_sd >> pdisc) & 1; + + /* replace bit 7 vy v_sd */ + gh->msg_type &= 0xbf; + gh->msg_type |= (v_sd << 6); + + /* increment V(SD) */ + rr->v_sd ^= (1 << pdisc); + LOGP(DRR, LOGL_INFO, "Using and incrementing V(SD) = %d " + "(pdisc %x)\n", v_sd, pdisc); + break; + case GSM48_PDISC_RR: + case GSM48_PDISC_SMS: + /* no V(VSD) is required */ + break; + default: + LOGP(DRR, LOGL_ERROR, "Error, V(SD) of pdisc %x not handled\n", + pdisc); + return -ENOTSUP; + } + + return 0; +} + +/* set channel mode if supported, or return error cause */ +static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr, + uint8_t mode) +{ + struct gsm_settings *set = &ms->settings; + uint8_t ch_type, ch_subch, ch_ts; + + /* only complain if we use TCH/F or TCH/H */ + rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ch_type != RSL_CHAN_Bm_ACCHs + && ch_type != RSL_CHAN_Lm_ACCHs) + return 0; + + switch (mode) { + case GSM48_CMODE_SIGN: + LOGP(DRR, LOGL_INFO, "Mode: signalling\n"); + break; + case GSM48_CMODE_SPEECH_V1: + if (ch_type == RSL_CHAN_Bm_ACCHs) { + if (!set->full_v1) { + LOGP(DRR, LOGL_NOTICE, "Not supporting " + "full-rate speech V1\n"); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V1\n"); + } else { + if (!set->half_v1) { + LOGP(DRR, LOGL_NOTICE, "Not supporting " + "half-rate speech V1\n"); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V1\n"); + } + break; + case GSM48_CMODE_SPEECH_EFR: + if (ch_type == RSL_CHAN_Bm_ACCHs) { + if (!set->full_v2) { + LOGP(DRR, LOGL_NOTICE, "Not supporting " + "full-rate speech V2\n"); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V2\n"); + } else { + LOGP(DRR, LOGL_NOTICE, "Not supporting " + "half-rate speech V2\n"); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + break; + case GSM48_CMODE_SPEECH_AMR: + if (ch_type == RSL_CHAN_Bm_ACCHs) { + if (!set->full_v3) { + LOGP(DRR, LOGL_NOTICE, "Not supporting " + "full-rate speech V3\n"); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: full-rate speech V3\n"); + } else { + if (!set->half_v3) { + LOGP(DRR, LOGL_NOTICE, "Not supporting " + "half-rate speech V3\n"); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V3\n"); + } + break; + default: + LOGP(DRR, LOGL_ERROR, "Mode 0x%02x not supported!\n", mode); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + + return 0; +} + +/* apply new "alter_delay" in dedicated mode */ +int gsm48_rr_alter_delay(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_settings *set = &rr->ms->settings; + + if (rr->state != GSM48_RR_ST_DEDICATED) + return -EINVAL; + l1ctl_tx_param_req(ms, rr->cd_now.ind_ta - set->alter_delay, + (set->alter_tx_power) ? set->alter_tx_power_value + : rr->cd_now.ind_tx_power); + + return 0; +} + +/* + * state transition + */ + +const char *gsm48_rr_state_names[] = { + "idle", + "connection pending", + "dedicated", + "release pending", +}; + +static void new_rr_state(struct gsm48_rrlayer *rr, int state) +{ + if (state < 0 || state >= + (sizeof(gsm48_rr_state_names) / sizeof(char *))) + return; + + /* must check against equal state */ + if (rr->state == state) { + LOGP(DRR, LOGL_INFO, "equal state ? %s\n", + gsm48_rr_state_names[rr->state]); + return; + } + + LOGP(DRR, LOGL_INFO, "new state %s -> %s\n", + gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]); + + /* abort handover, in case of release of dedicated mode */ + if (rr->state == GSM48_RR_ST_DEDICATED) { + /* disable handover / assign state */ + rr->modify_state = GSM48_RR_MOD_NONE; + /* stop start_time_timer */ + stop_rr_t_starting(rr); + /* stop handover timer */ + stop_rr_t3124(rr); + } + + rr->state = state; + + if (state == GSM48_RR_ST_IDLE) { + struct msgb *msg, *nmsg; + struct gsm322_msg *em; + + /* release dedicated mode, if any */ + l1ctl_tx_dm_rel_req(rr->ms); + rr->ms->meas.rl_fail = 0; + rr->dm_est = 0; + l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_FULL); + /* free establish message, if any */ + rr->rr_est_req = 0; + if (rr->rr_est_msg) { + msgb_free(rr->rr_est_msg); + rr->rr_est_msg = NULL; + } + /* free all pending messages */ + while((msg = msgb_dequeue(&rr->downqueue))) + msgb_free(msg); + /* clear all descriptions of last channel */ + memset(&rr->cd_now, 0, sizeof(rr->cd_now)); + /* reset ciphering */ + rr->cipher_on = 0; + /* tell cell selection process to return to idle mode + * NOTE: this must be sent unbuffered, because it will + * leave camping state, so it locks against subsequent + * establishment of dedicated channel, before the + * cell selection process returned to camping state + * again. (after cell reselection) + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_RET_IDLE); + if (!nmsg) + return; + /* return to same cell after LOC.UPD. */ + if (rr->est_cause == RR_EST_CAUSE_LOC_UPD) { + em = (struct gsm322_msg *) nmsg->data; + em->same_cell = 1; + } + gsm322_c_event(rr->ms, nmsg); + msgb_free(nmsg); + /* reset any BA range */ + rr->ba_ranges = 0; + } +} + +/* + * messages + */ + +/* names of RR-SAP */ +static const struct value_string gsm48_rr_msg_names[] = { + { GSM48_RR_EST_REQ, "RR_EST_REQ" }, + { GSM48_RR_EST_IND, "RR_EST_IND" }, + { GSM48_RR_EST_CNF, "RR_EST_CNF" }, + { GSM48_RR_REL_IND, "RR_REL_IND" }, + { GSM48_RR_SYNC_IND, "RR_SYNC_IND" }, + { GSM48_RR_DATA_REQ, "RR_DATA_REQ" }, + { GSM48_RR_DATA_IND, "RR_DATA_IND" }, + { GSM48_RR_UNIT_DATA_IND, "RR_UNIT_DATA_IND" }, + { GSM48_RR_ABORT_REQ, "RR_ABORT_REQ" }, + { GSM48_RR_ABORT_IND, "RR_ABORT_IND" }, + { GSM48_RR_ACT_REQ, "RR_ACT_REQ" }, + { 0, NULL } +}; + +const char *get_rr_name(int value) +{ + return get_value_string(gsm48_rr_msg_names, value); +} + +/* allocate GSM 04.08 layer 3 message */ +struct msgb *gsm48_l3_msgb_alloc(void) +{ + struct msgb *msg; + + msg = msgb_alloc_headroom(L3_ALLOC_SIZE+L3_ALLOC_HEADROOM, + L3_ALLOC_HEADROOM, "GSM 04.08 L3"); + if (!msg) + return NULL; + msg->l3h = msg->data; + + return msg; +} + +/* allocate GSM 04.06 layer 2 RSL message */ +struct msgb *gsm48_rsl_msgb_alloc(void) +{ + struct msgb *msg; + + msg = msgb_alloc_headroom(RSL_ALLOC_SIZE+RSL_ALLOC_HEADROOM, + RSL_ALLOC_HEADROOM, "GSM 04.06 RSL"); + if (!msg) + return NULL; + msg->l2h = msg->data; + + return msg; +} + +/* allocate GSM 04.08 message (RR-SAP) */ +struct msgb *gsm48_rr_msgb_alloc(int msg_type) +{ + struct msgb *msg; + struct gsm48_rr_hdr *rrh; + + msg = msgb_alloc_headroom(RR_ALLOC_SIZE+RR_ALLOC_HEADROOM, + RR_ALLOC_HEADROOM, "GSM 04.08 RR"); + if (!msg) + return NULL; + + rrh = (struct gsm48_rr_hdr *) msgb_put(msg, sizeof(*rrh)); + rrh->msg_type = msg_type; + + return msg; +} + +/* queue message (RR-SAP) */ +int gsm48_rr_upmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + msgb_enqueue(&mm->rr_upqueue, msg); + + return 0; +} + +/* push rsl header and send (RSL-SAP) */ +static int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type, + struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + if (!msg->l3h) { + LOGP(DRR, LOGL_ERROR, "FIX l3h\n"); + return -EINVAL; + } + rsl_rll_push_l3(msg, msg_type, rr->cd_now.chan_nr, + rr->cd_now.link_id, 1); + + return rslms_recvmsg(msg, ms); +} + +/* push rsl header + release mode and send (RSL-SAP) */ +static int gsm48_send_rsl_rel(struct osmocom_ms *ms, uint8_t msg_type, + struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + rsl_rll_push_hdr(msg, msg_type, rr->cd_now.chan_nr, + rr->cd_now.link_id, 1); + + return rslms_recvmsg(msg, ms); +} + +/* enqueue messages (RSL-SAP) */ +static int gsm48_rx_rsl(struct msgb *msg, struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + msgb_enqueue(&rr->rsl_upqueue, msg); + + return 0; +} + +/* dequeue messages (RSL-SAP) */ +int gsm48_rsl_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&rr->rsl_upqueue))) { + /* msg is freed there */ + gsm48_rcv_rsl(ms, msg); + work = 1; /* work done */ + } + + return work; +} + +int gsm48_rr_start_monitor(struct osmocom_ms *ms) +{ + ms->rrlayer.monitor = 1; + + return 0; +} + +int gsm48_rr_stop_monitor(struct osmocom_ms *ms) +{ + ms->rrlayer.monitor = 0; + + return 0; +} + +/* + * timers handling + */ + +/* special timer to monitor measurements */ +static void timeout_rr_meas(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct gsm322_cellsel *cs = &rr->ms->cellsel; + struct rx_meas_stat *meas = &rr->ms->meas; + struct gsm_settings *set = &rr->ms->settings; + int rxlev, berr, snr; + uint8_t ch_type, ch_subch, ch_ts; + char text[256]; + + if (!cs->selected) { + goto restart; + } else if (!meas->frames) { + sprintf(text, "MON: no cell info"); + } else { + rxlev = meas->rxlev / meas->frames; + berr = meas->berr / meas->frames; + snr = meas->snr / meas->frames; + sprintf(text, "MON: f=%d lev=%s snr=%2d ber=%3d " + "LAI=%s %s %04x ID=%04x", cs->sel_arfcn, + gsm_print_rxlev(rxlev), berr, snr, + gsm_print_mcc(cs->sel_mcc), + gsm_print_mnc(cs->sel_mnc), cs->sel_lac, cs->sel_id); + if (rr->state == GSM48_RR_ST_DEDICATED) { + rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, + &ch_subch, &ch_ts); + sprintf(text + strlen(text), " TA=%d pwr=%d TS=%d", + rr->cd_now.ind_ta - set->alter_delay, + (set->alter_tx_power) ? set->alter_tx_power_value + : rr->cd_now.ind_tx_power, ch_ts); + if (ch_type == RSL_CHAN_SDCCH8_ACCH + || ch_type == RSL_CHAN_SDCCH4_ACCH) + sprintf(text + strlen(text), "/%d", ch_subch); + } + } + LOGP(DRR, LOGL_INFO, "%s\n", text); + if (rr->monitor) + vty_notify(rr->ms, "%s\n", text); + + if (rr->dm_est) + gsm48_rr_tx_meas_rep(rr->ms); + +restart: + meas->frames = meas->snr = meas->berr = meas->rxlev = 0; + start_rr_t_meas(rr, 1, 0); +} + +/* special timer to assign / handover when starting time is reached */ +static void timeout_rr_t_starting(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct msgb *nmsg; + + LOGP(DRR, LOGL_INFO, "starting timer has fired\n"); + + /* open channel when starting timer of IMM.ASS has fired */ + if (rr->modify_state == GSM48_RR_MOD_IMM_ASS) { + rr->modify_state = GSM48_RR_MOD_NONE; + gsm48_rr_dl_est(rr->ms); + return; + } + + /* start suspension of current link */ + LOGP(DRR, LOGL_INFO, "request suspension of data link\n"); + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return; + gsm48_send_rsl(rr->ms, RSL_MT_SUSP_REQ, nmsg); +} + +/* special timer to ensure that UA is sent before disconnecting channel */ +static void timeout_rr_t_rel_wait(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + + LOGP(DRR, LOGL_INFO, "L2 release timer has fired, done waiting\n"); + + /* return to idle now */ + new_rr_state(rr, GSM48_RR_ST_IDLE); +} + +/* 3.4.13.1.1: Timeout of T3110 */ +static void timeout_rr_t3110(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct osmocom_ms *ms = rr->ms; + struct msgb *nmsg; + uint8_t *mode; + + LOGP(DRR, LOGL_INFO, "timer T3110 has fired, release locally\n"); + + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* disconnect the main signalling link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = 1; /* local release */ + gsm48_send_rsl_rel(ms, RSL_MT_REL_REQ, nmsg); + + return; +} + +static void timeout_rr_t3122(void *arg) +{ + LOGP(DRR, LOGL_INFO, "timer T3122 has fired\n"); +} + +static void timeout_rr_t3124(void *arg) +{ + LOGP(DRR, LOGL_INFO, "timer T3124 has fired\n"); +} + +static void timeout_rr_t3126(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct osmocom_ms *ms = rr->ms; + + LOGP(DRR, LOGL_INFO, "timer T3126 has fired\n"); + if (rr->rr_est_req) { + struct msgb *msg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + struct gsm48_rr_hdr *rrh; + + LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n"); + if (!msg) + return; + rrh = (struct gsm48_rr_hdr *)msg->data; + rrh->cause = RR_REL_CAUSE_RA_FAILURE; + gsm48_rr_upmsg(ms, msg); + } + + new_rr_state(rr, GSM48_RR_ST_IDLE); +} + +static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro) +{ + rr->t_meas.cb = timeout_rr_meas; + rr->t_meas.data = rr; + bsc_schedule_timer(&rr->t_meas, sec, micro); +} + +static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T_rel_wait with %d.%03d seconds\n", sec, + micro / 1000); + rr->t_rel_wait.cb = timeout_rr_t_rel_wait; + rr->t_rel_wait.data = rr; + bsc_schedule_timer(&rr->t_rel_wait, sec, micro); +} + +static void start_rr_t_starting(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T_starting with %d.%03d seconds\n", sec, + micro / 1000); + rr->t_starting.cb = timeout_rr_t_starting; + rr->t_starting.data = rr; + bsc_schedule_timer(&rr->t_starting, sec, micro); +} + +static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T3110 with %d.%03d seconds\n", sec, + micro / 1000); + rr->t3110.cb = timeout_rr_t3110; + rr->t3110.data = rr; + bsc_schedule_timer(&rr->t3110, sec, micro); +} + +static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T3122 with %d.%03d seconds\n", sec, + micro / 1000); + rr->t3122.cb = timeout_rr_t3122; + rr->t3122.data = rr; + bsc_schedule_timer(&rr->t3122, sec, micro); +} + +static void start_rr_t3124(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T3124 with %d.%03d seconds\n", sec, + micro / 1000); + rr->t3124.cb = timeout_rr_t3124; + rr->t3124.data = rr; + bsc_schedule_timer(&rr->t3124, sec, micro); +} + +static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T3126 with %d.%03d seconds\n", sec, + micro / 1000); + rr->t3126.cb = timeout_rr_t3126; + rr->t3126.data = rr; + bsc_schedule_timer(&rr->t3126, sec, micro); +} + +static void stop_rr_t_meas(struct gsm48_rrlayer *rr) +{ + if (bsc_timer_pending(&rr->t_meas)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T_meas\n"); + bsc_del_timer(&rr->t_meas); + } +} + +static void stop_rr_t_starting(struct gsm48_rrlayer *rr) +{ + if (bsc_timer_pending(&rr->t_starting)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T_starting\n"); + bsc_del_timer(&rr->t_starting); + } +} + +static void stop_rr_t_rel_wait(struct gsm48_rrlayer *rr) +{ + if (bsc_timer_pending(&rr->t_rel_wait)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T_rel_wait\n"); + bsc_del_timer(&rr->t_rel_wait); + } +} + +static void stop_rr_t3110(struct gsm48_rrlayer *rr) +{ + if (bsc_timer_pending(&rr->t3110)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T3110\n"); + bsc_del_timer(&rr->t3110); + } +} + +static void stop_rr_t3122(struct gsm48_rrlayer *rr) +{ + if (bsc_timer_pending(&rr->t3122)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T3122\n"); + bsc_del_timer(&rr->t3122); + } +} + +static void stop_rr_t3124(struct gsm48_rrlayer *rr) +{ + if (bsc_timer_pending(&rr->t3124)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T3124\n"); + bsc_del_timer(&rr->t3124); + } +} + +static void stop_rr_t3126(struct gsm48_rrlayer *rr) +{ + if (bsc_timer_pending(&rr->t3126)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T3126\n"); + bsc_del_timer(&rr->t3126); + } +} + +/* + * status + */ + +/* send rr status request */ +static int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_rr_status *st; + + LOGP(DRR, LOGL_INFO, "RR STATUS (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + st = (struct gsm48_rr_status *) msgb_put(nmsg, sizeof(*st)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL; + + /* rr cause */ + st->rr_cause = cause; + + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg); +} + +/* + * ciphering + */ + +/* send chiperhing mode complete */ +static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr) +{ + struct gsm_settings *set = &ms->settings; + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_rr_hdr *nrrh; + uint8_t buf[11], *tlv; + + LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMPLETE (cr %d)\n", cr); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL; + + /* MI */ + if (cr) { + gsm48_generate_mid_from_imsi(buf, set->imeisv); + /* alter MI type */ + buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | GSM_MI_TYPE_IMEISV; + tlv = msgb_put(nmsg, 2 + buf[1]); + memcpy(tlv, buf, 2 + buf[1]); + } + + gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg); + + /* send RR_SYNC_IND(ciphering) */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_SYNC_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = RR_SYNC_CAUSE_CIPHERING; + return gsm48_rr_upmsg(ms, nmsg); +} + +/* receive ciphering mode command */ +static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_settings *set = &ms->settings; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_cip_mode_cmd *cm = (struct gsm48_cip_mode_cmd *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm); + uint8_t sc, alg_id, cr; + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of CIPHERING MODE COMMAND " + "message.\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + + /* cipher mode setting */ + sc = cm->sc; + alg_id = cm->alg_id; + /* cipher mode response */ + cr = cm->cr; + + if (!sc) + LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, cr=%u)\n", + sc, cr); + else + LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, " + "algo=A5/%d cr=%u)\n", sc, alg_id + 1, cr); + + /* 3.4.7.2 */ + if (rr->cipher_on && sc) { + LOGP(DRR, LOGL_NOTICE, "chiphering already applied\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + + /* check if we actually support this cipher */ + if (sc && ((alg_id == GSM_CIPHER_A5_1 && !set->a5_1) + || (alg_id == GSM_CIPHER_A5_2 && !set->a5_2) + || (alg_id == GSM_CIPHER_A5_3 && !set->a5_3) + || (alg_id == GSM_CIPHER_A5_4 && !set->a5_4) + || (alg_id == GSM_CIPHER_A5_5 && !set->a5_5) + || (alg_id == GSM_CIPHER_A5_6 && !set->a5_6) + || (alg_id == GSM_CIPHER_A5_7 && !set->a5_7))) { + LOGP(DRR, LOGL_NOTICE, "algo not supported\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + + /* check if we have no key */ + if (sc && subscr->key_seq == 7) { + LOGP(DRR, LOGL_NOTICE, "no key available\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + + /* change to ciphering */ + rr->cipher_on = sc; + rr->cipher_type = alg_id; + if (rr->cipher_on) + l1ctl_tx_crypto_req(ms, rr->cipher_type + 1, subscr->key, 8); + else + l1ctl_tx_crypto_req(ms, 0, NULL, 0); + + /* response (using the new mode) */ + return gsm48_rr_tx_cip_mode_cpl(ms, cr); +} + +/* + * classmark + */ + +/* Encode "Classmark 3" (10.5.1.7) */ +static int gsm48_rr_enc_cm3(struct osmocom_ms *ms, uint8_t *buf, uint8_t *len) +{ + struct gsm_support *sup = &ms->support; + struct gsm_settings *set = &ms->settings; + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data = buf; + bv.data_len = 12; + + /* spare bit */ + bitvec_set_bit(&bv, 0); + /* band 3 supported */ + if (set->dcs) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + /* band 2 supported */ + if (set->e_gsm || set->r_gsm) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + /* band 1 supported */ + if (set->p_gsm && !(set->e_gsm || set->r_gsm)) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + /* a5 bits */ + if (set->a5_7) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + if (set->a5_6) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + if (set->a5_5) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + if (set->a5_4) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + /* radio capability */ + if (set->dcs && !set->p_gsm && !(set->e_gsm || set->r_gsm)) { + /* dcs only */ + bitvec_set_uint(&bv, 0, 4); + bitvec_set_uint(&bv, set->class_dcs, 4); + } else + if (set->dcs && (set->p_gsm || (set->e_gsm || set->r_gsm))) { + /* dcs */ + bitvec_set_uint(&bv, set->class_dcs, 4); + /* low band */ + bitvec_set_uint(&bv, set->class_900, 4); + } else { + /* low band only */ + bitvec_set_uint(&bv, 0, 4); + bitvec_set_uint(&bv, set->class_900, 4); + } + /* r support */ + if (set->r_gsm) { + bitvec_set_bit(&bv, ONE); + bitvec_set_uint(&bv, set->class_900, 3); + } else { + bitvec_set_bit(&bv, ZERO); + } + /* multi slot support */ + if (sup->ms_sup) { + bitvec_set_bit(&bv, ONE); + bitvec_set_uint(&bv, sup->ms_sup, 5); + } else { + bitvec_set_bit(&bv, ZERO); + } + /* ucs2 treatment */ + if (sup->ucs2_treat) { + bitvec_set_bit(&bv, ONE); + } else { + bitvec_set_bit(&bv, ZERO); + } + /* support extended measurements */ + if (sup->ext_meas) { + bitvec_set_bit(&bv, ONE); + } else { + bitvec_set_bit(&bv, ZERO); + } + /* support measurement capability */ + if (sup->meas_cap) { + bitvec_set_bit(&bv, ONE); + bitvec_set_uint(&bv, sup->sms_val, 4); + bitvec_set_uint(&bv, sup->sm_val, 4); + } else { + bitvec_set_bit(&bv, ZERO); + } + /* positioning method capability */ + if (sup->loc_serv) { + bitvec_set_bit(&bv, ONE); + bitvec_set_bit(&bv, sup->e_otd_ass == 1); + bitvec_set_bit(&bv, sup->e_otd_based == 1); + bitvec_set_bit(&bv, sup->gps_ass == 1); + bitvec_set_bit(&bv, sup->gps_based == 1); + bitvec_set_bit(&bv, sup->gps_conv == 1); + } else { + bitvec_set_bit(&bv, ZERO); + } + + /* partitial bytes will be completed */ + *len = (bv.cur_bit + 7) >> 3; + bitvec_spare_padding(&bv, (*len * 8) - 1); + + return 0; +} + +/* encode classmark 2 */ +int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_support *sup = &ms->support; + struct gsm_settings *set = &ms->settings; + + if (rr->cd_now.arfcn >= 512 && rr->cd_now.arfcn <= 885) + cm->pwr_lev = set->class_dcs - 1; + else + cm->pwr_lev = set->class_900 - 1; + cm->a5_1 = !set->a5_1; + cm->es_ind = sup->es_ind; + cm->rev_lev = sup->rev_lev; + cm->fc = (set->r_gsm || set->e_gsm); + cm->vgcs = sup->vgcs; + cm->vbs = sup->vbs; + cm->sm_cap = set->sms_ptp; + cm->ss_scr = sup->ss_ind; + cm->ps_cap = sup->ps_cap; + cm->a5_2 = set->a5_2; + cm->a5_3 = set->a5_3; + cm->cmsp = sup->cmsp; + cm->solsa = sup->solsa; + cm->lcsva_cap = sup->lcsva; + + return 0; +} + +/* send classmark change */ +static int gsm48_rr_tx_cm_change(struct osmocom_ms *ms) +{ + struct gsm_support *sup = &ms->support; + struct gsm_settings *set = &ms->settings; + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_cm_change *cc; + uint8_t cm3[14], *tlv; + + LOGP(DRR, LOGL_INFO, "CLASSMARK CHANGE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + cc = (struct gsm48_cm_change *) msgb_put(nmsg, sizeof(*cc)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CLSM_CHG; + + /* classmark 2 */ + cc->cm2_len = sizeof(cc->cm2); + gsm48_rr_enc_cm2(ms, &cc->cm2); + + /* classmark 3 */ + if (set->dcs || set->e_gsm || set->r_gsm + || set->a5_7 || set->a5_6 || set->a5_5 || set->a5_4 + || sup->ms_sup + || sup->ucs2_treat + || sup->ext_meas || sup->meas_cap + || sup->loc_serv) { + cc->cm2.cm3 = 1; + cm3[0] = GSM48_IE_CLASSMARK3; + gsm48_rr_enc_cm3(ms, cm3 + 2, &cm3[1]); + tlv = msgb_put(nmsg, 2 + cm3[1]); + memcpy(tlv, cm3, 2 + cm3[1]); + } + + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg); +} + +/* receiving classmark enquiry */ +static int gsm48_rr_rx_cm_enq(struct osmocom_ms *ms, struct msgb *msg) +{ + /* send classmark */ + return gsm48_rr_tx_cm_change(ms); +} + +/* + * random access + */ + +/* start random access */ +static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging) +{ + struct gsm_settings *set = &ms->settings; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + uint8_t chan_req_val, chan_req_mask; + int rc; + + LOGP(DSUM, LOGL_INFO, "Establish radio link due to %s request\n", + (paging) ? "paging" : "mobility management"); + + /* ignore paging, if not camping */ + if (paging + && (!cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL))) { + LOGP(DRR, LOGL_INFO, "Paging, but not camping, ignore.\n"); + return -EINVAL; + } + + /* tell cell selection process to leave idle mode + * NOTE: this must be sent unbuffered, because the state may not + * change until idle mode is left + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_LEAVE_IDLE); + if (!nmsg) + return -ENOMEM; + rc = gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + if (rc) { + if (paging) + return rc; + LOGP(DRR, LOGL_INFO, "Failed to leave IDLE mode.\n"); + goto undefined; + } + + /* 3.3.1.1.2 */ + new_rr_state(rr, GSM48_RR_ST_CONN_PEND); + + /* set assignment state */ + rr->wait_assign = 0; + + /* number of retransmissions (with first transmission) */ + rr->n_chan_req = s->max_retrans + 1; + + /* generate CHAN REQ (9.1.8) */ + switch (cause) { + case RR_EST_CAUSE_EMERGENCY: + /* 101xxxxx */ + chan_req_mask = 0x1f; + chan_req_val = 0xa0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Emergency call)\n", + chan_req_val); + break; + case RR_EST_CAUSE_REESTAB_TCH_F: + chan_req_mask = 0x1f; + chan_req_val = 0xc0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (re-establish " + "TCH/F)\n", chan_req_val); + break; + case RR_EST_CAUSE_REESTAB_TCH_H: + if (s->neci) { + chan_req_mask = 0x03; + chan_req_val = 0x68; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " + "(re-establish TCH/H with NECI)\n", + chan_req_val); + } else { + chan_req_mask = 0x1f; + chan_req_val = 0xc0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " + "(re-establish TCH/H no NECI)\n", chan_req_val); + } + break; + case RR_EST_CAUSE_REESTAB_2_TCH_H: + if (s->neci) { + chan_req_mask = 0x03; + chan_req_val = 0x6c; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " + "(re-establish TCH/H+TCH/H with NECI)\n", + chan_req_val); + } else { + chan_req_mask = 0x1f; + chan_req_val = 0xc0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " + "(re-establish TCH/H+TCH/H no NECI)\n", + chan_req_val); + } + break; + case RR_EST_CAUSE_ANS_PAG_ANY: + chan_req_mask = 0x1f; + chan_req_val = 0x80; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING " + "Any channel)\n", chan_req_val); + break; + case RR_EST_CAUSE_ANS_PAG_SDCCH: + chan_req_mask = 0x0f; + chan_req_val = 0x10; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING SDCCH)\n", + chan_req_val); + break; + case RR_EST_CAUSE_ANS_PAG_TCH_F: + switch (set->ch_cap) { + case GSM_CAP_SDCCH: + chan_req_mask = 0x0f; + chan_req_val = 0x10; + break; + case GSM_CAP_SDCCH_TCHF: + chan_req_mask = 0x1f; + chan_req_val = 0x80; + break; + default: + chan_req_mask = 0x0f; + chan_req_val = 0x20; + break; + } + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/F)\n", + chan_req_val); + break; + case RR_EST_CAUSE_ANS_PAG_TCH_ANY: + switch (set->ch_cap) { + case GSM_CAP_SDCCH: + chan_req_mask = 0x0f; + chan_req_val = 0x10; + break; + case GSM_CAP_SDCCH_TCHF: + chan_req_mask = 0x1f; + chan_req_val = 0x80; + break; + default: + chan_req_mask = 0x0f; + chan_req_val = 0x30; + break; + } + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/H or " + "TCH/F)\n", chan_req_val); + break; + case RR_EST_CAUSE_ORIG_TCHF: + /* ms supports no dual rate */ + chan_req_mask = 0x1f; + chan_req_val = 0xe0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Orig TCH/F)\n", + chan_req_val); + break; + case RR_EST_CAUSE_LOC_UPD: + if (s->neci) { + chan_req_mask = 0x0f; + chan_req_val = 0x00; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Location " + "Update with NECI)\n", chan_req_val); + } else { + chan_req_mask = 0x1f; + chan_req_val = 0x00; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Location " + "Update no NECI)\n", chan_req_val); + } + break; + case RR_EST_CAUSE_OTHER_SDCCH: + if (s->neci) { + chan_req_mask = 0x0f; + chan_req_val = 0x10; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OHTER " + "with NECI)\n", chan_req_val); + } else { + chan_req_mask = 0x1f; + chan_req_val = 0xe0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OTHER " + "no NECI)\n", chan_req_val); + } + break; + default: + if (!rr->rr_est_req) /* no request from MM */ + return -EINVAL; + + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: with unknown " + "establishment cause: %d\n", cause); + undefined: + LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n"); + + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = RR_REL_CAUSE_UNDEFINED; + gsm48_rr_upmsg(ms, nmsg); + new_rr_state(rr, GSM48_RR_ST_IDLE); + return -EINVAL; + } + + /* store value, mask and history */ + rr->chan_req_val = chan_req_val; + rr->chan_req_mask = chan_req_mask; + rr->cr_hist[2].valid = 0; + rr->cr_hist[1].valid = 0; + rr->cr_hist[0].valid = 0; + + /* store establishment cause, so 'choose cell' selects the last cell + * after location updating */ + rr->est_cause = cause; + + /* if channel is already active somehow */ + if (cs->ccch_state == GSM322_CCCH_ST_DATA) + return gsm48_rr_tx_rand_acc(ms, NULL); + + return 0; +} + +/* send first/next channel request in conn pend state */ +int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + struct gsm_settings *set = &ms->settings; + struct msgb *nmsg; + struct abis_rsl_cchan_hdr *ncch; + int slots; + uint8_t chan_req; + uint8_t tx_power; + + /* already assigned */ + if (rr->wait_assign == 2) + return 0; + + /* store frame number */ + if (msg) { + struct abis_rsl_cchan_hdr *ch = msgb_l2(msg); + struct gsm48_req_ref *ref = + (struct gsm48_req_ref *) (ch->data + 1); + + if (msgb_l2len(msg) < sizeof(*ch) + sizeof(*ref)) { + LOGP(DRR, LOGL_ERROR, "CHAN_CNF too slort\n"); + return -EINVAL; + } + + /* shift history and store */ + memcpy(&(rr->cr_hist[2]), &(rr->cr_hist[1]), + sizeof(struct gsm48_cr_hist)); + memcpy(&(rr->cr_hist[1]), &(rr->cr_hist[0]), + sizeof(struct gsm48_cr_hist)); + rr->cr_hist[0].valid = 1; + rr->cr_hist[0].ref.ra = rr->cr_ra; + rr->cr_hist[0].ref.t1 = ref->t1; + rr->cr_hist[0].ref.t2 = ref->t2; + rr->cr_hist[0].ref.t3_low = ref->t3_low; + rr->cr_hist[0].ref.t3_high = ref->t3_high; + } + + if (cs->ccch_state != GSM322_CCCH_ST_DATA) { + LOGP(DRR, LOGL_INFO, "CCCH channel activation failed.\n"); +fail: + if (rr->rr_est_req) { + struct msgb *msg = + gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + struct gsm48_rr_hdr *rrh; + + LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n"); + if (!msg) + return -ENOMEM; + rrh = (struct gsm48_rr_hdr *)msg->data; + rrh->cause = RR_REL_CAUSE_RA_FAILURE; + gsm48_rr_upmsg(ms, msg); + } + + new_rr_state(rr, GSM48_RR_ST_IDLE); + + return 0; + } + + if (!s || !s->si3 || !s->tx_integer) { + LOGP(DRR, LOGL_NOTICE, "Not enough SYSINFO\n"); + goto fail; + } + + if (rr->state == GSM48_RR_ST_IDLE) { + LOGP(DRR, LOGL_INFO, "MM already released RR.\n"); + + return 0; + } + + LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (requests left %d)\n", + rr->n_chan_req); + + if (!rr->n_chan_req) { + LOGP(DRR, LOGL_INFO, "Done with sending RANDOM ACCESS " + "bursts\n"); + if (!bsc_timer_pending(&rr->t3126)) + start_rr_t3126(rr, 5, 0); /* TODO improve! */ + return 0; + } + rr->n_chan_req--; + + if (rr->wait_assign == 0) { + /* first random acces, without delay of slots */ + slots = 0; + rr->wait_assign = 1; + } else { + /* subsequent random acces, with slots from table 3.1 */ + switch(s->tx_integer) { + case 3: case 8: case 14: case 50: + if (s->ccch_conf != 1) /* not combined CCCH */ + slots = 55; + else + slots = 41; + break; + case 4: case 9: case 16: + if (s->ccch_conf != 1) + slots = 76; + else + slots = 52; + break; + case 5: case 10: case 20: + if (s->ccch_conf != 1) + slots = 109; + else + slots = 58; + break; + case 6: case 11: case 25: + if (s->ccch_conf != 1) + slots = 163; + else + slots = 86; + break; + default: + if (s->ccch_conf != 1) + slots = 217; + else + slots = 115; + break; + } + } + + chan_req = random(); + chan_req &= rr->chan_req_mask; + chan_req |= rr->chan_req_val; + + LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (Tx-integer %d combined %s " + "S(lots) %d ra 0x%02x)\n", s->tx_integer, + (s->ccch_conf == 1) ? "yes": "no", slots, chan_req); + + slots = (random() % s->tx_integer) + slots; + + /* (re)send CHANNEL RQD with new randiom */ + nmsg = gsm48_rsl_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ncch = (struct abis_rsl_cchan_hdr *) msgb_put(nmsg, sizeof(*ncch) + + 4 + 2 + 2); + rsl_init_cchan_hdr(ncch, RSL_MT_CHAN_RQD); + ncch->chan_nr = RSL_CHAN_RACH; + ncch->data[0] = RSL_IE_REQ_REFERENCE; + ncch->data[1] = chan_req; + ncch->data[2] = (slots >> 8) | ((s->ccch_conf == 1) << 7); + ncch->data[3] = slots; + ncch->data[4] = RSL_IE_ACCESS_DELAY; + ncch->data[5] = set->alter_delay; /* (-)=earlier (+)=later */ + ncch->data[6] = RSL_IE_MS_POWER; + if (set->alter_tx_power) { + tx_power = set->alter_tx_power_value; + LOGP(DRR, LOGL_INFO, "Use alternative tx-power %d (%d dBm)\n", + tx_power, + ms_pwr_dbm(gsm_arfcn2band(cs->arfcn), tx_power)); + } else { + tx_power = s->ms_txpwr_max_cch; + /* power offset in case of DCS1800 */ + if (s->po && cs->arfcn >= 512 && cs->arfcn <= 885) { + LOGP(DRR, LOGL_INFO, "Use MS-TXPWR-MAX-CCH power value " + "%d (%d dBm) with offset %d dBm\n", tx_power, + ms_pwr_dbm(gsm_arfcn2band(cs->arfcn), tx_power), + s->po_value * 2); + /* use reserved bits 7,8 for offset (+ X * 2dB) */ + tx_power |= s->po_value << 6; + } else + LOGP(DRR, LOGL_INFO, "Use MS-TXPWR-MAX-CCH power value " + "%d (%d dBm)\n", tx_power, + ms_pwr_dbm(gsm_arfcn2band(cs->arfcn), + tx_power)); + } + ncch->data[7] = tx_power; + + /* set initial indications */ + rr->cd_now.ind_tx_power = s->ms_txpwr_max_cch; + rr->cd_now.ind_ta = set->alter_delay; + + /* store ra until confirmed, then copy it with time into cr_hist */ + rr->cr_ra = chan_req; + + return rslms_recvmsg(nmsg, ms); +} + +/* + * system information + */ + +/* send sysinfo event to other layers */ +static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type) +{ + struct gsm48_sysinfo *s = ms->cellsel.si; + struct msgb *nmsg; + struct gsm322_msg *em; + + /* update list of measurements, if BA(SACCH) is complete and new */ + if (s + && (type == GSM48_MT_RR_SYSINFO_5 + || type == GSM48_MT_RR_SYSINFO_5bis + || type == GSM48_MT_RR_SYSINFO_5ter) + && s->si5 + && (!s->nb_ext_ind_si5 || s->si5bis)) { + struct gsm48_rr_meas *rrmeas = &ms->rrlayer.meas; + int n = 0, i; + + LOGP(DRR, LOGL_NOTICE, "Complete set of SI5* for BA(%d)\n", + s->nb_ba_ind_si5); + rrmeas->nc_num = 0; + for (i = 1; i <= 1024; i++) { + if ((s->freq[i & 1023].mask & FREQ_TYPE_REP)) { + if (n == 32) { + LOGP(DRR, LOGL_NOTICE, "SI5* report " + "exceeds 32 BCCHs\n"); + break; + } + LOGP(DRR, LOGL_NOTICE, "SI5* report arfcn %d\n", + i & 1023); + rrmeas->nc_arfcn[n] = i & 1023; + rrmeas->nc_rxlev[n] = -128; + n++; + } + } + rrmeas->nc_num = n; + } + + /* send sysinfo event to other layers */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SYSINFO); + if (!nmsg) + return -ENOMEM; + em = (struct gsm322_msg *) nmsg->data; + em->sysinfo = type; + gsm322_cs_sendmsg(ms, nmsg); + + /* send timer info to location update process */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_SYSINFO); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + return 0; +} + +/* receive "SYSTEM INFORMATION 1" message (9.1.31) */ +static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_1 *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 1 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 1 " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si1_msg, MIN(msgb_l3len(msg), sizeof(s->si1_msg)))) + return 0; + + gsm48_decode_sysinfo1(s, si, msgb_l3len(msg)); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 1\n"); + + return gsm48_new_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 2" message (9.1.32) */ +static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_2 *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2 " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si2_msg, MIN(msgb_l3len(msg), sizeof(s->si2_msg)))) + return 0; + + gsm48_decode_sysinfo2(s, si, msgb_l3len(msg)); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2\n"); + + return gsm48_new_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 2bis" message (9.1.33) */ +static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_2bis *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2bis" + " ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2bis " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si2b_msg, MIN(msgb_l3len(msg), sizeof(s->si2b_msg)))) + return 0; + + gsm48_decode_sysinfo2bis(s, si, msgb_l3len(msg)); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2bis\n"); + + return gsm48_new_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 2ter" message (9.1.34) */ +static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_2ter *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2ter" + " ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2ter " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si2t_msg, MIN(msgb_l3len(msg), sizeof(s->si2t_msg)))) + return 0; + + gsm48_decode_sysinfo2ter(s, si, msgb_l3len(msg)); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2ter\n"); + + return gsm48_new_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 3" message (9.1.35) */ +static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_3 *si = msgb_l3(msg); + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 3 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 3 " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si3_msg, MIN(msgb_l3len(msg), sizeof(s->si3_msg)))) + return 0; + + gsm48_decode_sysinfo3(s, si, msgb_l3len(msg)); + + if (cs->ccch_mode == CCCH_MODE_NONE) { + cs->ccch_mode = (s->ccch_conf == 1) ? CCCH_MODE_COMBINED : + CCCH_MODE_NON_COMBINED; + LOGP(DRR, LOGL_NOTICE, "Changing CCCH_MODE to %d\n", + cs->ccch_mode); + l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode); + } + + return gsm48_new_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 4" message (9.1.36) */ +static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_4 *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 4 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 4 " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si4_msg, MIN(msgb_l3len(msg), sizeof(s->si4_msg)))) + return 0; + + gsm48_decode_sysinfo4(s, si, msgb_l3len(msg)); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (mcc %s mnc %s " + "lac 0x%04x)\n", gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); + + return gsm48_new_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 5" message (9.1.37) */ +static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5 *si = msgb_l3(msg) + 1; + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5 " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si5_msg, MIN(msgb_l3len(msg), sizeof(s->si5_msg)))) + return 0; + + gsm48_decode_sysinfo5(s, si, msgb_l3len(msg)); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5\n"); + + return gsm48_new_sysinfo(ms, si->system_information); +} + +/* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */ +static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5bis *si = msgb_l3(msg) + 1; + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5bis" + " ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5bis " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si5b_msg, MIN(msgb_l3len(msg), + sizeof(s->si5b_msg)))) + return 0; + + gsm48_decode_sysinfo5bis(s, si, msgb_l3len(msg)); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5bis\n"); + + return gsm48_new_sysinfo(ms, si->system_information); +} + +/* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */ +static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5ter *si = msgb_l3(msg) + 1; + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5ter" + " ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5ter " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si5t_msg, MIN(msgb_l3len(msg), + sizeof(s->si5t_msg)))) + return 0; + + gsm48_decode_sysinfo5ter(s, si, msgb_l3len(msg)); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5ter\n"); + + return gsm48_new_sysinfo(ms, si->system_information); +} + +/* receive "SYSTEM INFORMATION 6" message (9.1.39) */ +static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_6 *si = msgb_l3(msg) + 1; + struct gsm48_sysinfo *s = ms->cellsel.si; + struct rx_meas_stat *meas = &ms->meas; + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 6 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 6 " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si6_msg, MIN(msgb_l3len(msg), sizeof(s->si6_msg)))) + return 0; + + gsm48_decode_sysinfo6(s, si, msgb_l3len(msg)); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (mcc %s mnc %s " + "lac 0x%04x SACCH-timeout %d)\n", gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac, s->sacch_radio_link_timeout); + + meas->rl_fail = meas->s = s->sacch_radio_link_timeout; + LOGP(DRR, LOGL_INFO, "using (new) SACCH timeout %d\n", meas->rl_fail); + + return gsm48_new_sysinfo(ms, si->system_information); +} + +/* + * paging + */ + +/* paging channel request */ +static int gsm48_rr_chan2cause[4] = { + RR_EST_CAUSE_ANS_PAG_ANY, + RR_EST_CAUSE_ANS_PAG_SDCCH, + RR_EST_CAUSE_ANS_PAG_TCH_F, + RR_EST_CAUSE_ANS_PAG_TCH_ANY +}; + +/* given LV of mobile identity is checked agains ms */ +static int gsm_match_mi(struct osmocom_ms *ms, uint8_t *mi) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + char imsi[16]; + uint32_t tmsi; + uint8_t mi_type; + + if (mi[0] < 1) + return 0; + mi_type = mi[1] & GSM_MI_TYPE_MASK; + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + if (mi[0] < 5) + return 0; + memcpy(&tmsi, mi+2, 4); + if (ms->subscr.tmsi == ntohl(tmsi) + && ms->subscr.mcc == cs->sel_mcc + && ms->subscr.mnc == cs->sel_mnc + && ms->subscr.lac == cs->sel_lac) { + LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", + ntohl(tmsi)); + + return 1; + } else + LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", + ntohl(tmsi)); + break; + case GSM_MI_TYPE_IMSI: + gsm48_mi_to_string(imsi, sizeof(imsi), mi + 1, mi[0]); + if (!strcmp(imsi, ms->subscr.imsi)) { + LOGP(DPAG, LOGL_INFO, " IMSI %s matches\n", imsi); + + return 1; + } else + LOGP(DPAG, LOGL_INFO, " IMSI %s (not for us)\n", imsi); + break; + default: + LOGP(DPAG, LOGL_NOTICE, "Paging with unsupported MI type %d.\n", + mi_type); + } + + return 0; +} + +/* 9.1.22 PAGING REQUEST 1 message received */ +static int gsm48_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_paging1 *pa = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*pa); + int chan_1, chan_2; + uint8_t *mi; + + /* empty paging request */ + if (payload_len >= 2 && (pa->data[1] & GSM_MI_TYPE_MASK) == 0) + return 0; + + /* 3.3.1.1.2: ignore paging while not camping on a cell */ + if (rr->state != GSM48_RR_ST_IDLE || !cs->selected + || (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL)) { + LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); + + return 0; + } + LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 1\n"); + + if (payload_len < 2) { + short_read: + LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 1 " + "message.\n"); + + return -EINVAL; + } + + /* channel needed */ + chan_1 = pa->cneed1; + chan_2 = pa->cneed2; + /* first MI */ + mi = pa->data; + if (payload_len < mi[0] + 1) + goto short_read; + if (gsm_match_mi(ms, mi) > 0) + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1); + /* second MI */ + payload_len -= mi[0] + 1; + mi = pa->data + mi[0] + 1; + if (payload_len < 2) + return 0; + if (mi[0] != GSM48_IE_MOBILE_ID) + return 0; + if (payload_len < mi[1] + 2) + goto short_read; + if (gsm_match_mi(ms, mi + 1) > 0) + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1); + + return 0; +} + +/* 9.1.23 PAGING REQUEST 2 message received */ +static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_paging2 *pa = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*pa); + uint8_t *mi; + int chan_1, chan_2, chan_3; + + /* 3.3.1.1.2: ignore paging while not camping on a cell */ + if (rr->state != GSM48_RR_ST_IDLE || !cs->selected + || (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL)) { + LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); + + return 0; + } + LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 2\n"); + + if (payload_len < 0) { + short_read: + LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 2 " + "message .\n"); + + return -EINVAL; + } + + /* channel needed */ + chan_1 = pa->cneed1; + chan_2 = pa->cneed2; + /* first MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi1) + && ms->subscr.mcc == cs->sel_mcc + && ms->subscr.mnc == cs->sel_mnc + && ms->subscr.lac == cs->sel_lac) { + LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1); + } else + LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", + ntohl(pa->tmsi1)); + /* second MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi2) + && ms->subscr.mcc == cs->sel_mcc + && ms->subscr.mnc == cs->sel_mnc + && ms->subscr.lac == cs->sel_lac) { + LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1); + } else + LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", + ntohl(pa->tmsi2)); + /* third MI */ + mi = pa->data; + if (payload_len < 2) + return 0; + if (mi[0] != GSM48_IE_MOBILE_ID) + return 0; + if (payload_len < mi[1] + 2 + 1) /* must include "channel needed" */ + goto short_read; + chan_3 = mi[mi[1] + 2] & 0x03; /* channel needed */ + if (gsm_match_mi(ms, mi + 1) > 0) + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1); + + return 0; +} + +/* 9.1.24 PAGING REQUEST 3 message received */ +static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_paging3 *pa = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*pa); + int chan_1, chan_2, chan_3, chan_4; + + /* 3.3.1.1.2: ignore paging while not camping on a cell */ + if (rr->state != GSM48_RR_ST_IDLE || !cs->selected + || (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL)) { + LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); + + return 0; + } + LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 3\n"); + + if (payload_len < 0) { /* must include "channel needed", part of *pa */ + LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 3 " + "message .\n"); + + return -EINVAL; + } + + /* channel needed */ + chan_1 = pa->cneed1; + chan_2 = pa->cneed2; + chan_3 = pa->cneed3; + chan_4 = pa->cneed4; + /* first MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi1) + && ms->subscr.mcc == cs->sel_mcc + && ms->subscr.mnc == cs->sel_mnc + && ms->subscr.lac == cs->sel_lac) { + LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1); + } else + LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", + ntohl(pa->tmsi1)); + /* second MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi2) + && ms->subscr.mcc == cs->sel_mcc + && ms->subscr.mnc == cs->sel_mnc + && ms->subscr.lac == cs->sel_lac) { + LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1); + } else + LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", + ntohl(pa->tmsi2)); + /* thrid MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi3) + && ms->subscr.mcc == cs->sel_mcc + && ms->subscr.mnc == cs->sel_mnc + && ms->subscr.lac == cs->sel_lac) { + LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi3)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1); + } else + LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", + ntohl(pa->tmsi3)); + /* fourth MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi4) + && ms->subscr.mcc == cs->sel_mcc + && ms->subscr.mnc == cs->sel_mnc + && ms->subscr.lac == cs->sel_lac) { + LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi4)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_4], 1); + } else + LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", + ntohl(pa->tmsi4)); + + return 0; +} + +/* + * (immediate) assignment + */ + +/* match request reference agains request history */ +static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + int i; + uint8_t ia_t1, ia_t2, ia_t3; + uint8_t cr_t1, cr_t2, cr_t3; + + for (i = 0; i < 3; i++) { + /* filter confirmed RACH requests only */ + if (rr->cr_hist[i].valid && ref->ra == rr->cr_hist[i].ref.ra) { + ia_t1 = ref->t1; + ia_t2 = ref->t2; + ia_t3 = (ref->t3_high << 3) | ref->t3_low; + ref = &rr->cr_hist[i].ref; + cr_t1 = ref->t1; + cr_t2 = ref->t2; + cr_t3 = (ref->t3_high << 3) | ref->t3_low; + if (ia_t1 == cr_t1 && ia_t2 == cr_t2 + && ia_t3 == cr_t3) { + LOGP(DRR, LOGL_INFO, "request %02x matches " + "(fn=%d,%d,%d)\n", ref->ra, ia_t1, + ia_t2, ia_t3); + return 1; + } else + LOGP(DRR, LOGL_INFO, "request %02x matches " + "but not frame number (IMM.ASS " + "fn=%d,%d,%d != RACH fn=%d,%d,%d)\n", + ref->ra, ia_t1, ia_t2, ia_t3, + cr_t1, cr_t2, cr_t3); + } + } + + return 0; +} + +/* 9.1.18 IMMEDIATE ASSIGNMENT is received */ +static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_imm_ass *ia = msgb_l3(msg); + int ma_len = msgb_l3len(msg) - sizeof(*ia); + uint8_t ch_type, ch_subch, ch_ts; + struct gsm48_rr_cd cd; +#ifndef TEST_STARTING_TIMER + uint8_t *st, st_len; +#endif + + memset(&cd, 0, sizeof(cd)); + cd.ind_tx_power = rr->cd_now.ind_tx_power; + + if (ma_len < 0 /* mobile allocation IE must be included */ + || ia->mob_alloc_len > ma_len) { /* short read of IE */ + LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT " + "message.\n"); + return -EINVAL; + } + if (ia->mob_alloc_len > 8) { + LOGP(DRR, LOGL_NOTICE, "Moble allocation in IMMEDIATE " + "ASSIGNMENT too large.\n"); + return -EINVAL; + } + + /* starting time */ +#ifdef TEST_STARTING_TIMER + cd.start = 1; + cd.start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432; + LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n"); +#else + st_len = ma_len - ia->mob_alloc_len; + st = ia->mob_alloc + ia->mob_alloc_len; + if (st_len >= 3 && st[0] == GSM48_IE_START_TIME) + gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1)); +#endif + + /* decode channel description */ + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n"); + cd.chan_nr = ia->chan_desc.chan_nr; + rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ia->chan_desc.h0.h) { + cd.h = 1; + gsm48_decode_chan_h1(&ia->chan_desc, &cd.tsc, &cd.maio, + &cd.hsn); + LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x " + "MAIO %u HSN %u TS %u SS %u TSC %u)\n", + ia->timing_advance, + ia->timing_advance * GSM_TA_CM / 100, + ia->req_ref.ra, ia->chan_desc.chan_nr, cd.maio, + cd.hsn, ch_ts, ch_subch, cd.tsc); + } else { + cd.h = 0; + gsm48_decode_chan_h0(&ia->chan_desc, &cd.tsc, &cd.arfcn); + LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x " + "ARFCN %u TS %u SS %u TSC %u)\n", + ia->timing_advance, + ia->timing_advance * GSM_TA_CM / 100, + ia->req_ref.ra, ia->chan_desc.chan_nr, cd.arfcn, + ch_ts, ch_subch, cd.tsc); + } + + /* 3.3.1.1.2: ignore assignment while idle */ + if (rr->state != GSM48_RR_ST_CONN_PEND || rr->wait_assign == 0) { + LOGP(DRR, LOGL_INFO, "Not for us, no request.\n"); + return 0; + } + + if (rr->wait_assign == 2) { + LOGP(DRR, LOGL_INFO, "Ignoring, channel already assigned.\n"); + return 0; + } + + /* request ref */ + if (gsm48_match_ra(ms, &ia->req_ref)) { + /* channel description */ + memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now)); + /* timing advance */ + rr->cd_now.ind_ta = ia->timing_advance; + /* mobile allocation */ + memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len, + ia->mob_alloc_len + 1); + rr->wait_assign = 2; + /* reset scheduler */ + LOGP(DRR, LOGL_INFO, "resetting scheduler\n"); + l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED); + + return gsm48_rr_dl_est(ms); + } + LOGP(DRR, LOGL_INFO, "Request, but not for us.\n"); + + return 0; +} + +/* 9.1.19 IMMEDIATE ASSIGNMENT EXTENDED is received */ +static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_imm_ass_ext *ia = msgb_l3(msg); + int ma_len = msgb_l3len(msg) - sizeof(*ia); + uint8_t ch_type, ch_subch, ch_ts; + struct gsm48_rr_cd cd1, cd2; +#ifndef TEST_STARTING_TIMER + uint8_t *st, st_len; +#endif + + memset(&cd1, 0, sizeof(cd1)); + cd1.ind_tx_power = rr->cd_now.ind_tx_power; + memset(&cd2, 0, sizeof(cd2)); + cd2.ind_tx_power = rr->cd_now.ind_tx_power; + + if (ma_len < 0 /* mobile allocation IE must be included */ + || ia->mob_alloc_len > ma_len) { /* short read of IE */ + LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT " + "EXTENDED message.\n"); + return -EINVAL; + } + if (ia->mob_alloc_len > 4) { + LOGP(DRR, LOGL_NOTICE, "Moble allocation in IMMEDIATE " + "ASSIGNMENT EXTENDED too large.\n"); + return -EINVAL; + } + +#ifdef TEST_STARTING_TIMER + cd1.start = 1; + cd2.start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432; + memcpy(&cd2, &cd1, sizeof(cd2)); + LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n"); +#else + /* starting time */ + st_len = ma_len - ia->mob_alloc_len; + st = ia->mob_alloc + ia->mob_alloc_len; + if (st_len >= 3 && st[0] == GSM48_IE_START_TIME) { + gsm48_decode_start_time(&cd1, + (struct gsm48_start_time *)(st+1)); + memcpy(&cd2, &cd1, sizeof(cd2)); + } +#endif + + /* decode channel description */ + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT EXTENDED:\n"); + cd1.chan_nr = ia->chan_desc1.chan_nr; + rsl_dec_chan_nr(cd1.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ia->chan_desc1.h0.h) { + cd1.h = 1; + gsm48_decode_chan_h1(&ia->chan_desc1, &cd1.tsc, &cd1.maio, + &cd1.hsn); + LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x " + "chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n", + ia->timing_advance1, + ia->timing_advance1 * GSM_TA_CM / 100, + ia->req_ref1.ra, ia->chan_desc1.chan_nr, cd1.maio, + cd1.hsn, ch_ts, ch_subch, cd1.tsc); + } else { + cd1.h = 0; + gsm48_decode_chan_h0(&ia->chan_desc1, &cd1.tsc, &cd1.arfcn); + LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x " + "chan_nr 0x%02x ARFCN %u TS %u SS %u TSC %u)\n", + ia->timing_advance1, + ia->timing_advance1 * GSM_TA_CM / 100, + ia->req_ref1.ra, ia->chan_desc1.chan_nr, cd1.arfcn, + ch_ts, ch_subch, cd1.tsc); + } + cd2.chan_nr = ia->chan_desc2.chan_nr; + rsl_dec_chan_nr(cd2.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ia->chan_desc2.h0.h) { + cd2.h = 1; + gsm48_decode_chan_h1(&ia->chan_desc2, &cd2.tsc, &cd2.maio, + &cd2.hsn); + LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x " + "chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n", + ia->timing_advance2, + ia->timing_advance2 * GSM_TA_CM / 100, + ia->req_ref2.ra, ia->chan_desc2.chan_nr, cd2.maio, + cd2.hsn, ch_ts, ch_subch, cd2.tsc); + } else { + cd2.h = 0; + gsm48_decode_chan_h0(&ia->chan_desc2, &cd2.tsc, &cd2.arfcn); + LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x " + "chan_nr 0x%02x ARFCN %u TS %u SS %u TSC %u)\n", + ia->timing_advance2, + ia->timing_advance2 * GSM_TA_CM / 100, + ia->req_ref2.ra, ia->chan_desc2.chan_nr, cd2.arfcn, + ch_ts, ch_subch, cd2.tsc); + } + + /* 3.3.1.1.2: ignore assignment while idle */ + if (rr->state != GSM48_RR_ST_CONN_PEND || rr->wait_assign == 0) { + LOGP(DRR, LOGL_INFO, "Not for us, no request.\n"); + return 0; + } + + if (rr->wait_assign == 2) { + LOGP(DRR, LOGL_INFO, "Ignoring, channel already assigned.\n"); + return 0; + } + + /* request ref 1 */ + if (gsm48_match_ra(ms, &ia->req_ref1)) { + /* channel description */ + memcpy(&rr->cd_now, &cd1, sizeof(rr->cd_now)); + /* timing advance */ + rr->cd_now.ind_ta = ia->timing_advance1; + /* mobile allocation */ + memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len, + ia->mob_alloc_len + 1); + rr->wait_assign = 2; + /* reset scheduler */ + LOGP(DRR, LOGL_INFO, "resetting scheduler\n"); + l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED); + + return gsm48_rr_dl_est(ms); + } + /* request ref 2 */ + if (gsm48_match_ra(ms, &ia->req_ref2)) { + /* channel description */ + memcpy(&rr->cd_now, &cd2, sizeof(rr->cd_now)); + /* timing advance */ + rr->cd_now.ind_ta = ia->timing_advance2; + /* mobile allocation */ + memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len, + ia->mob_alloc_len + 1); + rr->wait_assign = 2; + /* reset scheduler */ + LOGP(DRR, LOGL_INFO, "resetting scheduler\n"); + l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED); + + return gsm48_rr_dl_est(ms); + } + LOGP(DRR, LOGL_INFO, "Request, but not for us.\n"); + + return 0; +} + +/* 9.1.20 IMMEDIATE ASSIGNMENT REJECT is received */ +static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_imm_ass_rej *ia = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*ia); + int i; + struct gsm48_req_ref *req_ref; + uint8_t t3122_value; + + /* 3.3.1.1.2: ignore assignment while idle */ + if (rr->state != GSM48_RR_ST_CONN_PEND || rr->wait_assign == 0) + return 0; + + if (rr->wait_assign == 2) { + return 0; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT " + "REJECT message.\n"); + return -EINVAL; + } + + for (i = 0; i < 4; i++) { + /* request reference */ + req_ref = (struct gsm48_req_ref *) + (((uint8_t *)&ia->req_ref1) + i * 4); + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT REJECT " + "(ref 0x%02x)\n", req_ref->ra); + if (gsm48_match_ra(ms, req_ref)) { + /* wait indication */ + t3122_value = *(((uint8_t *)&ia->wait_ind1) + i * 4); + if (t3122_value) + start_rr_t3122(rr, t3122_value, 0); + /* start timer 3126 if not already */ + if (!bsc_timer_pending(&rr->t3126)) + start_rr_t3126(rr, 5, 0); /* TODO improve! */ + /* stop assignmnet requests */ + rr->n_chan_req = 0; + + /* wait until timer 3126 expires, then release + * or wait for channel assignment */ + return 0; + } + } + + return 0; +} + +/* 9.1.1 ADDITIONAL ASSIGMENT is received */ +static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_add_ass *aa = (struct gsm48_add_ass *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*aa); + struct tlv_parsed tp; + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of ADDITIONAL ASSIGNMENT " + "message.\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + tlv_parse(&tp, &gsm48_rr_att_tlvdef, aa->data, payload_len, 0, 0); + + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); +} + +/* + * measturement reports + */ + +static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_sysinfo *s = ms->cellsel.si; + struct rx_meas_stat *meas = &rr->ms->meas; + struct gsm48_rr_meas *rrmeas = &rr->meas; + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_meas_res *mr; + uint8_t serv_rxlev_full = 0, serv_rxlev_sub = 0, serv_rxqual_full = 0, + serv_rxqual_sub = 0; + uint8_t ta, tx_power; + uint8_t rep_ba = 0, rep_valid = 0, meas_valid = 0, multi_rep = 0; + uint8_t n = 0, rxlev_nc[6], bsic_nc[6], bcch_f_nc[6]; + + /* just in case! */ + if (!s) + return -EINVAL; + + /* check if SI5* is completely received, check BA-IND */ + if (s->si5 + && (!s->nb_ext_ind_si5 || s->si5bis)) { + rep_ba = s->nb_ba_ind_si5; + if ((s->si5bis && s->nb_ext_ind_si5 + && s->nb_ba_ind_si5bis != rep_ba) + || (s->si5ter && s->nb_ba_ind_si5ter != rep_ba)) { + LOGP(DRR, LOGL_NOTICE, "BA-IND missmatch on SI5*"); + } else + rep_valid = 1; + } + + /* check for valid measurements, any frame must exist */ + if (meas->frames) { + meas_valid = 1; + serv_rxlev_full = serv_rxlev_sub = meas->rxlev / meas->frames; + serv_rxqual_full = serv_rxqual_sub = 0; // FIXME + } + + memset(&rxlev_nc, 0, sizeof(rxlev_nc)); + memset(&bsic_nc, 0, sizeof(bsic_nc)); + memset(&bcch_f_nc, 0, sizeof(bcch_f_nc)); + if (rep_valid) { + int8_t strongest, current; + uint8_t ncc; + int i, index; + + /* multiband reporting, if not: 0 = normal reporting */ + if (s->si5ter) + multi_rep = s->nb_multi_rep_si5ter; + + /* get 6 strongest measurements */ + // FIXME: multiband report + strongest = 127; /* infinite */ + for (n = 0; n < 6; n++) { + current = -128; /* -infinite */ + index = 0; + for (i = 0; i < rrmeas->nc_num; i++) { + /* only check if NCC is permitted */ + ncc = rrmeas->nc_bsic[i] >> 3; + if ((s->nb_ncc_permitted_si6 & (1 << ncc)) + && rrmeas->nc_rxlev[i] > current + && rrmeas->nc_rxlev[i] < strongest) { + current = rrmeas->nc_rxlev[i]; + index = i; + } + } + if (current == -128) /* no more found */ + break; + rxlev_nc[n] = rrmeas->nc_rxlev[index] + 110; + bsic_nc[n] = rrmeas->nc_bsic[index]; + bcch_f_nc[n] = index; + } + } + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + + /* use indicated tx-power and TA (not the altered ones) */ + tx_power = rr->cd_now.ind_tx_power; + // FIXME: degrade power to the max supported level + ta = rr->cd_now.ind_ta; + + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + mr = (struct gsm48_meas_res *) msgb_put(nmsg, sizeof(*mr)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_MEAS_REP; + + /* measurement results */ + mr->rxlev_full = serv_rxlev_full; + mr->rxlev_sub = serv_rxlev_sub; + mr->rxqual_full = serv_rxqual_full; + mr->rxqual_sub = serv_rxqual_sub; + mr->dtx_used = 0; // FIXME: no DTX yet + mr->ba_used = rep_ba; + mr->meas_valid = !meas_valid; /* 0 = valid */ + if (rep_valid) { + mr->no_nc_n_hi = n >> 2; + mr->no_nc_n_lo = n & 3; + } else { + /* no results for serving cells */ + mr->no_nc_n_hi = 1; + mr->no_nc_n_lo = 3; + } + mr->rxlev_nc1 = rxlev_nc[0]; + mr->rxlev_nc2_hi = rxlev_nc[1] >> 1; + mr->rxlev_nc2_lo = rxlev_nc[1] & 1; + mr->rxlev_nc3_hi = rxlev_nc[2] >> 2; + mr->rxlev_nc3_lo = rxlev_nc[2] & 3; + mr->rxlev_nc4_hi = rxlev_nc[3] >> 3; + mr->rxlev_nc4_lo = rxlev_nc[3] & 7; + mr->rxlev_nc5_hi = rxlev_nc[4] >> 4; + mr->rxlev_nc5_lo = rxlev_nc[4] & 15; + mr->rxlev_nc6_hi = rxlev_nc[5] >> 5; + mr->rxlev_nc6_lo = rxlev_nc[5] & 31; + mr->bsic_nc1_hi = bsic_nc[0] >> 3; + mr->bsic_nc1_lo = bsic_nc[0] & 7; + mr->bsic_nc2_hi = bsic_nc[1] >> 4; + mr->bsic_nc2_lo = bsic_nc[1] & 15; + mr->bsic_nc3_hi = bsic_nc[2] >> 5; + mr->bsic_nc3_lo = bsic_nc[2] & 31; + mr->bsic_nc4 = bsic_nc[3]; + mr->bsic_nc5 = bsic_nc[4]; + mr->bsic_nc6 = bsic_nc[5]; + mr->bcch_f_nc1 = bcch_f_nc[0]; + mr->bcch_f_nc2 = bcch_f_nc[1]; + mr->bcch_f_nc3 = bcch_f_nc[2]; + mr->bcch_f_nc4 = bcch_f_nc[3]; + mr->bcch_f_nc5_hi = bcch_f_nc[4] >> 1; + mr->bcch_f_nc5_lo = bcch_f_nc[4] & 1; + mr->bcch_f_nc6_hi = bcch_f_nc[5] >> 2; + mr->bcch_f_nc6_lo = bcch_f_nc[5] & 3; + + LOGP(DRR, LOGL_INFO, "MEAS REP: pwr=%d TA=%d meas-invalid=%d " + "rxlev-full=%d rxlev-sub=%d rxqual-full=%d rxqual-sub=%d " + "dtx %d ba %d no-ncell-n %d\n", tx_power, ta, mr->meas_valid, + mr->rxlev_full - 110, mr->rxlev_sub - 110, + mr->rxqual_full, mr->rxqual_sub, mr->dtx_used, mr->ba_used, + (mr->no_nc_n_hi << 2) | mr->no_nc_n_lo); + + msgb_tv16_push(nmsg, RSL_IE_L3_INFO, + nmsg->tail - (uint8_t *)msgb_l3(nmsg)); + msgb_push(nmsg, 2 + 2); + nmsg->data[0] = RSL_IE_TIMING_ADVANCE; + nmsg->data[1] = ta; + nmsg->data[2] = RSL_IE_MS_POWER; + nmsg->data[3] = tx_power; + rsl_rll_push_hdr(nmsg, RSL_MT_UNIT_DATA_REQ, rr->cd_now.chan_nr, + 0x40, 1); + + return rslms_recvmsg(nmsg, ms); +} + +/* + * link establishment and release + */ + +/* process "Loss Of Signal" */ +int gsm48_rr_los(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + uint8_t *mode; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + LOGP(DSUM, LOGL_INFO, "Radio link lost signal\n"); + + /* stop T3211 if running */ + stop_rr_t3110(rr); + + switch(rr->state) { + case GSM48_RR_ST_CONN_PEND: + LOGP(DRR, LOGL_INFO, "LOS during RACH request\n"); + + /* stop pending RACH timer */ + stop_rr_t3126(rr); + break; + case GSM48_RR_ST_DEDICATED: + LOGP(DRR, LOGL_INFO, "LOS during dedicated mode, release " + "locally\n"); + + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* release message */ + rel_local: + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = 1; /* local release */ + /* start release */ + return gsm48_send_rsl_rel(ms, RSL_MT_REL_REQ, nmsg); + case GSM48_RR_ST_REL_PEND: + LOGP(DRR, LOGL_INFO, "LOS during RR release procedure, release " + "locally\n"); + + /* stop pending RACH timer */ + stop_rr_t3110(rr); + + /* release locally */ + goto rel_local; + default: + /* this should not happen */ + LOGP(DRR, LOGL_ERROR, "LOS in IDLE state, ignoring\n"); + return -EINVAL; + } + + /* send inication to upper layer */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = RR_REL_CAUSE_LOST_SIGNAL; + gsm48_rr_upmsg(ms, nmsg); + + /* return idle */ + new_rr_state(rr, GSM48_RR_ST_IDLE); + return 0; +} + +/* activation of channel in dedicated mode */ +static int gsm48_rr_activate_channel(struct osmocom_ms *ms, + struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_settings *set = &ms->settings; + struct gsm48_sysinfo *s = ms->cellsel.si; + struct rx_meas_stat *meas = &ms->meas; + uint8_t ch_type, ch_subch, ch_ts; + uint8_t timeout = 64; + + /* setting (new) timing advance */ + LOGP(DRR, LOGL_INFO, "setting indicated TA %d (actual TA %d)\n", + cd->ind_ta, cd->ind_ta - set->alter_delay); + l1ctl_tx_param_req(ms, cd->ind_ta - set->alter_delay, + (set->alter_tx_power) ? set->alter_tx_power_value + : cd->ind_tx_power); + + /* reset measurement and link timeout */ + meas->ds_fail = 0; + if (s) { + if (s->sacch_radio_link_timeout) { + timeout = s->sacch_radio_link_timeout; + LOGP(DRR, LOGL_INFO, "using last SACCH timeout %d\n", + timeout); + } else if (s->bcch_radio_link_timeout) { + timeout = s->bcch_radio_link_timeout; + LOGP(DRR, LOGL_INFO, "using last BCCH timeout %d\n", + timeout); + } + } + meas->rl_fail = meas->s = timeout; + + /* setting initial (invalid) measurement report, resetting SI5* */ + if (s) { + memset(s->si5_msg, 0, sizeof(s->si5_msg)); + memset(s->si5b_msg, 0, sizeof(s->si5b_msg)); + memset(s->si5t_msg, 0, sizeof(s->si5t_msg)); + } + meas->frames = meas->snr = meas->berr = meas->rxlev = 0; + rr->meas.nc_num = 0; + stop_rr_t_meas(rr); + start_rr_t_meas(rr, 1, 0); + gsm48_rr_tx_meas_rep(ms); + + /* establish */ + LOGP(DRR, LOGL_INFO, "establishing channel in dedicated mode\n"); + rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts); + LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d, " + "cipher %d\n", ch_type, ch_subch, ch_ts, cd->mode, + rr->cipher_type + 1); + if (cd->h) + l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn, + ma, ma_len, cd->chan_nr, cd->tsc, cd->mode); + else + l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc, + cd->mode); + rr->dm_est = 1; + + /* old SI 5/6 are not valid on a new dedicated channel */ + s->si5 = s->si5bis = s->si5ter = s->si6 = 0; + + if (rr->cipher_on) + l1ctl_tx_crypto_req(ms, rr->cipher_type + 1, subscr->key, 8); + + return 0; +} + +/* frequency change of channel "after time" */ +static int gsm48_rr_channel_after_time(struct osmocom_ms *ms, + struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len, uint16_t fn) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rrlayer *rr = &ms->rrlayer; + + if (cd->h) + l1ctl_tx_dm_freq_req_h1(ms, cd->maio, cd->hsn, + ma, ma_len, cd->tsc, fn); + else + l1ctl_tx_dm_freq_req_h0(ms, cd->arfcn, cd->tsc, fn); + + if (rr->cipher_on) + l1ctl_tx_crypto_req(ms, rr->cipher_type + 1, subscr->key, 8); + + gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode); + + return 0; +} + +/* render list of hopping channels from channel description elements */ +static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, + uint16_t *ma, uint8_t *ma_len) +{ + struct gsm48_sysinfo *s = ms->cellsel.si; + struct gsm_support *sup = &ms->support; + int i; + uint16_t arfcn; + + /* no hopping (no MA), channel description is valid */ + if (!cd->h) { + ma_len = 0; + return 0; + } + + /* decode mobile allocation */ + if (cd->mob_alloc_lv[0]) { + struct gsm_sysinfo_freq *freq = s->freq; + + LOGP(DRR, LOGL_INFO, "decoding mobile allocation\n"); + + if (cd->cell_desc_lv[0]) { + LOGP(DRR, LOGL_INFO, "using cell channel descr.\n"); + if (cd->cell_desc_lv[0] != 16) { + LOGP(DRR, LOGL_ERROR, "cell channel descr. " + "has invalid lenght\n"); + return GSM48_RR_CAUSE_ABNORMAL_UNSPEC; + } + gsm48_decode_freq_list(freq, cd->cell_desc_lv + 1, 16, + 0xce, FREQ_TYPE_SERV); + } + + gsm48_decode_mobile_alloc(freq, cd->mob_alloc_lv + 1, + cd->mob_alloc_lv[0], ma, ma_len, 0); + if (*ma_len < 1) { + LOGP(DRR, LOGL_NOTICE, "mobile allocation with no " + "frequency available\n"); + return GSM48_RR_CAUSE_NO_CELL_ALLOC_A; + + } + } else + /* decode frequency list */ + if (cd->freq_list_lv[0]) { + struct gsm_sysinfo_freq f[1024]; + int j = 0; + + LOGP(DRR, LOGL_INFO, "decoding frequency list\n"); + + /* get bitmap */ + if (gsm48_decode_freq_list(f, cd->freq_list_lv + 1, + cd->freq_list_lv[0], 0xce, FREQ_TYPE_SERV)) { + LOGP(DRR, LOGL_NOTICE, "frequency list invalid\n"); + return GSM48_RR_CAUSE_ABNORMAL_UNSPEC; + } + + /* collect channels from bitmap (1..1023,0) */ + for (i = 1; i <= 1024; i++) { + if ((f[i & 1023].mask & FREQ_TYPE_SERV)) { + LOGP(DRR, LOGL_INFO, "Listed ARFCN #%d: %d\n", + j, i); + if (j == 64) { + LOGP(DRR, LOGL_NOTICE, "frequency list " + "exceeds 64 entries!\n"); + return GSM48_RR_CAUSE_ABNORMAL_UNSPEC; + } + ma[j++] = i; + } + } + *ma_len = j; + return 0; + } else + /* decode frequency channel sequence */ + if (cd->freq_seq_lv[0]) { + int j = 0, inc; + + LOGP(DRR, LOGL_INFO, "decoding frequency channel sequence\n"); + + if (cd->freq_seq_lv[0] != 9) { + LOGP(DRR, LOGL_NOTICE, "invalid frequency channel " + "sequence\n"); + return GSM48_RR_CAUSE_ABNORMAL_UNSPEC; + } + arfcn = cd->freq_seq_lv[1] & 0x7f; + LOGP(DRR, LOGL_INFO, "Listed Sequence ARFCN #%d: %d\n", j, + arfcn); + ma[j++] = arfcn; + for (i = 0; i <= 16; i++) { + if ((i & 1)) + inc = cd->freq_seq_lv[2 + (i >> 1)] & 0x0f; + else + inc = cd->freq_seq_lv[2 + (i >> 1)] >> 4; + if (inc) { + arfcn += inc; + LOGP(DRR, LOGL_INFO, "Listed Sequence ARFCN " + "#%d: %d\n", j, arfcn); + ma[j++] = arfcn; + } else + arfcn += 15; + } + *ma_len = j; + return 0; + } else { + LOGP(DRR, LOGL_NOTICE, "hopping, but nothing that tells us " + "a sequence\n"); + return GSM48_RR_CAUSE_ABNORMAL_UNSPEC; + } + + /* check for unsported frequency */ + for (i = 0; i < *ma_len; i++) { + arfcn = ma[i]; + if (!(sup->freq_map[arfcn >> 3] & (1 << (arfcn & 7)))) { + LOGP(DRR, LOGL_NOTICE, "Hopping frequency %d not " + "supported\n", arfcn); + return GSM48_RR_CAUSE_FREQ_NOT_IMPL; + } + } + + return 0; +} + +/* activate link and send establish request */ +static int gsm48_rr_dl_est(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_pag_rsp *pr; + uint8_t mi[11]; + uint16_t ma[64]; + uint8_t ma_len; + + /* 3.3.1.1.3.1 */ + stop_rr_t3126(rr); + + /* check if we have to change channel at starting time (we delay) */ + if (rr->cd_now.start) { + int32_t now, start, diff; + uint32_t start_mili = 0; + + /* how much time do we have left? */ + now = ms->meas.last_fn % 42432; + start = rr->cd_now.start_tm.fn % 42432; + diff = start - now; + if (diff < 0) + diff += 42432; + LOGP(DRR, LOGL_INFO, " (Tnow %d Tstart %d diff %d)\n", + now, start, diff); + start_mili = (uint32_t)diff * 19580 / 42432 * 10; + if (diff >= 32024 || !start_mili) { + LOGP(DRR, LOGL_INFO, " -> Start time already " + "elapsed\n"); + rr->cd_now.start = 0; + } else { + LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the " + "future\n", start_mili); + } + +#ifndef TEST_FREQUENCY_MOD + /* schedule start of IMM.ASS */ + rr->modify_state = GSM48_RR_MOD_IMM_ASS; + start_rr_t_starting(rr, start_mili / 1000, + (start_mili % 1000) * 1000); + /* when timer fires, start time is already elapsed */ + rr->cd_now.start = 0; + + return 0; +#endif + } + + /* get hopping sequence, if required */ + if (gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len)) + return -EINVAL; + + /* clear all sequence numbers for all possible PDs */ + rr->v_sd = 0; + + /* send DL_EST_REQ */ + if (rr->rr_est_msg) { + LOGP(DRR, LOGL_INFO, "sending establish message\n"); + + /* use queued message */ + nmsg = rr->rr_est_msg; + rr->rr_est_msg = 0; + + /* set sequence number and increment */ + gsm48_apply_v_sd(rr, nmsg); + } else { + /* create paging response */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_PAG_RESP; + pr = (struct gsm48_pag_rsp *) msgb_put(nmsg, sizeof(*pr)); + /* key sequence */ + pr->key_seq = subscr->key_seq; + /* classmark 2 */ + pr->cm2_len = sizeof(pr->cm2); + gsm48_rr_enc_cm2(ms, &pr->cm2); + /* mobile identity */ + if (ms->subscr.tmsi != 0xffffffff + && ms->subscr.mcc == cs->sel_mcc + && ms->subscr.mnc == cs->sel_mnc + && ms->subscr.lac == cs->sel_lac) { + gsm48_generate_mid_from_tmsi(mi, subscr->tmsi); + LOGP(DRR, LOGL_INFO, "sending paging response with " + "TMSI\n"); + } else if (subscr->imsi[0]) { + gsm48_generate_mid_from_imsi(mi, subscr->imsi); + LOGP(DRR, LOGL_INFO, "sending paging response with " + "IMSI\n"); + } else { + mi[1] = 1; + mi[2] = 0xf0 | GSM_MI_TYPE_NONE; + LOGP(DRR, LOGL_INFO, "sending paging response without " + "TMSI/IMSI\n"); + } + msgb_put(nmsg, 1 + mi[1]); + memcpy(pr->data, mi + 1, 1 + mi[1]); + } + +#ifdef TEST_FREQUENCY_MOD + LOGP(DRR, LOGL_INFO, " TESTING: frequency modify IMM.ASS\n"); + memcpy(&rr->cd_before, &rr->cd_now, sizeof(rr->cd_before)); + rr->cd_before.h = 0; + rr->cd_before.arfcn = 0; + /* activate channel */ + gsm48_rr_activate_channel(ms, &rr->cd_before, ma, ma_len); + /* render channel "after time" */ + gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); + /* schedule change of channel */ + gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len, + rr->cd_now.start_tm.fn); +#else + /* activate channel */ + gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len); +#endif + + /* start establishmnet */ + return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg); +} + +/* the link is established */ +static int gsm48_rr_estab_cnf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + uint8_t *mode; + struct msgb *nmsg; + + /* if MM has releases before confirm, we start release */ + if (rr->state == GSM48_RR_ST_REL_PEND) { + LOGP(DRR, LOGL_INFO, "MM already released RR.\n"); + /* release message */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = 0; /* normal release */ + /* start release */ + return gsm48_send_rsl_rel(ms, RSL_MT_REL_REQ, nmsg); + } + + /* 3.3.1.1.4 */ + new_rr_state(rr, GSM48_RR_ST_DEDICATED); + + /* send confirm to upper layer */ + nmsg = gsm48_rr_msgb_alloc( + (rr->rr_est_req) ? GSM48_RR_EST_CNF : GSM48_RR_EST_IND); + if (!nmsg) + return -ENOMEM; + return gsm48_rr_upmsg(ms, nmsg); +} + +/* the link is released in pending state (by l2) */ +static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + /* switch back to old channel, if modify/ho failed */ + switch (rr->modify_state) { + case GSM48_RR_MOD_ASSIGN: + case GSM48_RR_MOD_HANDO: + /* channel is deactivate there */ + return gsm48_rr_rel_cnf(ms, msg); + case GSM48_RR_MOD_ASSIGN_RESUME: + case GSM48_RR_MOD_HANDO_RESUME: + rr->modify_state = GSM48_RR_MOD_NONE; + break; + } + + LOGP(DSUM, LOGL_INFO, "Radio link is released\n"); + + /* send inication to upper layer */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = RR_REL_CAUSE_NORMAL; + gsm48_rr_upmsg(ms, nmsg); + + /* start release timer, so UA will be transmitted */ + start_rr_t_rel_wait(rr, 1, 500000); + + /* pending release */ + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + return 0; +} + +/* 9.1.7 CHANNEL RELEASE is received */ +static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_chan_rel *cr = (struct gsm48_chan_rel *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cr); + struct tlv_parsed tp; + struct msgb *nmsg; + uint8_t *mode; + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE " + "message.\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0); + + LOGP(DRR, LOGL_INFO, "channel release request with cause 0x%02x)\n", + cr->rr_cause); + + /* BA range */ + if (TLVP_PRESENT(&tp, GSM48_IE_BA_RANGE)) { + gsm48_decode_ba_range(TLVP_VAL(&tp, GSM48_IE_BA_RANGE), + *(TLVP_VAL(&tp, GSM48_IE_BA_RANGE) - 1), rr->ba_range, + &rr->ba_ranges, + sizeof(rr->ba_range) / sizeof(rr->ba_range[0])); + /* NOTE: the ranges are kept until IDLE state is returned + * (see new_rr_state) + */ + } + + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* start T3110, so that two DISCs can be sent due to T200 timeout */ + start_rr_t3110(rr, 1, 500000); + + /* disconnect the main signalling link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = 0; /* normal release */ + return gsm48_send_rsl_rel(ms, RSL_MT_REL_REQ, nmsg); +} + +/* + * frequency redefition, chanel mode modify, assignment, and handover + */ + +/* set channel mode in case of TCH */ +static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, + uint8_t mode) +{ + uint8_t ch_type, ch_subch, ch_ts; + + /* only apply mode to TCH/F or TCH/H */ + rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ch_type != RSL_CHAN_Bm_ACCHs + && ch_type != RSL_CHAN_Lm_ACCHs) + return -ENOTSUP; + + /* setting (new) timing advance */ + LOGP(DRR, LOGL_INFO, "setting TCH mode to %d\n", mode); + l1ctl_tx_tch_mode_req(ms, mode); + + return 0; +} + +/* 9.1.13 FREQUENCY REDEFINITION is received */ +static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_frq_redef *fr = msgb_l3(msg); + int mob_al_len = msgb_l3len(msg) - sizeof(*fr); + uint8_t ch_type, ch_subch, ch_ts; + struct gsm48_rr_cd cd; + uint8_t cause; + uint8_t *st; + uint16_t ma[64]; + uint8_t ma_len; + + memcpy(&cd, &rr->cd_now, sizeof(cd)); + + if (mob_al_len < 0 /* mobile allocation IE must be included */ + || fr->mob_alloc_len + 2 > mob_al_len) { /* short read of IE */ + LOGP(DRR, LOGL_NOTICE, "Short read of FREQUENCY REDEFINITION " + "message.\n"); + return -EINVAL; + } + if (fr->mob_alloc_len > 8) { + LOGP(DRR, LOGL_NOTICE, "Moble allocation in FREQUENCY " + "REDEFINITION too large.\n"); + return -EINVAL; + } + + /* decode channel description */ + LOGP(DRR, LOGL_INFO, "FREQUENCY REDEFINITION:\n"); + cd.chan_nr = fr->chan_desc.chan_nr; + rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (fr->chan_desc.h0.h) { + cd.h = 1; + gsm48_decode_chan_h1(&fr->chan_desc, &cd.tsc, &cd.maio, + &cd.hsn); + LOGP(DRR, LOGL_INFO, " (MAIO %u HSN %u TS %u SS %u TSC %u)\n", + cd.maio, cd.hsn, ch_ts, ch_subch, cd.tsc); + } else { + cd.h = 0; + gsm48_decode_chan_h0(&fr->chan_desc, &cd.tsc, &cd.arfcn); + LOGP(DRR, LOGL_INFO, " (ARFCN %u TS %u SS %u TSC %u)\n", + cd.arfcn, ch_ts, ch_subch, cd.tsc); + } + + /* mobile allocation */ + memcpy(rr->cd_now.mob_alloc_lv, &fr->mob_alloc_len, + fr->mob_alloc_len + 1); + + /* starting time */ + st = fr->mob_alloc + fr->mob_alloc_len; + gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1)); + + /* cell channel description */ + if (mob_al_len >= fr->mob_alloc_len + 2 + 17 + && fr->mob_alloc[fr->mob_alloc_len + 2] == GSM48_IE_CELL_CH_DESC) { + const uint8_t *v = fr->mob_alloc + fr->mob_alloc_len + 2 + 1; + + LOGP(DRR, LOGL_INFO, " using cell channel description)\n"); + cd.cell_desc_lv[0] = 16; + memcpy(cd.cell_desc_lv + 1, v, 17); + } + + /* render channel "after time" */ + cause = gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); + if (cause) + return gsm48_rr_tx_rr_status(ms, cause); + + /* update to new channel data */ + memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now)); + + /* schedule change of channel */ + gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len, + rr->cd_now.start_tm.fn); + + rr->cd_now.start = 0; + + return 0; +} + +/* 9.1.6 sending CHANNEL MODE MODIFY ACKNOWLEDGE */ +static int gsm48_rr_tx_chan_modify_ack(struct osmocom_ms *ms, + struct gsm48_chan_desc *cd, uint8_t mode) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_chan_mode_modify *cm; + + LOGP(DRR, LOGL_INFO, "CHAN.MODE.MOD ACKNOWLEDGE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + cm = (struct gsm48_chan_mode_modify *) msgb_put(nmsg, sizeof(*cm)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF_ACK; + + /* CD */ + memcpy(&cm->chan_desc, cd, sizeof(struct gsm48_chan_desc)); + /* mode */ + cm->mode = mode; + + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg); +} + +/* 9.1.5 CHANNEL MODE MODIFY is received */ +static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_chan_mode_modify *cm = + (struct gsm48_chan_mode_modify *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm); + struct gsm48_rr_cd *cd = &rr->cd_now; + uint8_t ch_type, ch_subch, ch_ts; + uint8_t cause; + + LOGP(DRR, LOGL_INFO, "CHANNEL MODE MODIFY\n"); + + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL MODE MODIFY " + "message.\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + + /* decode channel description */ + cd->chan_nr = cm->chan_desc.chan_nr; + rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (cm->chan_desc.h0.h) { + cd->h = 1; + gsm48_decode_chan_h1(&cm->chan_desc, &cd->tsc, &cd->maio, + &cd->hsn); + LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x MAIO %u HSN %u TS %u " + "SS %u TSC %u mode %u)\n", cm->chan_desc.chan_nr, + cd->maio, cd->hsn, ch_ts, ch_subch, cd->tsc, cm->mode); + } else { + cd->h = 0; + gsm48_decode_chan_h0(&cm->chan_desc, &cd->tsc, &cd->arfcn); + LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %u TS %u SS %u " + "TSC %u mode %u)\n", cm->chan_desc.chan_nr, cd->arfcn, + ch_ts, ch_subch, cd->tsc, cm->mode); + } + /* mode */ + cause = gsm48_rr_check_mode(ms, cd->chan_nr, cm->mode); + if (cause) + return gsm48_rr_tx_rr_status(ms, cause); + cd->mode = cm->mode; + gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode); + + return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, cm->mode); +} + +/* 9.1.3 sending ASSIGNMENT COMPLETE */ +static int gsm48_rr_tx_ass_cpl(struct osmocom_ms *ms, uint8_t cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_ass_cpl *ac; + + LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMPLETE (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + ac = (struct gsm48_ass_cpl *) msgb_put(nmsg, sizeof(*ac)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_ASS_COMPL; + + /* RR_CAUSE */ + ac->rr_cause = cause; + + return gsm48_send_rsl(ms, RSL_MT_RES_REQ, nmsg); +} + +/* 9.1.4 sending ASSIGNMENT FAILURE */ +static int gsm48_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause, + uint8_t rsl_prim) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_ass_fail *af; + + LOGP(DRR, LOGL_INFO, "ASSIGNMENT FAILURE (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + af = (struct gsm48_ass_fail *) msgb_put(nmsg, sizeof(*af)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_ASS_COMPL; + + /* RR_CAUSE */ + af->rr_cause = cause; + + return gsm48_send_rsl(ms, rsl_prim, nmsg); +} + +/* 9.1.2 ASSIGNMENT COMMAND is received */ +static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_ass_cmd *ac = (struct gsm48_ass_cmd *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ac); + struct tlv_parsed tp; + struct gsm48_rr_cd *cda = &rr->cd_after; + struct gsm48_rr_cd *cdb = &rr->cd_before; + uint8_t ch_type, ch_subch, ch_ts; + uint8_t before_time = 0; + uint16_t ma[64]; + uint8_t ma_len; + uint32_t start_mili = 0; + uint8_t cause; + struct msgb *nmsg; + + + LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMMAND\n"); + + memset(cda, 0, sizeof(*cda)); + cda->ind_tx_power = rr->cd_now.ind_tx_power; + memset(cdb, 0, sizeof(*cdb)); + cdb->ind_tx_power = rr->cd_now.ind_tx_power; + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND " + "message.\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0); + + /* decode channel description (before time) */ + if (TLVP_PRESENT(&tp, GSM48_IE_CH_DESC_1_BEFORE)) { + struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *) + TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE); + cdb->chan_nr = ccd->chan_nr; + rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ccd->h0.h) { + cdb->h = 1; + gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio, + &cdb->hsn); + LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x MAIO %u " + "HSN %u TS %u SS %u TSC %u)\n", ccd->chan_nr, + cdb->maio, cdb->hsn, ch_ts, ch_subch, cdb->tsc); + } else { + cdb->h = 0; + gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn); + LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x " + "ARFCN %u TS %u SS %u TSC %u)\n", ccd->chan_nr, + cdb->arfcn, ch_ts, ch_subch, cdb->tsc); + } + before_time = 1; + } + + /* decode channel description (after time) */ + cda->chan_nr = ac->chan_desc.chan_nr; + rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ac->chan_desc.h0.h) { + cda->h = 1; + gsm48_decode_chan_h1(&ac->chan_desc, &cda->tsc, &cda->maio, + &cda->hsn); + LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x MAIO %u HSN %u " + "TS %u SS %u TSC %u)\n", ac->chan_desc.chan_nr, + cda->maio, cda->hsn, ch_ts, ch_subch, cda->tsc); + } else { + cda->h = 0; + gsm48_decode_chan_h0(&ac->chan_desc, &cda->tsc, &cda->arfcn); + LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %u TS %u " + "SS %u TSC %u)\n", ac->chan_desc.chan_nr, + cda->arfcn, ch_ts, ch_subch, cda->tsc); + } + + /* starting time */ +#ifdef TEST_STARTING_TIMER + cda->start = 1; + cda->start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432; + LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n"); +#else + if (TLVP_PRESENT(&tp, GSM48_IE_START_TIME)) { + gsm48_decode_start_time(cda, (struct gsm48_start_time *) + TLVP_VAL(&tp, GSM48_IE_START_TIME)); + /* 9.1.2.5 "... before time IE is not present..." */ + if (!before_time) { + LOGP(DRR, LOGL_INFO, " -> channel description after " + "time only, but starting time\n"); + } else + LOGP(DRR, LOGL_INFO, " -> channel description before " + "and after time\n"); + } else { + /* 9.1.2.5 "... IEs unnecessary in this message." */ + if (before_time) { + before_time = 0; + LOGP(DRR, LOGL_INFO, " -> channel description before " + "time, but no starting time, ignoring!\n"); + } + } +#endif + + /* mobile allocation / frequency list after time */ + if (cda->h) { + if (TLVP_PRESENT(&tp, GSM48_IE_MA_AFTER)) { + const uint8_t *lv = + TLVP_VAL(&tp, GSM48_IE_MA_AFTER) - 1; + + LOGP(DRR, LOGL_INFO, " after: hopping required and " + "mobile allocation available\n"); + if (*lv + 1 > sizeof(cda->mob_alloc_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + memcpy(cda->mob_alloc_lv, lv, *lv + 1); + } else + if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) { + const uint8_t *lv = + TLVP_VAL(&tp, GSM48_IE_FREQ_L_AFTER) - 1; + + LOGP(DRR, LOGL_INFO, " after: hopping required and " + "frequency list available\n"); + if (*lv + 1 > sizeof(cda->freq_list_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + memcpy(cda->freq_list_lv, lv, *lv + 1); + } else { + LOGP(DRR, LOGL_NOTICE, " after: hopping required, but " + "no mobile allocation / frequency list\n"); + } + } + + /* mobile allocation / frequency list before time */ + if (cdb->h) { + if (TLVP_PRESENT(&tp, GSM48_IE_MA_BEFORE)) { + const uint8_t *lv = + TLVP_VAL(&tp, GSM48_IE_MA_BEFORE) - 1; + + LOGP(DRR, LOGL_INFO, " before: hopping required and " + "mobile allocation available\n"); + if (*lv + 1 > sizeof(cdb->mob_alloc_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + memcpy(cdb->mob_alloc_lv, lv, *lv + 1); + } else + if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_BEFORE)) { + const uint8_t *lv = + TLVP_VAL(&tp, GSM48_IE_FREQ_L_BEFORE) - 1; + + LOGP(DRR, LOGL_INFO, " before: hopping required and " + "frequency list available\n"); + if (*lv + 1 > sizeof(cdb->freq_list_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + memcpy(cdb->freq_list_lv, lv, *lv + 1); + } else + if (TLVP_PRESENT(&tp, GSM48_IE_F_CH_SEQ_BEFORE)) { + const uint8_t *v = + TLVP_VAL(&tp, GSM48_IE_F_CH_SEQ_BEFORE); + uint8_t len = TLVP_LEN(&tp, GSM48_IE_F_CH_SEQ_BEFORE); + + LOGP(DRR, LOGL_INFO, " before: hopping required and " + "frequency channel sequence available\n"); + if (len + 1 > sizeof(cdb->freq_seq_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + cdb->freq_seq_lv[0] = len; + memcpy(cdb->freq_seq_lv + 1, v, len); + } else + if (cda->mob_alloc_lv[0]) { + LOGP(DRR, LOGL_INFO, " before: hopping required and " + "mobile allocation not available, using " + "mobile allocation after time\n"); + memcpy(cdb->mob_alloc_lv, cda->mob_alloc_lv, + sizeof(cdb->mob_alloc_lv)); + } else + if (cda->freq_list_lv[0]) { + LOGP(DRR, LOGL_INFO, " before: hopping required and " + "frequency list not available, using " + "frequency list after time\n"); + memcpy(cdb->freq_list_lv, cda->freq_list_lv, + sizeof(cdb->freq_list_lv)); + } else { + LOGP(DRR, LOGL_NOTICE, " before: hopping required, but " + "no mobile allocation / frequency list\n"); + } + } + + /* cell channel description */ + if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) { + const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC); + uint8_t len = TLVP_LEN(&tp, GSM48_IE_CELL_CH_DESC); + + LOGP(DRR, LOGL_INFO, " both: using cell channel description " + "in case of mobile allocation\n"); + if (len + 1 > sizeof(cdb->cell_desc_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + cdb->cell_desc_lv[0] = len; + memcpy(cdb->cell_desc_lv + 1, v, len); + cda->cell_desc_lv[0] = len; + memcpy(cda->cell_desc_lv + 1, v, len); + } else { + /* keep old */ + memcpy(cdb->cell_desc_lv, rr->cd_now.cell_desc_lv, + sizeof(cdb->cell_desc_lv)); + memcpy(cda->cell_desc_lv, rr->cd_now.cell_desc_lv, + sizeof(cda->cell_desc_lv)); + } + + /* channel mode */ + if (TLVP_PRESENT(&tp, GSM48_IE_CHANMODE_1)) { + cda->mode = cdb->mode = *TLVP_VAL(&tp, GSM48_IE_CHANMODE_1); + LOGP(DRR, LOGL_INFO, " both: changing channel mode 0x%02x\n", + cda->mode); + } else + cda->mode = cdb->mode = rr->cd_now.mode; + + /* cipher mode setting */ + if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET)) { + cda->cipher = cdb->cipher = + *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET); + LOGP(DRR, LOGL_INFO, " both: changing cipher mode 0x%02x\n", + cda->cipher); + } else + cda->cipher = cdb->cipher = rr->cd_now.cipher; + + /* power command and TA (before and after time) */ + gsm48_decode_power_cmd_acc( + (struct gsm48_power_cmd *) &ac->power_command, + &cda->ind_tx_power, NULL); + cdb->ind_tx_power = cda->ind_tx_power; + cda->ind_ta = cdb->ind_ta = rr->cd_now.ind_ta; /* same cell */ + LOGP(DRR, LOGL_INFO, " both: (tx_power %d TA %d)\n", cda->ind_tx_power, + cda->ind_ta); + + /* check if we have to change channel at starting time */ + if (cda->start) { + int32_t now, start, diff; + + /* how much time do we have left? */ + now = ms->meas.last_fn % 42432; + start = cda->start_tm.fn % 42432; + diff = start - now; + if (diff < 0) + diff += 42432; + LOGP(DRR, LOGL_INFO, " after: (Tnow %d Tstart %d diff %d)\n", + now, start, diff); + start_mili = (uint32_t)diff * 19580 / 42432 * 10; + if (diff >= 32024 || !start_mili) { + LOGP(DRR, LOGL_INFO, " -> Start time already " + "elapsed\n"); + before_time = 0; + cda->start = 0; + } else { + LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the " + "future\n", start_mili); + } + } + + /* check if channels are valid */ + cause = gsm48_rr_check_mode(ms, cda->chan_nr, cda->mode); + if (cause) + return gsm48_rr_tx_ass_fail(ms, cause, RSL_MT_DATA_REQ); + if (before_time) { + cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len); + if (cause) + return gsm48_rr_tx_ass_fail(ms, cause, RSL_MT_DATA_REQ); + } + cause = gsm48_rr_render_ma(ms, cda, ma, &ma_len); + if (cause) + return gsm48_rr_tx_ass_fail(ms, cause, RSL_MT_DATA_REQ); + +#ifdef TEST_FREQUENCY_MOD + LOGP(DRR, LOGL_INFO, " TESTING: frequency modify ASS.CMD\n"); + before_time = 1; + memcpy(cdb, cda, sizeof(*cdb)); + cdb->h = 0; + cdb->arfcn = 0; +#endif + + /* schedule start of assignment */ + rr->modify_state = GSM48_RR_MOD_ASSIGN; + if (!before_time && cda->start) { + start_rr_t_starting(rr, start_mili / 1000, start_mili % 1000); + /* when timer fires, start time is already elapsed */ + cda->start = 0; + + return 0; + } + + /* if no starting time, start suspension of current link directly */ + LOGP(DRR, LOGL_INFO, "request suspension of data link\n"); + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, nmsg); + + return 0; +} + +/* 9.1.16 sending HANDOVER COMPLETE */ +static int gsm48_rr_tx_hando_cpl(struct osmocom_ms *ms, uint8_t cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_ho_cpl *hc; + + LOGP(DRR, LOGL_INFO, "HANDOVER COMPLETE (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + hc = (struct gsm48_ho_cpl *) msgb_put(nmsg, sizeof(*hc)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_HANDO_COMPL; + + /* RR_CAUSE */ + hc->rr_cause = cause; + + // FIXME: mobile observed time + + return gsm48_send_rsl(ms, RSL_MT_RES_REQ, nmsg); +} + +/* 9.1.4 sending HANDOVER FAILURE */ +static int gsm48_rr_tx_hando_fail(struct osmocom_ms *ms, uint8_t cause, + uint8_t rsl_prim) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_ho_fail *hf; + + LOGP(DRR, LOGL_INFO, "HANDOVER FAILURE (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + hf = (struct gsm48_ho_fail *) msgb_put(nmsg, sizeof(*hf)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_ASS_COMPL; + + /* RR_CAUSE */ + hf->rr_cause = cause; + + return gsm48_send_rsl(ms, rsl_prim, nmsg); +} + +/* receiving HANDOVER COMMAND message (9.1.15) */ +static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ho); + struct tlv_parsed tp; + struct gsm48_rr_cd *cda = &rr->cd_after; + struct gsm48_rr_cd *cdb = &rr->cd_before; + uint16_t arfcn; + uint8_t bcc, ncc; + uint8_t ch_type, ch_subch, ch_ts; + uint8_t before_time = 0; + uint16_t ma[64]; + uint8_t ma_len; + uint32_t start_mili = 0; + uint8_t cause; + struct msgb *nmsg; + + LOGP(DRR, LOGL_INFO, "HANDOVER COMMAND\n"); + + memset(cda, 0, sizeof(*cda)); + cda->ind_tx_power = rr->cd_now.ind_tx_power; + memset(cdb, 0, sizeof(*cdb)); + cdb->ind_tx_power = rr->cd_now.ind_tx_power; + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND " + "message.\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + + /* cell description */ + gsm48_decode_cell_desc(&ho->cell_desc, &arfcn, &ncc, &bcc); + + /* handover reference */ + rr->chan_req_val = ho->ho_ref; + rr->chan_req_mask = 0x00; + + tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0); + + /* sync ind */ + if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND)) { + gsm48_decode_sync_ind(rr, (struct gsm48_sync_ind *) + TLVP_VAL(&tp, GSM48_IE_SYNC_IND)); + LOGP(DRR, LOGL_INFO, " (sync_ind=%d rot=%d nci=%d)\n", + rr->hando_sync_ind, rr->hando_rot, rr->hando_nci); + } + + /* decode channel description (before time) */ + if (TLVP_PRESENT(&tp, GSM48_IE_CH_DESC_1_BEFORE)) { + struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *) + TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE); + cdb->chan_nr = ccd->chan_nr; + rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ccd->h0.h) { + cdb->h = 1; + gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio, + &cdb->hsn); + LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x MAIO %u " + "HSN %u TS %u SS %u TSC %u)\n", ccd->chan_nr, + cdb->maio, cdb->hsn, ch_ts, ch_subch, cdb->tsc); + } else { + cdb->h = 0; + gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn); + LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x " + "ARFCN %u TS %u SS %u TSC %u)\n", ccd->chan_nr, + cdb->arfcn, ch_ts, ch_subch, cdb->tsc); + } + before_time = 1; + } + + /* decode channel description (after time) */ + cda->chan_nr = ho->chan_desc.chan_nr; + rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ho->chan_desc.h0.h) { + cda->h = 1; + gsm48_decode_chan_h1(&ho->chan_desc, &cda->tsc, &cda->maio, + &cda->hsn); + LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x MAIO %u HSN %u " + "TS %u SS %u TSC %u)\n", ho->chan_desc.chan_nr, + cda->maio, cda->hsn, ch_ts, ch_subch, cda->tsc); + } else { + cda->h = 0; + gsm48_decode_chan_h0(&ho->chan_desc, &cda->tsc, &cda->arfcn); + LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %u TS %u " + "SS %u TSC %u)\n", ho->chan_desc.chan_nr, + cda->arfcn, ch_ts, ch_subch, cda->tsc); + } + + /* starting time */ +#ifdef TEST_STARTING_TIMER + cda->start = 1; + cda->start_tm.fn = (ms->meas.last_fn + TEST_STARTING_TIMER) % 42432; + LOGP(DRR, LOGL_INFO, " TESTING: starting time ahead\n"); +#else + if (TLVP_PRESENT(&tp, GSM48_IE_START_TIME)) { + gsm48_decode_start_time(cda, (struct gsm48_start_time *) + TLVP_VAL(&tp, GSM48_IE_START_TIME)); + /* 9.1.2.5 "... before time IE is not present..." */ + if (!before_time) { + LOGP(DRR, LOGL_INFO, " -> channel description after " + "time only, but starting time\n"); + } else + LOGP(DRR, LOGL_INFO, " -> channel description before " + "and after time\n"); + } else { + /* 9.1.2.5 "... IEs unnecessary in this message." */ + if (before_time) { + before_time = 0; + LOGP(DRR, LOGL_INFO, " -> channel description before " + "time, but no starting time, ignoring!\n"); + } + } +#endif + + /* mobile allocation / frequency list after time */ + if (cda->h) { + if (TLVP_PRESENT(&tp, GSM48_IE_MA_AFTER)) { + const uint8_t *lv = + TLVP_VAL(&tp, GSM48_IE_MA_AFTER) - 1; + + LOGP(DRR, LOGL_INFO, " after: hopping required and " + "mobile allocation available\n"); + if (*lv + 1 > sizeof(cda->mob_alloc_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + memcpy(cda->mob_alloc_lv, lv, *lv + 1); + } else + if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_AFTER)) { + const uint8_t *lv = + TLVP_VAL(&tp, GSM48_IE_FREQ_L_AFTER) - 1; + + LOGP(DRR, LOGL_INFO, " after: hopping required and " + "frequency list available\n"); + if (*lv + 1 > sizeof(cda->freq_list_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + memcpy(cda->freq_list_lv, lv, *lv + 1); + } else { + LOGP(DRR, LOGL_NOTICE, " after: hopping required, but " + "no mobile allocation / frequency list\n"); + } + } + + /* mobile allocation / frequency list before time */ + if (cdb->h) { + if (TLVP_PRESENT(&tp, GSM48_IE_MA_BEFORE)) { + const uint8_t *lv = + TLVP_VAL(&tp, GSM48_IE_MA_BEFORE) - 1; + + LOGP(DRR, LOGL_INFO, " before: hopping required and " + "mobile allocation available\n"); + if (*lv + 1 > sizeof(cdb->mob_alloc_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + memcpy(cdb->mob_alloc_lv, lv, *lv + 1); + } else + if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_L_BEFORE)) { + const uint8_t *lv = + TLVP_VAL(&tp, GSM48_IE_FREQ_L_BEFORE) - 1; + + LOGP(DRR, LOGL_INFO, " before: hopping required and " + "frequency list available\n"); + if (*lv + 1 > sizeof(cdb->freq_list_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + memcpy(cdb->freq_list_lv, lv, *lv + 1); + } else + if (TLVP_PRESENT(&tp, GSM48_IE_F_CH_SEQ_BEFORE)) { + const uint8_t *v = + TLVP_VAL(&tp, GSM48_IE_F_CH_SEQ_BEFORE); + uint8_t len = TLVP_LEN(&tp, GSM48_IE_F_CH_SEQ_BEFORE); + + LOGP(DRR, LOGL_INFO, " before: hopping required and " + "frequency channel sequence available\n"); + if (len + 1 > sizeof(cdb->freq_seq_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + cdb->freq_seq_lv[0] = len; + memcpy(cdb->freq_seq_lv, v + 1, *v); + } else + if (cda->mob_alloc_lv[0]) { + LOGP(DRR, LOGL_INFO, " before: hopping required and " + "mobile allocation not available, using " + "mobile allocation after time\n"); + memcpy(cdb->mob_alloc_lv, cda->mob_alloc_lv, + sizeof(cdb->mob_alloc_lv)); + } else + if (cda->freq_list_lv[0]) { + LOGP(DRR, LOGL_INFO, " before: hopping required and " + "frequency list not available, using " + "frequency list after time\n"); + memcpy(cdb->freq_list_lv, cda->freq_list_lv, + sizeof(cdb->freq_list_lv)); + } else { + LOGP(DRR, LOGL_NOTICE, " before: hopping required, but " + "no mobile allocation / frequency list\n"); + } + } + + /* cell channel description */ + if (TLVP_PRESENT(&tp, GSM48_IE_CELL_CH_DESC)) { + const uint8_t *v = TLVP_VAL(&tp, GSM48_IE_CELL_CH_DESC); + uint8_t len = TLVP_LEN(&tp, GSM48_IE_CELL_CH_DESC); + + LOGP(DRR, LOGL_INFO, " both: using cell channel description " + "in case of mobile allocation\n"); + if (len + 1 > sizeof(cdb->cell_desc_lv)) { + LOGP(DRR, LOGL_ERROR, "Error: no LV space!\n"); + return -ENOMEM; + } + cdb->cell_desc_lv[0] = len; + memcpy(cdb->cell_desc_lv + 1, v, len); + cda->cell_desc_lv[0] = len; + memcpy(cda->cell_desc_lv + 1, v, len); + } else { + /* keep old */ + memcpy(cdb->cell_desc_lv, rr->cd_now.cell_desc_lv, + sizeof(cdb->cell_desc_lv)); + memcpy(cda->cell_desc_lv, rr->cd_now.cell_desc_lv, + sizeof(cda->cell_desc_lv)); + } + + /* channel mode */ + if (TLVP_PRESENT(&tp, GSM48_IE_CHANMODE_1)) { + cda->mode = cdb->mode = *TLVP_VAL(&tp, GSM48_IE_CHANMODE_1); + LOGP(DRR, LOGL_INFO, " both: changing channel mode 0x%02x\n", + cda->mode); + } else + cda->mode = cdb->mode = rr->cd_now.mode; + + /* cipher mode setting */ + if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET)) { + cda->cipher = cdb->cipher = + *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET); + LOGP(DRR, LOGL_INFO, " both: changing cipher mode 0x%02x\n", + cda->cipher); + } else + cda->cipher = cdb->cipher = rr->cd_now.cipher; + + /* power command and TA (before and after time) */ + gsm48_decode_power_cmd_acc( + (struct gsm48_power_cmd *) &ho->power_command, + &cda->ind_tx_power, &rr->hando_act); + cdb->ind_tx_power = cda->ind_tx_power; + cda->ind_ta = cdb->ind_ta = rr->cd_now.ind_ta; /* same cell */ + LOGP(DRR, LOGL_INFO, " both: (tx_power %d TA %d access=%s)\n", + cda->ind_tx_power, cda->ind_ta, + (rr->hando_act) ? "optional" : "mandatory"); + + /* check if we have to change channel at starting time */ + if (cda->start) { + int32_t now, start, diff; + + /* how much time do we have left? */ + now = ms->meas.last_fn % 42432; + start = cda->start_tm.fn % 42432; + diff = start - now; + if (diff < 0) + diff += 42432; + LOGP(DRR, LOGL_INFO, " after: (Tnow %d Tstart %d diff %d)\n", + now, start, diff); + start_mili = (uint32_t)diff * 19580 / 42432 * 10; + if (diff >= 32024 || !start_mili) { + LOGP(DRR, LOGL_INFO, " -> Start time already " + "elapsed\n"); + before_time = 0; + cda->start = 0; + } else { + LOGP(DRR, LOGL_INFO, " -> Start time is %d ms in the " + "future\n", start_mili); + } + } + + /* check if channels are valid */ + if (before_time) { + cause = gsm48_rr_render_ma(ms, cdb, ma, &ma_len); + if (cause) + return gsm48_rr_tx_hando_fail(ms, cause, RSL_MT_DATA_REQ); + } + cause = gsm48_rr_render_ma(ms, cda, ma, &ma_len); + if (cause) + return gsm48_rr_tx_hando_fail(ms, cause, RSL_MT_DATA_REQ); + + +#if 0 + if (not supported) { + LOGP(DRR, LOGL_NOTICE, "New channel is not supported.\n"); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCEPT; + } +#endif + +#ifdef TEST_FREQUENCY_MOD + LOGP(DRR, LOGL_INFO, " TESTING: frequency modify HANDO.CMD\n"); + before_time = 1; + memcpy(cdb, cda, sizeof(*cdb)); + cdb->h = 0; + cdb->arfcn = 0; +#endif + + /* schedule start of handover */ + rr->modify_state = GSM48_RR_MOD_HANDO; + if (!before_time && cda->start) { + start_rr_t_starting(rr, start_mili / 1000, start_mili % 1000); + /* when timer fires, start time is already elapsed */ + cda->start = 0; + + return 0; + } + + /* if no starting time, start suspension of current link directly */ + LOGP(DRR, LOGL_INFO, "request suspension of data link\n"); + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, nmsg); + + return 0; +} + +/* send all queued messages down to layer 2 */ +static int gsm48_rr_dequeue_down(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *msg; + + while((msg = msgb_dequeue(&rr->downqueue))) { + LOGP(DRR, LOGL_INFO, "Sending queued message.\n"); + gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg); + } + + return 0; +} + +/* channel is resumed in dedicated mode */ +static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + LOGP(DRR, LOGL_INFO, "data link is resumed\n"); + + /* transmit queued frames during ho / ass transition */ + gsm48_rr_dequeue_down(ms); + + rr->modify_state = GSM48_RR_MOD_NONE; + + return 0; +} + +/* suspend confirm in dedicated mode */ +static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + if (rr->modify_state) { + uint16_t ma[64]; + uint8_t ma_len; + + /* deactivating dedicated mode */ + LOGP(DRR, LOGL_INFO, "suspension coplete, leaving dedicated " + "mode\n"); + l1ctl_tx_dm_rel_req(ms); + ms->meas.rl_fail = 0; + rr->dm_est = 0; + l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED); + + /* store current channel descriptions */ + memcpy(&rr->cd_last, &rr->cd_now, sizeof(rr->cd_last)); + + /* copy channel description "after time" */ + memcpy(&rr->cd_now, &rr->cd_after, sizeof(rr->cd_now)); + + if (rr->cd_after.start) { + /* render channel "before time" */ + gsm48_rr_render_ma(ms, &rr->cd_before, ma, &ma_len); + + /* activate channel */ + gsm48_rr_activate_channel(ms, &rr->cd_before, ma, + ma_len); + + /* render channel "after time" */ + gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); + + /* schedule change of channel */ + gsm48_rr_channel_after_time(ms, &rr->cd_now, ma, ma_len, + rr->cd_now.start_tm.fn); + } else { + /* render channel "after time" */ + gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); + + /* activate channel */ + gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len); + } + + /* send DL-RESUME REQUEST */ + LOGP(DRR, LOGL_INFO, "request resume of data link\n"); + switch (rr->modify_state) { + case GSM48_RR_MOD_ASSIGN: + gsm48_rr_tx_ass_cpl(ms, GSM48_RR_CAUSE_NORMAL); + break; + case GSM48_RR_MOD_HANDO: + gsm48_rr_tx_hando_cpl(ms, GSM48_RR_CAUSE_NORMAL); + break; + } + +#ifdef TODO + /* trigger RACH */ + if (rr->modify_state == GSM48_RR_MOD_HANDO) { + gsm48_rr_tx_hando_access(ms); + rr->hando_acc_left = 3; + } +#endif + } + return 0; +} + +/* + * radio ressource requests + */ + +/* establish request for dedicated mode */ +static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data; + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t cause; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + uint16_t acc_class; + + /* 3.3.1.1.3.2 */ + if (bsc_timer_pending(&rr->t3122)) { + if (rrh->cause != RR_EST_CAUSE_EMERGENCY) { + LOGP(DRR, LOGL_INFO, "T3122 running, rejecting!\n"); + cause = RR_REL_CAUSE_T3122; + reject: + LOGP(DSUM, LOGL_INFO, "Establishing radio link not " + "possible\n"); + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = cause; + return gsm48_rr_upmsg(ms, nmsg); + } + LOGP(DRR, LOGL_INFO, "T3122 running, but emergency call\n"); + stop_rr_t3122(rr); + } + + /* if state is not idle */ + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DRR, LOGL_INFO, "We are not IDLE yet, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* cell selected */ + if (!cs->selected) { + LOGP(DRR, LOGL_INFO, "No cell selected, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* check if camping */ + if (cs->state != GSM322_C3_CAMPED_NORMALLY + && rrh->cause != RR_EST_CAUSE_EMERGENCY) { + LOGP(DRR, LOGL_INFO, "Not camping normally, rejecting! " + "(cs->state = %d)\n", cs->state); + cause = RR_REL_CAUSE_EMERGENCY_ONLY; + goto reject; + } + if (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL) { + LOGP(DRR, LOGL_INFO, "Not camping, rejecting! " + "(cs->state = %d)\n", cs->state); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* check for relevant informations */ + if (!s->si3) { + LOGP(DRR, LOGL_INFO, "Not enough SI, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* 3.3.1.1.1 */ + if (!subscr->acc_barr && s->cell_barr) { + LOGP(DRR, LOGL_INFO, "Cell barred, rejecting!\n"); + cause = RR_REL_CAUSE_NOT_AUTHORIZED; + goto reject; + } + if (rrh->cause == RR_EST_CAUSE_EMERGENCY) + acc_class = subscr->acc_class | 0x0400; + else + acc_class = subscr->acc_class & 0xfbff; + if (!subscr->acc_barr && !(acc_class & (s->class_barr ^ 0xffff))) { + LOGP(DRR, LOGL_INFO, "Cell barred for our access class (access " + "%04x barred %04x)!\n", acc_class, s->class_barr); + cause = RR_REL_CAUSE_NOT_AUTHORIZED; + goto reject; + } + + /* requested by RR */ + rr->rr_est_req = 1; + + /* clone and store REQUEST message */ + if (!gh) { + LOGP(DRR, LOGL_ERROR, "Error, missing l3 message\n"); + return -EINVAL; + } + rr->rr_est_msg = gsm48_l3_msgb_alloc(); + if (!rr->rr_est_msg) + return -ENOMEM; + memcpy(msgb_put(rr->rr_est_msg, msgb_l3len(msg)), + msgb_l3(msg), msgb_l3len(msg)); + + /* request channel */ + return gsm48_rr_chan_req(ms, rrh->cause, 0); +} + +/* 3.4.2 transfer data in dedicated mode */ +static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + if (rr->state != GSM48_RR_ST_DEDICATED) { + msgb_free(msg); + return -EINVAL; + } + + /* pull RR header */ + msgb_pull(msg, sizeof(struct gsm48_rr_hdr)); + + /* set sequence number and increment */ + gsm48_apply_v_sd(rr, msg); + + /* queue message, during handover or assignment procedure */ + if (rr->modify_state == GSM48_RR_MOD_ASSIGN + || rr->modify_state == GSM48_RR_MOD_HANDO) { + LOGP(DRR, LOGL_INFO, "Queueing message during suspend.\n"); + msgb_enqueue(&rr->downqueue, msg); + return 0; + } + + /* forward message */ + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg); +} + +/* + * data indications from data link + */ + +/* 3.4.2 data from layer 2 to RR and upper layer*/ +static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_rr_hdr *rrh; + uint8_t pdisc = gh->proto_discr & 0x0f; + + if (pdisc == GSM48_PDISC_RR) { + int rc = -EINVAL; + uint8_t skip_ind = (gh->proto_discr & 0xf0) >> 4; + + /* ignore if skip indicator is not B'0000' */ + if (skip_ind) + return 0; + + switch(gh->msg_type) { + case GSM48_MT_RR_ADD_ASS: + rc = gsm48_rr_rx_add_ass(ms, msg); + break; + case GSM48_MT_RR_ASS_CMD: + rc = gsm48_rr_rx_ass_cmd(ms, msg); + break; + case GSM48_MT_RR_CIPH_M_CMD: + rc = gsm48_rr_rx_cip_mode_cmd(ms, msg); + break; + case GSM48_MT_RR_CLSM_ENQ: + rc = gsm48_rr_rx_cm_enq(ms, msg); + break; + case GSM48_MT_RR_CHAN_MODE_MODIF: + rc = gsm48_rr_rx_chan_modify(ms, msg); + break; + case GSM48_MT_RR_HANDO_CMD: + rc = gsm48_rr_rx_hando_cmd(ms, msg); + break; + case GSM48_MT_RR_FREQ_REDEF: + rc = gsm48_rr_rx_frq_redef(ms, msg); + break; + case GSM48_MT_RR_CHAN_REL: + rc = gsm48_rr_rx_chan_rel(ms, msg); + break; + case GSM48_MT_RR_APP_INFO: + LOGP(DRR, LOGL_NOTICE, "APP INFO not supported!\n"); + break; + default: + LOGP(DRR, LOGL_NOTICE, "Message type 0x%02x unknown.\n", + gh->msg_type); + + /* status message */ + gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N); + } + + msgb_free(msg); + return rc; + } + + /* pull off RSL header up to L3 message */ + msgb_pull(msg, (long)msgb_l3(msg) - (long)msg->data); + + /* push RR header */ + msgb_push(msg, sizeof(struct gsm48_rr_hdr)); + rrh = (struct gsm48_rr_hdr *)msg->data; + rrh->msg_type = GSM48_RR_DATA_IND; + + return gsm48_rr_upmsg(ms, msg); +} + +/* receive BCCH at RR layer */ +static int gsm48_rr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_header *sih = msgb_l3(msg); + + switch (sih->system_information) { + case GSM48_MT_RR_SYSINFO_1: + return gsm48_rr_rx_sysinfo1(ms, msg); + case GSM48_MT_RR_SYSINFO_2: + return gsm48_rr_rx_sysinfo2(ms, msg); + case GSM48_MT_RR_SYSINFO_2bis: + return gsm48_rr_rx_sysinfo2bis(ms, msg); + case GSM48_MT_RR_SYSINFO_2ter: + return gsm48_rr_rx_sysinfo2ter(ms, msg); + case GSM48_MT_RR_SYSINFO_3: + return gsm48_rr_rx_sysinfo3(ms, msg); + case GSM48_MT_RR_SYSINFO_4: + return gsm48_rr_rx_sysinfo4(ms, msg); + default: +#if 0 + LOGP(DRR, LOGL_NOTICE, "BCCH message type 0x%02x not sup.\n", + sih->system_information); +#endif + return -EINVAL; + } +} + +/* receive CCCH at RR layer */ +static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_header *sih = msgb_l3(msg); + + switch (sih->system_information) { + case GSM48_MT_RR_PAG_REQ_1: + return gsm48_rr_rx_pag_req_1(ms, msg); + case GSM48_MT_RR_PAG_REQ_2: + return gsm48_rr_rx_pag_req_2(ms, msg); + case GSM48_MT_RR_PAG_REQ_3: + return gsm48_rr_rx_pag_req_3(ms, msg); + + case GSM48_MT_RR_IMM_ASS: + return gsm48_rr_rx_imm_ass(ms, msg); + case GSM48_MT_RR_IMM_ASS_EXT: + return gsm48_rr_rx_imm_ass_ext(ms, msg); + case GSM48_MT_RR_IMM_ASS_REJ: + return gsm48_rr_rx_imm_ass_rej(ms, msg); + default: +#if 0 + LOGP(DRR, LOGL_NOTICE, "CCCH message type 0x%02x unknown.\n", + sih->system_information); +#endif + return -EINVAL; + } +} + +/* receive ACCH at RR layer */ +static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_settings *set = &ms->settings; + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + struct gsm48_system_information_type_header *sih = msgb_l3(msg); + uint8_t ind_ta, ind_tx_power; + + if (msgb_l2len(msg) < sizeof(*rllh) + 2 + 2) { + LOGP(DRR, LOGL_ERROR, "Missing TA and TX_POWER IEs\n"); + return -EINVAL; + } + + ind_ta = rllh->data[1]; + ind_tx_power = rllh->data[3]; + LOGP(DRR, LOGL_INFO, "Indicated ta %d (actual ta %d)\n", + ind_ta, ind_ta - set->alter_delay); + LOGP(DRR, LOGL_INFO, "Indicated tx_power %d\n", + ind_tx_power); + if (ind_ta != rr->cd_now.ind_ta + || ind_tx_power != rr->cd_now.ind_tx_power) { + LOGP(DRR, LOGL_INFO, "setting new ta and tx_power\n"); + l1ctl_tx_param_req(ms, ind_ta - set->alter_delay, + (set->alter_tx_power) ? set->alter_tx_power_value + : ind_tx_power); + rr->cd_now.ind_ta = ind_ta; + rr->cd_now.ind_tx_power = ind_tx_power; + } + + switch (sih->system_information) { + case GSM48_MT_RR_SYSINFO_5: + return gsm48_rr_rx_sysinfo5(ms, msg); + case GSM48_MT_RR_SYSINFO_5bis: + return gsm48_rr_rx_sysinfo5bis(ms, msg); + case GSM48_MT_RR_SYSINFO_5ter: + return gsm48_rr_rx_sysinfo5ter(ms, msg); + case GSM48_MT_RR_SYSINFO_6: + return gsm48_rr_rx_sysinfo6(ms, msg); + default: + LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", + sih->system_information); + return -EINVAL; + } +} + +/* unit data from layer 2 to RR layer */ +static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + struct tlv_parsed tv; + uint8_t ch_type, ch_subch, ch_ts; + + DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", + rllh->chan_nr, rllh->link_id); + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n"); + return -EIO; + } + msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); + + if (cs->ccch_state != GSM322_CCCH_ST_SYNC + && cs->ccch_state != GSM322_CCCH_ST_DATA) + return -EINVAL; + + /* temporary moved here until confirm is fixed */ + if (cs->ccch_state != GSM322_CCCH_ST_DATA) { + LOGP(DCS, LOGL_INFO, "Channel provides data.\n"); + cs->ccch_state = GSM322_CCCH_ST_DATA; + + /* in dedicated mode */ + if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND) + return gsm48_rr_tx_rand_acc(ms, NULL); + + /* set timer for reading BCCH */ + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C1_NORMAL_CELL_SEL + || cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C4_NORMAL_CELL_RESEL + || cs->state == GSM322_C8_ANY_CELL_RESEL + || cs->state == GSM322_C5_CHOOSE_CELL + || cs->state == GSM322_C9_CHOOSE_ANY_CELL + || cs->state == GSM322_PLMN_SEARCH + || cs->state == GSM322_HPLMN_SEARCH) + start_cs_timer(cs, ms->support.scan_to, 0); + // TODO: timer depends on BCCH config + } + + rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts); + switch (ch_type) { + case RSL_CHAN_PCH_AGCH: + return gsm48_rr_rx_pch_agch(ms, msg); + case RSL_CHAN_BCCH: + return gsm48_rr_rx_bcch(ms, msg); + case RSL_CHAN_Bm_ACCHs: + case RSL_CHAN_Lm_ACCHs: + case RSL_CHAN_SDCCH4_ACCH: + case RSL_CHAN_SDCCH8_ACCH: + return gsm48_rr_rx_acch(ms, msg); + default: + LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n", + rllh->chan_nr); + return -EINVAL; + } +} + +/* 3.4.13.3 RR abort in dedicated mode (also in conn. pending mode) */ +static int gsm48_rr_abort_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + uint8_t *mode; + + /* stop pending RACH timer */ + stop_rr_t3126(rr); + + /* release "normally" if we are in dedicated mode */ + if (rr->state == GSM48_RR_ST_DEDICATED) { + struct msgb *nmsg; + + LOGP(DRR, LOGL_INFO, "Abort in dedicated state, send release " + "to layer 2.\n"); + + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* release message */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = 0; /* normal release */ + return gsm48_send_rsl_rel(ms, RSL_MT_REL_REQ, nmsg); + } + + LOGP(DRR, LOGL_INFO, "Abort in connection pending state, return to " + "idle state.\n"); + /* return idle */ + new_rr_state(rr, GSM48_RR_ST_IDLE); + + return 0; +} + +/* release confirm */ +static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + uint8_t cause = RR_REL_CAUSE_NORMAL; + uint16_t ma[64]; + uint8_t ma_len; + + /* switch back to old channel, if modify/ho failed */ + switch (rr->modify_state) { + case GSM48_RR_MOD_ASSIGN: + case GSM48_RR_MOD_HANDO: + /* deactivate channel */ + l1ctl_tx_dm_rel_req(ms); + ms->meas.rl_fail = 0; + rr->dm_est = 0; + l1ctl_tx_reset_req(ms, L1CTL_RES_T_SCHED); + + /* get old channel description */ + memcpy(&rr->cd_now, &rr->cd_last, sizeof(rr->cd_now)); + + /* render and change radio to old channel */ + gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); + gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len); + + /* re-establish old link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + if (rr->modify_state == GSM48_RR_MOD_ASSIGN) { + rr->modify_state = GSM48_RR_MOD_ASSIGN_RESUME; + return gsm48_rr_tx_ass_fail(ms, + GSM48_RR_CAUSE_ABNORMAL_UNSPEC, + RSL_MT_RECON_REQ); + } else { + rr->modify_state = GSM48_RR_MOD_HANDO_RESUME; + return gsm48_rr_tx_hando_fail(ms, + GSM48_RR_CAUSE_ABNORMAL_UNSPEC, + RSL_MT_RECON_REQ); + } + /* returns above */ + case GSM48_RR_MOD_ASSIGN_RESUME: + case GSM48_RR_MOD_HANDO_RESUME: + rr->modify_state = GSM48_RR_MOD_NONE; + cause = RR_REL_CAUSE_LINK_FAILURE; + break; + } + + LOGP(DSUM, LOGL_INFO, "Requested channel aborted\n"); + + /* stop T3211 if running */ + stop_rr_t3110(rr); + + /* send release indication */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = cause; + gsm48_rr_upmsg(ms, nmsg); + + /* return idle */ + new_rr_state(rr, GSM48_RR_ST_IDLE); + return 0; +} + +/* MDL-ERROR */ +static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + uint8_t *mode; + uint8_t cause = rllh->data[2]; + + switch (cause) { + case RLL_CAUSE_SEQ_ERR: + case RLL_CAUSE_UNSOL_DM_RESP_MF: + break; + default: + LOGP(DRR, LOGL_NOTICE, "MDL-Error (cause %d) ignoring\n", + cause); + } + + LOGP(DRR, LOGL_NOTICE, "MDL-Error (cause %d) aborting\n", cause); + + /* disconnect the main signalling link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = 1; /* local release */ + gsm48_send_rsl_rel(ms, RSL_MT_REL_REQ, nmsg); + + /* in case of modify/hando: wait for confirm */ + if (rr->modify_state) + return 0; + + /* send abort ind to upper layer */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = RR_REL_CAUSE_LINK_FAILURE; + gsm48_rr_upmsg(ms, nmsg); + + /* return idle */ + new_rr_state(rr, GSM48_RR_ST_IDLE); + return 0; +} + +/* + * state machines + */ + +/* state trasitions for link layer messages (lower layer) */ +static struct dldatastate { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} dldatastatelist[] = { + /* data transfer */ + {SBIT(GSM48_RR_ST_IDLE) | + SBIT(GSM48_RR_ST_CONN_PEND) | + SBIT(GSM48_RR_ST_DEDICATED) | + SBIT(GSM48_RR_ST_REL_PEND), + RSL_MT_UNIT_DATA_IND, gsm48_rr_unit_data_ind}, + + {SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */ + RSL_MT_DATA_IND, gsm48_rr_data_ind}, + + /* esablish */ + {SBIT(GSM48_RR_ST_IDLE) | + SBIT(GSM48_RR_ST_CONN_PEND) | + SBIT(GSM48_RR_ST_REL_PEND), + RSL_MT_EST_CONF, gsm48_rr_estab_cnf}, + + /* resume */ + {SBIT(GSM48_RR_ST_DEDICATED), + RSL_MT_EST_CONF, gsm48_rr_estab_cnf_dedicated}, + + /* release */ + {SBIT(GSM48_RR_ST_CONN_PEND) | + SBIT(GSM48_RR_ST_DEDICATED), + RSL_MT_REL_IND, gsm48_rr_rel_ind}, + + {SBIT(GSM48_RR_ST_REL_PEND), + RSL_MT_REL_CONF, gsm48_rr_rel_cnf}, + + /* reconnect */ + {SBIT(GSM48_RR_ST_CONN_PEND) | + SBIT(GSM48_RR_ST_DEDICATED), + RSL_MT_REL_CONF, gsm48_rr_rel_cnf}, + + /* suspenion */ + {SBIT(GSM48_RR_ST_DEDICATED), + RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated}, + +#if 0 + {SBIT(GSM48_RR_ST_DEDICATED), + RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated}, +#endif + + {SBIT(GSM48_RR_ST_DEDICATED), + RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind}, +}; + +#define DLDATASLLEN \ + (sizeof(dldatastatelist) / sizeof(struct dldatastate)) + +static int gsm48_rcv_rll(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + int msg_type = rllh->c.msg_type; + int i; + int rc; + + if (msg_type != RSL_MT_UNIT_DATA_IND) { + LOGP(DRSL, LOGL_INFO, "(ms %s) Received '%s' from L2 in state " + "%s\n", ms->name, get_rsl_name(msg_type), + gsm48_rr_state_names[rr->state]); + } + + /* find function for current state and message */ + for (i = 0; i < DLDATASLLEN; i++) + if ((msg_type == dldatastatelist[i].type) + && ((1 << rr->state) & dldatastatelist[i].states)) + break; + if (i == DLDATASLLEN) { + LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n"); + msgb_free(msg); + return 0; + } + + rc = dldatastatelist[i].rout(ms, msg); + + /* free msgb unless it is forwarded */ + if (dldatastatelist[i].rout != gsm48_rr_data_ind) + msgb_free(msg); + + return rc; +} + +static int gsm48_rcv_cch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct abis_rsl_cchan_hdr *ch = msgb_l2(msg); + int msg_type = ch->c.msg_type; + int rc; + + LOGP(DRSL, LOGL_INFO, "(ms %s) Received '%s' from L2 in state " + "%s\n", ms->name, get_rsl_name(msg_type), + gsm48_rr_state_names[rr->state]); + + if (rr->state == GSM48_RR_ST_CONN_PEND + && msg_type == RSL_MT_CHAN_CONF) { + rc = gsm48_rr_tx_rand_acc(ms, msg); + msgb_free(msg); + return rc; + } + + LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n"); + msgb_free(msg); + return 0; +} + + +/* input function for L2 messags up to L3 */ +static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + int rc = 0; + + switch (rslh->msg_discr & 0xfe) { + case ABIS_RSL_MDISC_RLL: + rc = gsm48_rcv_rll(ms, msg); + break; + case ABIS_RSL_MDISC_COM_CHAN: + rc = gsm48_rcv_cch(ms, msg); + break; + default: + /* FIXME: implement this */ + LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n", + rslh->msg_discr); + msgb_free(msg); + rc = -EINVAL; + break; + } + + return rc; +} + +/* state trasitions for RR-SAP messages from up */ +static struct rrdownstate { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} rrdownstatelist[] = { + /* NOTE: If not IDLE, it is rejected there. */ + {ALL_STATES, /* 3.3.1.1 */ + GSM48_RR_EST_REQ, gsm48_rr_est_req}, + + {SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */ + GSM48_RR_DATA_REQ, gsm48_rr_data_req}, + + {SBIT(GSM48_RR_ST_CONN_PEND) | + SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.13.3 */ + GSM48_RR_ABORT_REQ, gsm48_rr_abort_req}, +}; + +#define RRDOWNSLLEN \ + (sizeof(rrdownstatelist) / sizeof(struct rrdownstate)) + +int gsm48_rr_downmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data; + int msg_type = rrh->msg_type; + int i; + int rc; + + LOGP(DRR, LOGL_INFO, "(ms %s) Message '%s' received in state %s\n", + ms->name, get_rr_name(msg_type), + gsm48_rr_state_names[rr->state]); + + /* find function for current state and message */ + for (i = 0; i < RRDOWNSLLEN; i++) + if ((msg_type == rrdownstatelist[i].type) + && ((1 << rr->state) & rrdownstatelist[i].states)) + break; + if (i == RRDOWNSLLEN) { + LOGP(DRR, LOGL_NOTICE, "Message unhandled at this state.\n"); + msgb_free(msg); + return 0; + } + + rc = rrdownstatelist[i].rout(ms, msg); + + /* free msgb uless it is forwarded */ + if (rrdownstatelist[i].rout != gsm48_rr_data_req) + msgb_free(msg); + + return rc; +} + +/* + * init/exit + */ + +int gsm48_rr_init(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + memset(rr, 0, sizeof(*rr)); + rr->ms = ms; + + LOGP(DRR, LOGL_INFO, "init Radio Ressource process\n"); + + INIT_LLIST_HEAD(&rr->rsl_upqueue); + INIT_LLIST_HEAD(&rr->downqueue); + /* downqueue is handled here, so don't add_work */ + + osmol2_register_handler(ms, &gsm48_rx_rsl); + + start_rr_t_meas(rr, 1, 0); + + return 0; +} + +int gsm48_rr_exit(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *msg; + + LOGP(DRR, LOGL_INFO, "exit Radio Ressource process\n"); + + /* flush queues */ + while ((msg = msgb_dequeue(&rr->rsl_upqueue))) + msgb_free(msg); + while ((msg = msgb_dequeue(&rr->downqueue))) + msgb_free(msg); + + if (rr->rr_est_msg) { + msgb_free(rr->rr_est_msg); + rr->rr_est_msg = NULL; + } + + stop_rr_t_meas(rr); + stop_rr_t_starting(rr); + stop_rr_t_rel_wait(rr); + stop_rr_t3110(rr); + stop_rr_t3122(rr); + stop_rr_t3124(rr); + stop_rr_t3126(rr); + + return 0; +} + +#if 0 + +todo rr_sync_ind when receiving ciph, re ass, channel mode modify + + +static void timeout_rr_t3124(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct msgb *nmsg; + + /* stop sending more access bursts when timer expired */ + hando_acc_left = 0; + + /* get old channel description */ + memcpy(&rr->chan_desc, &rr->chan_last, sizeof(rr->chan_desc)); + + /* change radio to old channel */ + tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr, + rr->cd_now.tsc); + rr->dm_est = 1; + + /* re-establish old link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + return gsm48_send_rsl(ms, RSL_MT_REEST_REQ, nmsg); + + todo +} + +/* send HANDOVER ACCESS burst (9.1.14) */ +static int gsm48_rr_tx_hando_access(struct osmocom_ms *ms) +{ + nmsg = msgb_alloc_headroom(20, 16, "HAND_ACCESS"); + if (!nmsg) + return -ENOMEM; + *msgb_put(nmsg, 1) = rr->hando_ref; + todo burst + return gsm48_send_rsl(ms, RSL_MT_RAND_ACC_REQ, nmsg); +} + +/* send next channel request in dedicated state */ +static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + int s; + + if (rr->modify_state != GSM48_RR_MOD_HANDO) { + LOGP(DRR, LOGL_NOTICE, "Random acces confirm, but not in handover state.\n"); + return 0; + } + + /* send up to four handover access bursts */ + if (rr->hando_acc_left) { + rr->hando_acc_left--; + gsm48_rr_tx_hando_access(ms); + return; + } + + /* start timer for sending next HANDOVER ACCESS bursts afterwards */ + if (!bsc_timer_pending(&rr->t3124)) { + if (allocated channel is SDCCH) + start_rr_t3124(rr, GSM_T3124_675); + else + start_rr_t3124(rr, GSM_T3124_320); + } + if (!rr->n_chan_req) { + start_rr_t3126(rr, 5, 0); /* TODO improve! */ + return 0; + } + rr->n_chan_req--; + + /* wait for PHYSICAL INFORMATION message or T3124 timeout */ + return 0; + +} + +#endif + diff --git a/src/host/layer23/src/mobile/main.c b/src/host/layer23/src/mobile/main.c new file mode 100644 index 00000000..87aa4c6e --- /dev/null +++ b/src/host/layer23/src/mobile/main.c @@ -0,0 +1,193 @@ +/* Main method of the layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/mobile/app_mobile.h> + +#include <osmocore/talloc.h> +#include <osmocore/linuxlist.h> +#include <osmocore/gsmtap_util.h> +#include <osmocore/signal.h> + +#include <arpa/inet.h> + +#define _GNU_SOURCE +#include <getopt.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <time.h> + +struct log_target *stderr_target; + +void *l23_ctx = NULL; +static const char *config_file = "/etc/osmocom/osmocom.cfg"; +struct llist_head ms_list; +static uint32_t gsmtap_ip = 0; +unsigned short vty_port = 4247; +int debug_set = 0; + +int mobile_delete(struct osmocom_ms *ms, int force); +int mobile_signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data); +int mobile_work(struct osmocom_ms *ms); +int mobile_exit(struct osmocom_ms *ms, int force); + + +const char *openbsc_copyright = + "Copyright (C) 2008-2010 ...\n" + "Contributions by ...\n\n" + "License GPLv2+: GNU GPL version 2 or later " + "<http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n"; + +static void print_usage(const char *app) +{ + printf("Usage: %s\n", app); +} + +static void print_help() +{ + printf(" Some help...\n"); + printf(" -h --help this text\n"); + printf(" -i --gsmtap-ip The destination IP used for GSMTAP.\n"); + printf(" -v --vty-port The VTY port number to telnet to. " + "(default %u)\n", vty_port); + printf(" -d --debug Change debug flags.\n"); +} + +static void handle_options(int argc, char **argv) +{ + struct sockaddr_in gsmtap; + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"gsmtap-ip", 1, 0, 'i'}, + {"vty-port", 1, 0, 'v'}, + {"debug", 1, 0, 'd'}, + {0, 0, 0, 0}, + }; + + c = getopt_long(argc, argv, "hi:v:d:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(argv[0]); + print_help(); + exit(0); + break; + case 'i': + if (!inet_aton(optarg, &gsmtap.sin_addr)) { + perror("inet_aton"); + exit(2); + } + gsmtap_ip = ntohl(gsmtap.sin_addr.s_addr); + break; + case 'v': + vty_port = atoi(optarg); + break; + case 'd': + log_parse_category_mask(stderr_target, optarg); + debug_set = 1; + break; + default: + break; + } + } +} + +void sighandler(int sigset) +{ + if (sigset == SIGHUP || sigset == SIGPIPE) + return; + + fprintf(stderr, "Signal %d recevied.\n", sigset); + + /* in case there is a lockup during exit */ + signal(SIGINT, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + + dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL); +} + +int main(int argc, char **argv) +{ + int quit = 0; + int rc; + + printf("%s\n", openbsc_copyright); + + srand(time(NULL)); + + INIT_LLIST_HEAD(&ms_list); + log_init(&log_info); + stderr_target = log_target_create_stderr(); + log_add_target(stderr_target); + log_set_all_filter(stderr_target, 1); + + l23_ctx = talloc_named_const(NULL, 1, "layer2 context"); + + handle_options(argc, argv); + + if (!debug_set) + log_parse_category_mask(stderr_target, "DCS:DPLMN:DRR:DMM:DSIM:DCC:DMNCC:DPAG:DSUM"); + log_set_log_level(stderr_target, LOGL_INFO); + + if (gsmtap_ip) { + rc = gsmtap_init(gsmtap_ip); + if (rc < 0) { + fprintf(stderr, "Failed during gsmtap_init()\n"); + exit(1); + } + } + + rc = l23_app_init(NULL, config_file, vty_port); + if (rc) + exit(rc); + + signal(SIGINT, sighandler); + signal(SIGHUP, sighandler); + signal(SIGTERM, sighandler); + signal(SIGPIPE, sighandler); + + while (1) { + l23_app_work(&quit); + if (quit && llist_empty(&ms_list)) + break; + bsc_select_main(0); + } + + l23_app_exit(); + + return 0; +} diff --git a/src/host/layer23/src/mobile/mnccms.c b/src/host/layer23/src/mobile/mnccms.c new file mode 100644 index 00000000..6997993e --- /dev/null +++ b/src/host/layer23/src/mobile/mnccms.c @@ -0,0 +1,777 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <osmocore/talloc.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/mobile/mncc.h> +#include <osmocom/bb/mobile/vty.h> + +void *l23_ctx; +static uint32_t new_callref = 1; +static LLIST_HEAD(call_list); + +void mncc_set_cause(struct gsm_mncc *data, int loc, int val); +static int dtmf_statemachine(struct gsm_call *call, struct gsm_mncc *mncc); +static void timeout_dtmf(void *arg); +int mncc_answer(struct osmocom_ms *ms); + +/* + * support functions + */ + +/* DTMF timer */ +static void start_dtmf_timer(struct gsm_call *call, uint16_t ms) +{ + LOGP(DCC, LOGL_INFO, "starting DTMF timer %d ms\n", ms); + call->dtmf_timer.cb = timeout_dtmf; + call->dtmf_timer.data = call; + bsc_schedule_timer(&call->dtmf_timer, 0, ms * 1000); +} + +static void stop_dtmf_timer(struct gsm_call *call) +{ + if (bsc_timer_pending(&call->dtmf_timer)) { + LOGP(DCC, LOGL_INFO, "stopping pending DTMF timer\n"); + bsc_del_timer(&call->dtmf_timer); + } +} + +/* free call instance */ +static void free_call(struct gsm_call *call) +{ + stop_dtmf_timer(call); + + llist_del(&call->entry); + DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref); + talloc_free(call); +} + + +struct gsm_call *get_call_ref(uint32_t callref) +{ + struct gsm_call *callt; + + llist_for_each_entry(callt, &call_list, entry) { + if (callt->callref == callref) + return callt; + } + return NULL; +} + +static int8_t mncc_get_bearer(struct gsm_settings *set, uint8_t speech_ver) +{ + switch (speech_ver) { + case 4: + if (set->full_v3) + LOGP(DMNCC, LOGL_INFO, " net suggests full rate v3\n"); + else { + LOGP(DMNCC, LOGL_INFO, " full rate v3 not supported\n"); + speech_ver = -1; + } + break; + case 2: + if (set->full_v2) + LOGP(DMNCC, LOGL_INFO, " net suggests full rate v2\n"); + else { + LOGP(DMNCC, LOGL_INFO, " full rate v2 not supported\n"); + speech_ver = -1; + } + break; + case 0: /* mandatory */ + if (set->full_v1) + LOGP(DMNCC, LOGL_INFO, " net suggests full rate v1\n"); + else { + LOGP(DMNCC, LOGL_INFO, " full rate v1 not supported\n"); + speech_ver = -1; + } + break; + case 5: + if (set->half_v3) + LOGP(DMNCC, LOGL_INFO, " net suggests half rate v3\n"); + else { + LOGP(DMNCC, LOGL_INFO, " half rate v3 not supported\n"); + speech_ver = -1; + } + break; + case 1: + if (set->half_v1) + LOGP(DMNCC, LOGL_INFO, " net suggests half rate v1\n"); + else { + LOGP(DMNCC, LOGL_INFO, " half rate v1 not supported\n"); + speech_ver = -1; + } + break; + default: + LOGP(DMNCC, LOGL_INFO, " net suggests unknown speech version " + "%d\n", speech_ver); + speech_ver = -1; + } + + return speech_ver; +} + +static void mncc_set_bearer(struct osmocom_ms *ms, int8_t speech_ver, + struct gsm_mncc *mncc) +{ + struct gsm_settings *set = &ms->settings; + int i = 0; + + mncc->fields |= MNCC_F_BEARER_CAP; + mncc->bearer_cap.coding = 0; + if (set->ch_cap == GSM_CAP_SDCCH_TCHF_TCHH + && (set->half_v1 || set->half_v3)) { + mncc->bearer_cap.radio = 3; + LOGP(DMNCC, LOGL_INFO, " support TCH/H also\n"); + } else { + mncc->bearer_cap.radio = 1; + LOGP(DMNCC, LOGL_INFO, " support TCH/F only\n"); + } + mncc->bearer_cap.speech_ctm = 0; + /* if no specific speech_ver is given */ + if (speech_ver < 0) { + /* if half rate is supported and prefered */ + if (set->half_v3 && set->half && set->half_prefer) { + mncc->bearer_cap.speech_ver[i++] = 5; + LOGP(DMNCC, LOGL_INFO, " support half rate v3\n"); + } + if (set->half_v1 && set->half && set->half_prefer) { + mncc->bearer_cap.speech_ver[i++] = 1; + LOGP(DMNCC, LOGL_INFO, " support half rate v1\n"); + } + /* if full rate is supported */ + if (set->full_v3) { + mncc->bearer_cap.speech_ver[i++] = 4; + LOGP(DMNCC, LOGL_INFO, " support full rate v3\n"); + } + if (set->full_v2) { + mncc->bearer_cap.speech_ver[i++] = 2; + LOGP(DMNCC, LOGL_INFO, " support full rate v2\n"); + } + if (set->full_v1) { /* mandatory, so it's always true */ + mncc->bearer_cap.speech_ver[i++] = 0; + LOGP(DMNCC, LOGL_INFO, " support full rate v1\n"); + } + /* if half rate is supported and not prefered */ + if (set->half_v3 && set->half && !set->half_prefer) { + mncc->bearer_cap.speech_ver[i++] = 5; + LOGP(DMNCC, LOGL_INFO, " support half rate v3\n"); + } + if (set->half_v1 && set->half && !set->half_prefer) { + mncc->bearer_cap.speech_ver[i++] = 1; + LOGP(DMNCC, LOGL_INFO, " support half rate v1\n"); + } + /* if specific speech_ver is given (it must be supported) */ + } else + mncc->bearer_cap.speech_ver[i++] = speech_ver; + mncc->bearer_cap.speech_ver[i] = -1; /* end of list */ + mncc->bearer_cap.transfer = 0; + mncc->bearer_cap.mode = 0; +} + +/* + * MNCCms dummy application + */ + +/* this is a minimal implementation as required by GSM 04.08 */ +int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg) +{ + struct gsm_mncc *data = arg; + uint32_t callref = data->callref; + struct gsm_mncc rel; + + if (msg_type == MNCC_REL_IND || msg_type == MNCC_REL_CNF) + return 0; + + LOGP(DMNCC, LOGL_INFO, "Rejecting incoming call\n"); + + /* reject, as we don't support Calls */ + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = callref; + mncc_set_cause(&rel, GSM48_CAUSE_LOC_USER, + GSM48_CC_CAUSE_INCOMPAT_DEST); + + return mncc_send(ms, MNCC_REL_REQ, &rel); +} + +/* + * MNCCms basic call application + */ + +int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) +{ + struct gsm_settings *set = &ms->settings; + struct gsm_mncc *data = arg; + struct gsm_call *call = get_call_ref(data->callref); + struct gsm_mncc mncc; + uint8_t cause; + int8_t speech_ver = -1, speech_ver_half = -1, temp; + int first_call = 0; + + /* call does not exist */ + if (!call && msg_type != MNCC_SETUP_IND) { + LOGP(DMNCC, LOGL_INFO, "Rejecting incoming call " + "(callref %x)\n", data->callref); + if (msg_type == MNCC_REL_IND || msg_type == MNCC_REL_CNF) + return 0; + cause = GSM48_CC_CAUSE_INCOMPAT_DEST; + release: + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = data->callref; + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_USER, cause); + return mncc_send(ms, MNCC_REL_REQ, &mncc); + } + + /* setup without call */ + if (!call) { + if (llist_empty(&call_list)) + first_call = 1; + call = talloc_zero(l23_ctx, struct gsm_call); + if (!call) + return -ENOMEM; + call->ms = ms; + call->callref = data->callref; + llist_add_tail(&call->entry, &call_list); + } + + /* not in initiated state anymore */ + call->init = 0; + + switch (msg_type) { + case MNCC_DISC_IND: + vty_notify(ms, NULL); + switch (data->cause.value) { + case GSM48_CC_CAUSE_UNASSIGNED_NR: + vty_notify(ms, "Call: Number not assigned\n"); + break; + case GSM48_CC_CAUSE_NO_ROUTE: + vty_notify(ms, "Call: Destination unreachable\n"); + break; + case GSM48_CC_CAUSE_NORM_CALL_CLEAR: + vty_notify(ms, "Call: Remote hangs up\n"); + break; + case GSM48_CC_CAUSE_USER_BUSY: + vty_notify(ms, "Call: Remote busy\n"); + break; + case GSM48_CC_CAUSE_USER_NOTRESPOND: + vty_notify(ms, "Call: Remote not responding\n"); + break; + case GSM48_CC_CAUSE_USER_ALERTING_NA: + vty_notify(ms, "Call: Remote not answering\n"); + break; + case GSM48_CC_CAUSE_CALL_REJECTED: + vty_notify(ms, "Call has been rejected\n"); + break; + case GSM48_CC_CAUSE_NUMBER_CHANGED: + vty_notify(ms, "Call: Number changed\n"); + break; + case GSM48_CC_CAUSE_PRE_EMPTION: + vty_notify(ms, "Call: Cleared due to pre-emption\n"); + break; + case GSM48_CC_CAUSE_DEST_OOO: + vty_notify(ms, "Call: Remote out of order\n"); + break; + case GSM48_CC_CAUSE_INV_NR_FORMAT: + vty_notify(ms, "Call: Number invalid or imcomplete\n"); + break; + case GSM48_CC_CAUSE_NO_CIRCUIT_CHAN: + vty_notify(ms, "Call: No channel available\n"); + break; + case GSM48_CC_CAUSE_NETWORK_OOO: + vty_notify(ms, "Call: Network out of order\n"); + break; + case GSM48_CC_CAUSE_TEMP_FAILURE: + vty_notify(ms, "Call: Temporary failure\n"); + break; + case GSM48_CC_CAUSE_SWITCH_CONG: + vty_notify(ms, "Congestion\n"); + break; + default: + vty_notify(ms, "Call has been disconnected " + "(clear cause %d)\n", data->cause.value); + } + LOGP(DMNCC, LOGL_INFO, "Call has been disconnected " + "(cause %d)\n", data->cause.value); + if ((data->fields & MNCC_F_PROGRESS) + && data->progress.descr == 8) { + vty_notify(ms, "Please hang up!\n"); + break; + } + free_call(call); + cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR; + goto release; + case MNCC_REL_IND: + case MNCC_REL_CNF: + vty_notify(ms, NULL); + if (data->cause.value == GSM48_CC_CAUSE_CALL_REJECTED) + vty_notify(ms, "Call has been rejected\n"); + else + vty_notify(ms, "Call has been released\n"); + LOGP(DMNCC, LOGL_INFO, "Call has been released (cause %d)\n", + data->cause.value); + free_call(call); + break; + case MNCC_CALL_PROC_IND: + vty_notify(ms, NULL); + vty_notify(ms, "Call is proceeding\n"); + LOGP(DMNCC, LOGL_INFO, "Call is proceeding\n"); + if ((data->fields & MNCC_F_BEARER_CAP) + && data->bearer_cap.speech_ver[0] >= 0) { + mncc_get_bearer(set, data->bearer_cap.speech_ver[0]); + } + break; + case MNCC_ALERT_IND: + vty_notify(ms, NULL); + vty_notify(ms, "Call is alerting\n"); + LOGP(DMNCC, LOGL_INFO, "Call is alerting\n"); + break; + case MNCC_SETUP_CNF: + vty_notify(ms, NULL); + vty_notify(ms, "Call is answered\n"); + LOGP(DMNCC, LOGL_INFO, "Call is answered\n"); + break; + case MNCC_SETUP_IND: + vty_notify(ms, NULL); + if (!first_call && !ms->settings.cw) { + vty_notify(ms, "Incoming call rejected while busy\n"); + LOGP(DMNCC, LOGL_INFO, "Incoming call but busy\n"); + cause = GSM48_CC_CAUSE_USER_BUSY; + goto release; + } + /* select first supported speech_ver */ + if ((data->fields & MNCC_F_BEARER_CAP)) { + int i; + + for (i = 0; data->bearer_cap.speech_ver[i] >= 0; i++) { + + temp = mncc_get_bearer(set, + data->bearer_cap.speech_ver[i]); + if (temp < 0) + continue; + if (temp == 5 || temp == 1) { /* half */ + /* only the first half rate */ + if (speech_ver_half < 0) + speech_ver_half = temp; + } else { + /* only the first full rate */ + if (speech_ver < 0) + speech_ver = temp; + } + } + /* half and full given */ + if (speech_ver_half >= 0 && speech_ver >= 0) { + if (set->half_prefer) { + LOGP(DMNCC, LOGL_INFO, " both supported" + " codec rates are given, using " + "preferred half rate\n"); + speech_ver = speech_ver_half; + } else + LOGP(DMNCC, LOGL_INFO, " both supported" + " codec rates are given, using " + "preferred full rate\n"); + } else if (speech_ver_half < 0 && speech_ver < 0) { + LOGP(DMNCC, LOGL_INFO, " no supported codec " + "rate is given\n"); + /* only half rate is given, use it */ + } else if (speech_ver_half >= 0) { + LOGP(DMNCC, LOGL_INFO, " only supported half " + "rate codec is given, using it\n"); + speech_ver = speech_ver_half; + /* only full rate is given, use it */ + } else { + LOGP(DMNCC, LOGL_INFO, " only supported full " + "rate codec is given, using it\n"); + } + } + /* presentation allowed if present == 0 */ + if (data->calling.present || !data->calling.number[0]) + vty_notify(ms, "Incoming call (anonymous)\n"); + else if (data->calling.type == 1) + vty_notify(ms, "Incoming call (from +%s)\n", + data->calling.number); + else if (data->calling.type == 2) + vty_notify(ms, "Incoming call (from 0-%s)\n", + data->calling.number); + else + vty_notify(ms, "Incoming call (from %s)\n", + data->calling.number); + LOGP(DMNCC, LOGL_INFO, "Incoming call (from %s callref %x)\n", + data->calling.number, call->callref); + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + /* only include bearer cap, if not given in setup + * or if multiple codecs are given + * or if not only full rate + * or if given codec is unimplemented + */ + if (!(data->fields & MNCC_F_BEARER_CAP) || speech_ver < 0) + mncc_set_bearer(ms, -1, &mncc); + else if (data->bearer_cap.speech_ver[1] >= 0 + || speech_ver != 0) + mncc_set_bearer(ms, speech_ver, &mncc); + /* CC capabilities (optional) */ + if (ms->settings.cc_dtmf) { + mncc.fields |= MNCC_F_CCCAP; + mncc.cccap.dtmf = 1; + } + mncc_send(ms, MNCC_CALL_CONF_REQ, &mncc); + if (first_call) + LOGP(DMNCC, LOGL_INFO, "Ring!\n"); + else { + LOGP(DMNCC, LOGL_INFO, "Knock!\n"); + call->hold = 1; + } + call->ring = 1; + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + mncc_send(ms, MNCC_ALERT_REQ, &mncc); + if (ms->settings.auto_answer) { + LOGP(DMNCC, LOGL_INFO, "Auto-answering call\n"); + mncc_answer(ms); + } + break; + case MNCC_SETUP_COMPL_IND: + vty_notify(ms, NULL); + vty_notify(ms, "Call is connected\n"); + LOGP(DMNCC, LOGL_INFO, "Call is connected\n"); + break; + case MNCC_HOLD_CNF: + vty_notify(ms, NULL); + vty_notify(ms, "Call is on hold\n"); + LOGP(DMNCC, LOGL_INFO, "Call is on hold\n"); + call->hold = 1; + break; + case MNCC_HOLD_REJ: + vty_notify(ms, NULL); + vty_notify(ms, "Call hold was rejected\n"); + LOGP(DMNCC, LOGL_INFO, "Call hold was rejected\n"); + break; + case MNCC_RETRIEVE_CNF: + vty_notify(ms, NULL); + vty_notify(ms, "Call is retrieved\n"); + LOGP(DMNCC, LOGL_INFO, "Call is retrieved\n"); + call->hold = 0; + break; + case MNCC_RETRIEVE_REJ: + vty_notify(ms, NULL); + vty_notify(ms, "Call retrieve was rejected\n"); + LOGP(DMNCC, LOGL_INFO, "Call retrieve was rejected\n"); + break; + case MNCC_FACILITY_IND: + LOGP(DMNCC, LOGL_INFO, "Facility info not displayed, " + "unsupported\n"); + break; + case MNCC_START_DTMF_RSP: + case MNCC_START_DTMF_REJ: + case MNCC_STOP_DTMF_RSP: + dtmf_statemachine(call, data); + break; + default: + LOGP(DMNCC, LOGL_INFO, "Message 0x%02x unsupported\n", + msg_type); + return -EINVAL; + } + + return 0; +} + +int mncc_call(struct osmocom_ms *ms, char *number) +{ + struct gsm_call *call; + struct gsm_mncc setup; + + llist_for_each_entry(call, &call_list, entry) { + if (!call->hold) { + vty_notify(ms, NULL); + vty_notify(ms, "Please put active call on hold " + "first!\n"); + LOGP(DMNCC, LOGL_INFO, "Cannot make a call, busy!\n"); + return -EBUSY; + } + } + + call = talloc_zero(l23_ctx, struct gsm_call); + if (!call) + return -ENOMEM; + call->ms = ms; + call->callref = new_callref++; + call->init = 1; + llist_add_tail(&call->entry, &call_list); + + memset(&setup, 0, sizeof(struct gsm_mncc)); + setup.callref = call->callref; + + if (!strncasecmp(number, "emerg", 5)) { + LOGP(DMNCC, LOGL_INFO, "Make emergency call\n"); + /* emergency */ + setup.emergency = 1; + } else { + LOGP(DMNCC, LOGL_INFO, "Make call to %s\n", number); + /* called number */ + setup.fields |= MNCC_F_CALLED; + if (number[0] == '+') { + number++; + setup.called.type = 1; /* international */ + } else + setup.called.type = 0; /* auto/unknown - prefix must be + used */ + setup.called.plan = 1; /* ISDN */ + strncpy(setup.called.number, number, + sizeof(setup.called.number) - 1); + + /* bearer capability (mandatory) */ + mncc_set_bearer(ms, -1, &setup); + if (ms->settings.clir) + setup.clir.sup = 1; + else if (ms->settings.clip) + setup.clir.inv = 1; + + /* CC capabilities (optional) */ + if (ms->settings.cc_dtmf) { + setup.fields |= MNCC_F_CCCAP; + setup.cccap.dtmf = 1; + } + } + + return mncc_send(ms, MNCC_SETUP_REQ, &setup); +} + +int mncc_hangup(struct osmocom_ms *ms) +{ + struct gsm_call *call, *found = NULL; + struct gsm_mncc disc; + + llist_for_each_entry(call, &call_list, entry) { + if (!call->hold) { + found = call; + break; + } + } + if (!found) { + LOGP(DMNCC, LOGL_INFO, "No active call to hangup\n"); + vty_notify(ms, NULL); + vty_notify(ms, "No active call\n"); + return -EINVAL; + } + + memset(&disc, 0, sizeof(struct gsm_mncc)); + disc.callref = found->callref; + mncc_set_cause(&disc, GSM48_CAUSE_LOC_USER, + GSM48_CC_CAUSE_NORM_CALL_CLEAR); + return mncc_send(ms, (call->init) ? MNCC_REL_REQ : MNCC_DISC_REQ, + &disc); +} + +int mncc_answer(struct osmocom_ms *ms) +{ + struct gsm_call *call, *alerting = NULL; + struct gsm_mncc rsp; + int active = 0; + + llist_for_each_entry(call, &call_list, entry) { + if (call->ring) + alerting = call; + else if (!call->hold) + active = 1; + } + if (!alerting) { + LOGP(DMNCC, LOGL_INFO, "No call alerting\n"); + vty_notify(ms, NULL); + vty_notify(ms, "No alerting call\n"); + return -EBUSY; + } + if (active) { + LOGP(DMNCC, LOGL_INFO, "Answer but we have an active call\n"); + vty_notify(ms, NULL); + vty_notify(ms, "Please put active call on hold first!\n"); + return -EBUSY; + } + alerting->ring = 0; + alerting->hold = 0; + + memset(&rsp, 0, sizeof(struct gsm_mncc)); + rsp.callref = alerting->callref; + return mncc_send(ms, MNCC_SETUP_RSP, &rsp); +} + +int mncc_hold(struct osmocom_ms *ms) +{ + struct gsm_call *call, *found = NULL; + struct gsm_mncc hold; + + llist_for_each_entry(call, &call_list, entry) { + if (!call->hold) { + found = call; + break; + } + } + if (!found) { + LOGP(DMNCC, LOGL_INFO, "No active call to hold\n"); + vty_notify(ms, NULL); + vty_notify(ms, "No active call\n"); + return -EINVAL; + } + + memset(&hold, 0, sizeof(struct gsm_mncc)); + hold.callref = found->callref; + return mncc_send(ms, MNCC_HOLD_REQ, &hold); +} + +int mncc_retrieve(struct osmocom_ms *ms, int number) +{ + struct gsm_call *call; + struct gsm_mncc retr; + int holdnum = 0, active = 0, i = 0; + + llist_for_each_entry(call, &call_list, entry) { + if (call->hold) + holdnum++; + if (!call->hold) + active = 1; + } + if (active) { + LOGP(DMNCC, LOGL_INFO, "Cannot retrieve during active call\n"); + vty_notify(ms, NULL); + vty_notify(ms, "Hold active call first!\n"); + return -EINVAL; + } + if (holdnum == 0) { + vty_notify(ms, NULL); + vty_notify(ms, "No call on hold!\n"); + return -EINVAL; + } + if (holdnum > 1 && number <= 0) { + vty_notify(ms, NULL); + vty_notify(ms, "Select call 1..%d\n", holdnum); + return -EINVAL; + } + if (holdnum == 1 && number <= 0) + number = 1; + if (number > holdnum) { + vty_notify(ms, NULL); + vty_notify(ms, "Given number %d out of range!\n", number); + vty_notify(ms, "Select call 1..%d\n", holdnum); + return -EINVAL; + } + + llist_for_each_entry(call, &call_list, entry) { + i++; + if (i == number) + break; + } + + memset(&retr, 0, sizeof(struct gsm_mncc)); + retr.callref = call->callref; + return mncc_send(ms, MNCC_RETRIEVE_REQ, &retr); +} + +/* + * DTMF + */ + +static int dtmf_statemachine(struct gsm_call *call, struct gsm_mncc *mncc) +{ + struct osmocom_ms *ms = call->ms; + struct gsm_mncc dtmf; + + switch (call->dtmf_state) { + case DTMF_ST_SPACE: + case DTMF_ST_IDLE: + /* end of string */ + if (!call->dtmf[call->dtmf_index]) { + LOGP(DMNCC, LOGL_INFO, "done with DTMF\n"); + call->dtmf_state = DTMF_ST_IDLE; + return -EOF; + } + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = call->callref; + dtmf.keypad = call->dtmf[call->dtmf_index++]; + call->dtmf_state = DTMF_ST_START; + LOGP(DMNCC, LOGL_INFO, "start DTMF (keypad %c)\n", + dtmf.keypad); + return mncc_send(ms, MNCC_START_DTMF_REQ, &dtmf); + case DTMF_ST_START: + if (mncc->msg_type != MNCC_START_DTMF_RSP) { + LOGP(DMNCC, LOGL_INFO, "DTMF was rejected\n"); + return -ENOTSUP; + } + start_dtmf_timer(call, 70); + call->dtmf_state = DTMF_ST_MARK; + LOGP(DMNCC, LOGL_INFO, "DTMF is on\n"); + break; + case DTMF_ST_MARK: + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = call->callref; + call->dtmf_state = DTMF_ST_STOP; + LOGP(DMNCC, LOGL_INFO, "stop DTMF\n"); + return mncc_send(ms, MNCC_STOP_DTMF_REQ, &dtmf); + case DTMF_ST_STOP: + start_dtmf_timer(call, 120); + call->dtmf_state = DTMF_ST_SPACE; + LOGP(DMNCC, LOGL_INFO, "DTMF is off\n"); + break; + } + + return 0; +} + +static void timeout_dtmf(void *arg) +{ + struct gsm_call *call = arg; + + LOGP(DCC, LOGL_INFO, "DTMF timer has fired\n"); + dtmf_statemachine(call, NULL); +} + +int mncc_dtmf(struct osmocom_ms *ms, char *dtmf) +{ + struct gsm_call *call, *found = NULL; + + llist_for_each_entry(call, &call_list, entry) { + if (!call->hold) { + found = call; + break; + } + } + if (!found) { + LOGP(DMNCC, LOGL_INFO, "No active call to send DTMF\n"); + vty_notify(ms, NULL); + vty_notify(ms, "No active call\n"); + return -EINVAL; + } + + if (call->dtmf_state != DTMF_ST_IDLE) { + LOGP(DMNCC, LOGL_INFO, "sending DTMF already\n"); + return -EINVAL; + } + + call->dtmf_index = 0; + strncpy(call->dtmf, dtmf, sizeof(call->dtmf) - 1); + return dtmf_statemachine(call, NULL); +} + diff --git a/src/host/layer23/src/mobile/settings.c b/src/host/layer23/src/mobile/settings.c new file mode 100644 index 00000000..a5a91ce1 --- /dev/null +++ b/src/host/layer23/src/mobile/settings.c @@ -0,0 +1,145 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <osmocore/talloc.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/networks.h> + +static char *layer2_socket_path = "/tmp/osmocom_l2"; +static char *sap_socket_path = "/tmp/osmocom_sap"; + +int gsm_settings_init(struct osmocom_ms *ms) +{ + struct gsm_settings *set = &ms->settings; + struct gsm_support *sup = &ms->support; + + strcpy(set->layer2_socket_path, layer2_socket_path); + strcpy(set->sap_socket_path, sap_socket_path); + + /* IMEI */ + sprintf(set->imei, "000000000000000"); + sprintf(set->imeisv, "0000000000000000"); + + /* SIM type */ +#warning TODO: Enable after SIM reader is available in master branch. +// set->sim_type = SIM_TYPE_READER; + + /* test SIM */ + strcpy(set->test_imsi, "001010000000000"); + set->test_rplmn_mcc = set->test_rplmn_mnc = 1; + set->test_lac = 0x0000; + set->test_tmsi = 0xffffffff; + + /* set all supported features */ + set->sms_ptp = sup->sms_ptp; + set->a5_1 = sup->a5_1; + set->a5_2 = sup->a5_2; + set->a5_3 = sup->a5_3; + set->a5_4 = sup->a5_4; + set->a5_5 = sup->a5_5; + set->a5_6 = sup->a5_6; + set->a5_7 = sup->a5_7; + set->p_gsm = sup->p_gsm; + set->e_gsm = sup->e_gsm; + set->r_gsm = sup->r_gsm; + set->dcs = sup->dcs; + set->class_900 = sup->class_900; + set->class_dcs = sup->class_dcs; + set->full_v1 = sup->full_v1; + set->full_v2 = sup->full_v2; + set->full_v3 = sup->full_v3; + set->half_v1 = sup->half_v1; + set->half_v3 = sup->half_v3; + set->ch_cap = sup->ch_cap; + set->min_rxlev_db = sup->min_rxlev_db; + set->dsc_max = sup->dsc_max; + + if (sup->half_v1 || sup->half_v3) + set->half = 1; + + /* software features */ + set->cc_dtmf = 1; + + INIT_LLIST_HEAD(&set->abbrev); + + return 0; +} + +int gsm_settings_exit(struct osmocom_ms *ms) +{ + struct gsm_settings *set = &ms->settings; + struct gsm_settings_abbrev *abbrev; + + while (!llist_empty(&set->abbrev)) { + abbrev = llist_entry(set->abbrev.next, + struct gsm_settings_abbrev, list); + llist_del(&abbrev->list); + talloc_free(abbrev); + } + + return 0; +} + +char *gsm_check_imei(const char *imei, const char *sv) +{ + int i; + + if (!imei || strlen(imei) != 15) + return "IMEI must have 15 digits!"; + + for (i = 0; i < strlen(imei); i++) { + if (imei[i] < '0' || imei[i] > '9') + return "IMEI must have digits 0 to 9 only!"; + } + + if (!sv || strlen(sv) != 1) + return "Software version must have 1 digit!"; + + if (sv[0] < '0' || sv[0] > '9') + return "Software version must have digits 0 to 9 only!"; + + return NULL; +} + +int gsm_random_imei(struct gsm_settings *set) +{ + int digits = set->imei_random; + char rand[16]; + + if (digits <= 0) + return 0; + if (digits > 15) + digits = 15; + + sprintf(rand, "%08ld", random() % 100000000); + sprintf(rand + 8, "%07ld", random() % 10000000); + + strcpy(set->imei + 15 - digits, rand + 15 - digits); + strncpy(set->imeisv, set->imei, 15); + + return 0; +} + diff --git a/src/host/layer23/src/mobile/subscriber.c b/src/host/layer23/src/mobile/subscriber.c new file mode 100644 index 00000000..3ba78f3f --- /dev/null +++ b/src/host/layer23/src/mobile/subscriber.c @@ -0,0 +1,1178 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <arpa/inet.h> +#include <osmocore/talloc.h> +#include <osmocore/comp128.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/networks.h> +#include <osmocom/bb/mobile/vty.h> + +void *l23_ctx; + +static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg); +static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg); +static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg); + +/* + * support + */ + +char *gsm_check_imsi(const char *imsi) +{ + int i; + + if (!imsi || strlen(imsi) != 15) + return "IMSI must have 15 digits!"; + + for (i = 0; i < strlen(imsi); i++) { + if (imsi[i] < '0' || imsi[i] > '9') + return "IMSI must have digits 0 to 9 only!"; + } + + return NULL; +} + +static char *sim_decode_bcd(uint8_t *data, uint8_t length) +{ + int i, j = 0; + static char result[32], c; + + for (i = 0; i < (length << 1); i++) { + if ((i & 1)) + c = (data[i >> 1] >> 4); + else + c = (data[i >> 1] & 0xf); + if (c == 0xf) + break; + result[j++] = c + '0'; + if (j == sizeof(result) - 1) + break; + } + result[j] = '\0'; + + return result; +} + +static void xor96(uint8_t *ki, uint8_t *rand, uint8_t *sres, uint8_t *kc) +{ + int i; + + for (i=0; i < 4; i++) + sres[i] = rand[i] ^ ki[i]; + for (i=0; i < 8; i++) + kc[i] = rand[i] ^ ki[i+4]; +} + +/* + * init/exit + */ + +int gsm_subscr_init(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + memset(subscr, 0, sizeof(*subscr)); + subscr->ms = ms; + + /* set TMSI / LAC invalid */ + subscr->tmsi = 0xffffffff; + subscr->lac = 0x0000; + + /* set key invalid */ + subscr->key_seq = 7; + + /* init lists */ + INIT_LLIST_HEAD(&subscr->plmn_list); + INIT_LLIST_HEAD(&subscr->plmn_na); + + /* open SIM */ + subscr->sim_handle_query = sim_open(ms, subscr_sim_query_cb); + subscr->sim_handle_update = sim_open(ms, subscr_sim_update_cb); + subscr->sim_handle_key = sim_open(ms, subscr_sim_key_cb); + + return 0; +} + +int gsm_subscr_exit(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct llist_head *lh, *lh2; + + if (subscr->sim_handle_query) { + sim_close(ms, subscr->sim_handle_query); + subscr->sim_handle_query = 0; + } + if (subscr->sim_handle_update) { + sim_close(ms, subscr->sim_handle_update); + subscr->sim_handle_update = 0; + } + if (subscr->sim_handle_key) { + sim_close(ms, subscr->sim_handle_key); + subscr->sim_handle_key = 0; + } + + /* flush lists */ + llist_for_each_safe(lh, lh2, &subscr->plmn_list) { + llist_del(lh); + talloc_free(lh); + } + llist_for_each_safe(lh, lh2, &subscr->plmn_na) { + llist_del(lh); + talloc_free(lh); + } + + return 0; +} + +/* + * test card + */ + +/* Attach test card, no SIM must be currently attached */ +int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, + uint16_t lac, uint32_t tmsi) +{ + struct gsm_settings *set = &ms->settings; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + char *error; + + if (subscr->sim_valid) { + LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card " + "is detached.\n"); + return -EBUSY; + } + + error = gsm_check_imsi(set->test_imsi); + if (error) { + LOGP(DMM, LOGL_ERROR, "%s\n", error); + return -EINVAL; + } + + /* reset subscriber */ + gsm_subscr_exit(ms); + gsm_subscr_init(ms); + + subscr->sim_type = GSM_SIM_TYPE_TEST; + sprintf(subscr->sim_name, "test"); + subscr->sim_valid = 1; + subscr->ustate = GSM_SIM_U2_NOT_UPDATED; + subscr->acc_barr = set->test_barr; /* we may access barred cell */ + subscr->acc_class = 0xffff; /* we have any access class */ + subscr->plmn_valid = set->test_rplmn_valid; + subscr->plmn_mcc = mcc; + subscr->plmn_mnc = mnc; + subscr->mcc = mcc; + subscr->mnc = mnc; + subscr->lac = lac; + subscr->tmsi = tmsi; + subscr->always_search_hplmn = set->test_always; + subscr->t6m_hplmn = 1; /* try to find home network every 6 min */ + strcpy(subscr->imsi, set->test_imsi); + + LOGP(DMM, LOGL_INFO, "(ms %s) Inserting test card (IMSI=%s %s, %s)\n", + ms->name, subscr->imsi, gsm_imsi_mcc(subscr->imsi), + gsm_imsi_mnc(subscr->imsi)); + + if (subscr->plmn_valid) + LOGP(DMM, LOGL_INFO, "-> Test card registered to %s %s 0x%04x" + "(%s, %s)\n", gsm_print_mcc(mcc), + gsm_print_mnc(mnc), lac, gsm_get_mcc(mcc), + gsm_get_mnc(mcc, mnc)); + else + LOGP(DMM, LOGL_INFO, "-> Test card not registered\n"); + + /* insert card */ + nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); + if (!nmsg) + return -ENOMEM; + gsm48_mmr_downmsg(ms, nmsg); + + return 0; +} + +/* + * sim card + */ + +static int subscr_sim_iccid(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + strcpy(subscr->iccid, sim_decode_bcd(data, length)); + sprintf(subscr->sim_name, "sim-%s", subscr->iccid); + LOGP(DMM, LOGL_INFO, "received ICCID %s from SIM\n", subscr->iccid); + + return 0; +} + +static int subscr_sim_imsi(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + char *imsi; + + /* get actual length */ + if (length < 1) + return -EINVAL; + if (data[0] + 1 < length) { + LOGP(DMM, LOGL_NOTICE, "invalid length = %d\n", length); + return -EINVAL; + } + length = data[0]; + + /* decode IMSI, skip first digit (parity) */ + imsi = sim_decode_bcd(data + 1, length); + if (strlen(imsi) - 1 > GSM_IMSI_LENGTH - 1 || strlen(imsi) - 1 < 6) { + LOGP(DMM, LOGL_NOTICE, "IMSI invalid length = %d\n", + strlen(imsi) - 1); + return -EINVAL; + } + + strncpy(subscr->imsi, imsi + 1, sizeof(subscr->imsi) - 1); + + LOGP(DMM, LOGL_INFO, "received IMSI %s from SIM\n", subscr->imsi); + + return 0; +} + +static int subscr_sim_loci(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm1111_ef_loci *loci; + + if (length < 11) + return -EINVAL; + loci = (struct gsm1111_ef_loci *) data; + + /* TMSI */ + subscr->tmsi = ntohl(loci->tmsi); + + /* LAI */ + gsm48_decode_lai(&loci->lai, &subscr->mcc, &subscr->mnc, &subscr->lac); + + /* location update status */ + switch (loci->lupd_status & 0x07) { + case 0x00: + subscr->ustate = GSM_SIM_U1_UPDATED; + break; + case 0x02: + case 0x03: + subscr->ustate = GSM_SIM_U3_ROAMING_NA; + break; + default: + subscr->ustate = GSM_SIM_U2_NOT_UPDATED; + } + + LOGP(DMM, LOGL_INFO, "received LOCI from SIM (mcc=%s mnc=%s lac=0x%04x " + "U%d)\n", gsm_print_mcc(subscr->mcc), + gsm_print_mnc(subscr->mnc), subscr->lac, subscr->ustate); + + return 0; +} + +static int subscr_sim_msisdn(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm1111_ef_adn *adn; + + if (length < sizeof(*adn)) + return -EINVAL; + adn = (struct gsm1111_ef_adn *) (data + length - sizeof(*adn)); + + /* empty */ + subscr->msisdn[0] = '\0'; + if (adn->len_bcd <= 1) + return 0; + + /* number */ + if (adn->ton_npi == 1) + strcpy(subscr->msisdn, "+"); + if (adn->ton_npi == 2) + strcpy(subscr->msisdn, "0"); + strncat(subscr->msisdn, sim_decode_bcd(adn->number, adn->len_bcd - 1), + sizeof(subscr->msisdn) - 2); + + LOGP(DMM, LOGL_INFO, "received MSISDN %s from SIM\n", subscr->msisdn); + + return 0; +} + +static int subscr_sim_kc(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + if (length < 9) + return -EINVAL; + + /* key */ + memcpy(subscr->key, data, 8); + + /* key sequence */ + subscr->key_seq = data[8] & 0x07; + + LOGP(DMM, LOGL_INFO, "received KEY from SIM\n"); + + return 0; +} + +static int subscr_sim_plmnsel(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_sub_plmn_list *plmn; + struct llist_head *lh, *lh2; + uint8_t lai[5]; + uint16_t dummy_lac; + + /* flush list */ + llist_for_each_safe(lh, lh2, &subscr->plmn_list) { + llist_del(lh); + talloc_free(lh); + } + + while(length >= 3) { + /* end of list inside mandatory fields */ + if (data[0] == 0xff && data[1] == 0xff && data[2] == 0x0ff) + break; + + /* add to list */ + plmn = talloc_zero(l23_ctx, struct gsm_sub_plmn_list); + if (!plmn) + return -ENOMEM; + lai[0] = data[0]; + lai[1] = data[1]; + lai[2] = data[2]; + gsm48_decode_lai((struct gsm48_loc_area_id *)lai, &plmn->mcc, + &plmn->mnc, &dummy_lac); + llist_add_tail(&plmn->entry, &subscr->plmn_list); + + LOGP(DMM, LOGL_INFO, "received PLMN selector (mcc=%s mnc=%s) " + "from SIM\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc)); + + data += 3; + length -= 3; + } + + return 0; +} + +static int subscr_sim_hpplmn(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + if (length < 1) + return -EINVAL; + + /* HPLMN search interval */ + subscr->t6m_hplmn = *data; /* multiple of 6 minutes */ + + LOGP(DMM, LOGL_INFO, "received HPPLMN %d (%d mins) from SIM\n", + subscr->t6m_hplmn, subscr->t6m_hplmn * 6); + + return 0; +} + +static int subscr_sim_spn(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + int i; + + /* UCS2 code not supported */ + if (length < 17 || data[1] >= 0x80) + return -ENOTSUP; + + data++; + for (i = 0; i < 16; i++) { + if (*data == 0xff) + break; + subscr->sim_spn[i] = *data++; + } + subscr->sim_spn[i] = '\0'; + + LOGP(DMM, LOGL_INFO, "received SPN %s from SIM\n", subscr->sim_spn); + + return 0; +} + +static int subscr_sim_acc(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + uint16_t ac; + + if (length < 2) + return -EINVAL; + + /* cell access */ + memcpy(&ac, data, sizeof(ac)); + subscr->acc_class = ntohs(ac); + + LOGP(DMM, LOGL_INFO, "received ACC %04x from SIM\n", subscr->acc_class); + + return 0; +} + +static int subscr_sim_fplmn(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_sub_plmn_na *na; + struct llist_head *lh, *lh2; + uint8_t lai[5]; + uint16_t dummy_lac; + + /* flush list */ + llist_for_each_safe(lh, lh2, &subscr->plmn_na) { + llist_del(lh); + talloc_free(lh); + } + + while (length >= 3) { + /* end of list inside mandatory fields */ + if (data[0] == 0xff && data[1] == 0xff && data[2] == 0x0ff) + break; + + /* add to list */ + na = talloc_zero(l23_ctx, struct gsm_sub_plmn_na); + if (!na) + return -ENOMEM; + lai[0] = data[0]; + lai[1] = data[1]; + lai[2] = data[2]; + gsm48_decode_lai((struct gsm48_loc_area_id *)lai, &na->mcc, + &na->mnc, &dummy_lac); + na->cause = 0; + llist_add_tail(&na->entry, &subscr->plmn_na); + + data += 3; + length -= 3; + } + return 0; +} + +static struct subscr_sim_file { + uint8_t mandatory; + uint16_t path[MAX_SIM_PATH_LENGTH]; + uint16_t file; + int (*func)(struct osmocom_ms *ms, uint8_t *data, + uint8_t length); +} subscr_sim_files[] = { + { 1, { 0 }, 0x2fe2, subscr_sim_iccid }, + { 1, { 0x7f20, 0 }, 0x6f07, subscr_sim_imsi }, + { 1, { 0x7f20, 0 }, 0x6f7e, subscr_sim_loci }, + { 0, { 0x7f20, 0 }, 0x6f40, subscr_sim_msisdn }, + { 0, { 0x7f20, 0 }, 0x6f20, subscr_sim_kc }, + { 0, { 0x7f20, 0 }, 0x6f30, subscr_sim_plmnsel }, + { 0, { 0x7f20, 0 }, 0x6f31, subscr_sim_hpplmn }, + { 0, { 0x7f20, 0 }, 0x6f46, subscr_sim_spn }, + { 0, { 0x7f20, 0 }, 0x6f78, subscr_sim_acc }, + { 0, { 0x7f20, 0 }, 0x6f7b, subscr_sim_fplmn }, + { 0, { 0 }, 0, NULL } +}; + +/* request file from SIM */ +static int subscr_sim_request(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct subscr_sim_file *sf = &subscr_sim_files[subscr->sim_file_index]; + struct msgb *nmsg; + struct sim_hdr *nsh; + int i; + + /* we are done, fire up PLMN and cell selection process */ + if (!sf->func) { + LOGP(DMM, LOGL_INFO, "(ms %s) Done reading SIM card " + "(IMSI=%s %s, %s)\n", ms->name, subscr->imsi, + gsm_imsi_mcc(subscr->imsi), gsm_imsi_mnc(subscr->imsi)); + + /* if LAI is valid, set RPLMN */ + if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { + subscr->plmn_valid = 1; + subscr->plmn_mcc = subscr->mcc; + subscr->plmn_mnc = subscr->mnc; + LOGP(DMM, LOGL_INFO, "-> SIM card registered to %s %s " + "(%s, %s)\n", gsm_print_mcc(subscr->plmn_mcc), + gsm_print_mnc(subscr->plmn_mnc), + gsm_get_mcc(subscr->plmn_mcc), + gsm_get_mnc(subscr->plmn_mcc, + subscr->plmn_mnc)); + } else + LOGP(DMM, LOGL_INFO, "-> SIM card not registered\n"); + + /* insert card */ + nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); + if (!nmsg) + return -ENOMEM; + gsm48_mmr_downmsg(ms, nmsg); + + return 0; + } + + /* trigger SIM reading */ + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_query, + SIM_JOB_READ_BINARY); + if (!nmsg) + return -ENOMEM; + nsh = (struct sim_hdr *) nmsg->data; + i = 0; + while (sf->path[i]) { + nsh->path[i] = sf->path[i]; + i++; + } + nsh->path[i] = 0; /* end of path */ + nsh->file = sf->file; + LOGP(DMM, LOGL_INFO, "Requesting SIM file 0x%04x\n", nsh->file); + sim_job(ms, nmsg); + + return 0; +} + +static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct sim_hdr *sh = (struct sim_hdr *) msg->data; + uint8_t *payload = msg->data + sizeof(*sh); + uint16_t payload_len = msg->len - sizeof(*sh); + int rc; + struct subscr_sim_file *sf = &subscr_sim_files[subscr->sim_file_index]; + + /* error handling */ + if (sh->job_type == SIM_JOB_ERROR) { + uint8_t cause = payload[0]; + + switch (cause) { + /* unlocking required */ + case SIM_CAUSE_PIN1_REQUIRED: + LOGP(DMM, LOGL_INFO, "PIN is required, %d tries left\n", + payload[1]); + + vty_notify(ms, NULL); + vty_notify(ms, "Please give PIN for ICCID %s (you have " + "%d tries left)\n", subscr->iccid, payload[1]); + subscr->sim_pin_required = 1; + break; + case SIM_CAUSE_PIN1_BLOCKED: + LOGP(DMM, LOGL_NOTICE, "PIN is blocked\n"); + + vty_notify(ms, NULL); + vty_notify(ms, "PIN is blocked\n"); + if (payload[1]) { + vty_notify(ms, "Please give PUC for ICCID %s " + "(you have %d tries left)\n", + subscr->iccid, payload[1]); + } + subscr->sim_pin_required = 1; + break; + case SIM_CAUSE_PUC_BLOCKED: + LOGP(DMM, LOGL_NOTICE, "PUC is blocked\n"); + + vty_notify(ms, NULL); + vty_notify(ms, "PUC is blocked\n"); + subscr->sim_pin_required = 1; + break; + default: + if (sf->func && !sf->mandatory) { + LOGP(DMM, LOGL_NOTICE, "SIM reading failed, " + "ignoring!\n"); + goto ignore; + } + LOGP(DMM, LOGL_NOTICE, "SIM reading failed\n"); + + vty_notify(ms, NULL); + vty_notify(ms, "SIM failed, replace SIM!\n"); + } + msgb_free(msg); + + return; + } + + /* if pin was successfully unlocked, then resend request */ + if (subscr->sim_pin_required) { + subscr->sim_pin_required = 0; + subscr_sim_request(ms); + return; + } + + /* done when nothing more to read. this happens on PIN requests */ + if (!sf->func) + return; + + /* call function do decode SIM reply */ + rc = sf->func(ms, payload, payload_len); + if (rc) { + LOGP(DMM, LOGL_NOTICE, "SIM reading failed, file invalid\n"); + if (subscr_sim_files[subscr->sim_file_index].mandatory) { + vty_notify(ms, NULL); + vty_notify(ms, "SIM failed, data invalid, replace " + "SIM!\n"); + msgb_free(msg); + + return; + } + } + +ignore: + msgb_free(msg); + + /* trigger next file */ + subscr->sim_file_index++; + subscr_sim_request(ms); +} + +/* enter PIN */ +void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2, + int8_t mode) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + uint8_t job; + + /* skip, if no real valid SIM */ + if (subscr->sim_type != GSM_SIM_TYPE_READER) + return; + + switch (mode) { + case -1: + job = SIM_JOB_PIN1_DISABLE; + LOGP(DMM, LOGL_INFO, "disabling PIN %s\n", pin1); + break; + case 1: + job = SIM_JOB_PIN1_ENABLE; + LOGP(DMM, LOGL_INFO, "enabling PIN %s\n", pin1); + break; + case 2: + job = SIM_JOB_PIN1_CHANGE; + LOGP(DMM, LOGL_INFO, "changing PIN %s to %s\n", pin1, pin2); + break; + case 99: + job = SIM_JOB_PIN1_UNBLOCK; + LOGP(DMM, LOGL_INFO, "unblocking PIN %s with PUC %s\n", pin1, + pin2); + break; + default: + if (!subscr->sim_pin_required) { + LOGP(DMM, LOGL_ERROR, "No PIN required now\n"); + return; + } + LOGP(DMM, LOGL_INFO, "entering PIN %s\n", pin1); + job = SIM_JOB_PIN1_UNLOCK; + } + + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_query, job); + if (!nmsg) + return; + memcpy(msgb_put(nmsg, strlen(pin1) + 1), pin1, strlen(pin1) + 1); + memcpy(msgb_put(nmsg, strlen(pin2) + 1), pin2, strlen(pin2) + 1); + sim_job(ms, nmsg); +} + +/* Attach SIM reader, no SIM must be currently attached */ +int gsm_subscr_simcard(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + if (subscr->sim_valid) { + LOGP(DMM, LOGL_ERROR, "Cannot attach card, until current card " + "is detached.\n"); + return -EBUSY; + } + + /* reset subscriber */ + gsm_subscr_exit(ms); + gsm_subscr_init(ms); + + subscr->sim_type = GSM_SIM_TYPE_READER; + sprintf(subscr->sim_name, "sim"); + subscr->sim_valid = 1; + subscr->ustate = GSM_SIM_U2_NOT_UPDATED; + + /* start with first index */ + subscr->sim_file_index = 0; + return subscr_sim_request(ms); +} + +/* update plmn not allowed list on SIM */ +static int subscr_write_plmn_na(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct sim_hdr *nsh; + struct gsm_sub_plmn_na *na, *nas[4] = { NULL, NULL, NULL, NULL }; + int count = 0, i; + uint8_t *data; + uint8_t lai[5]; + + /* skip, if no real valid SIM */ + if (subscr->sim_type != GSM_SIM_TYPE_READER || !subscr->sim_valid) + return 0; + + /* get tail list from "PLMN not allowed" */ + llist_for_each_entry(na, &subscr->plmn_na, entry) { + if (count < 4) + nas[count] = na; + else { + nas[0] = nas[1]; + nas[1] = nas[2]; + nas[2] = nas[3]; + nas[3] = na; + } + count++; + } + + /* write to SIM */ + LOGP(DMM, LOGL_INFO, "Updating FPLMN on SIM\n"); + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, + SIM_JOB_UPDATE_BINARY); + if (!nmsg) + return -ENOMEM; + nsh = (struct sim_hdr *) nmsg->data; + data = msgb_put(nmsg, 12); + nsh->path[0] = 0x7f20; + nsh->path[1] = 0; + nsh->file = 0x6f7b; + for (i = 0; i < 4; i++) { + if (nas[i]) { + gsm48_encode_lai((struct gsm48_loc_area_id *)lai, + nas[i]->mcc, nas[i]->mnc, 0); + *data++ = lai[0]; + *data++ = lai[1]; + *data++ = lai[2]; + } else { + *data++ = 0xff; + *data++ = 0xff; + *data++ = 0xff; + } + } + sim_job(ms, nmsg); + + return 0; +} + +/* update LOCI on SIM */ +int gsm_subscr_write_loci(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct sim_hdr *nsh; + struct gsm1111_ef_loci *loci; + + /* skip, if no real valid SIM */ + if (subscr->sim_type != GSM_SIM_TYPE_READER || !subscr->sim_valid) + return 0; + + LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n"); + + /* write to SIM */ + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, + SIM_JOB_UPDATE_BINARY); + if (!nmsg) + return -ENOMEM; + nsh = (struct sim_hdr *) nmsg->data; + nsh->path[0] = 0x7f20; + nsh->path[1] = 0; + nsh->file = 0x6f7e; + loci = (struct gsm1111_ef_loci *)msgb_put(nmsg, sizeof(*loci)); + + /* TMSI */ + loci->tmsi = htonl(subscr->tmsi); + + /* LAI */ + gsm48_encode_lai(&loci->lai, subscr->mcc, subscr->mnc, subscr->lac); + + /* TMSI time */ + loci->tmsi_time = 0xff; + + /* location update status */ + switch (subscr->ustate) { + case GSM_SIM_U1_UPDATED: + loci->lupd_status = 0x00; + break; + case GSM_SIM_U3_ROAMING_NA: + loci->lupd_status = 0x03; + break; + default: + loci->lupd_status = 0x01; + } + + sim_job(ms, nmsg); + + return 0; +} + +static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg) +{ + struct sim_hdr *sh = (struct sim_hdr *) msg->data; + uint8_t *payload = msg->data + sizeof(*sh); + + /* error handling */ + if (sh->job_type == SIM_JOB_ERROR) + LOGP(DMM, LOGL_NOTICE, "SIM update failed (cause %d)\n", + *payload); + + msgb_free(msg); +} + +int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, + uint8_t *rand, uint8_t no_sim) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct sim_hdr *nsh; + + /* not a SIM */ + if ((subscr->sim_type != GSM_SIM_TYPE_READER + && subscr->sim_type != GSM_SIM_TYPE_TEST) + || !subscr->sim_valid || no_sim) { + struct gsm48_mm_event *nmme; + + LOGP(DMM, LOGL_INFO, "Sending dummy authentication response\n"); + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); + if (!nmsg) + return -ENOMEM; + nmme = (struct gsm48_mm_event *) nmsg->data; + nmme->sres[0] = 0x12; + nmme->sres[1] = 0x34; + nmme->sres[2] = 0x56; + nmme->sres[3] = 0x78; + gsm48_mmevent_msg(ms, nmsg); + + return 0; + } + + /* test SIM */ + if (subscr->sim_type == GSM_SIM_TYPE_TEST) { + struct gsm48_mm_event *nmme; + uint8_t sres[4]; + struct gsm_settings *set = &ms->settings; + + if (set->test_ki_type == GSM_SIM_KEY_COMP128) + comp128(set->test_ki, rand, sres, subscr->key); + else + xor96(set->test_ki, rand, sres, subscr->key); + /* store sequence */ + subscr->key_seq = key_seq; + + LOGP(DMM, LOGL_INFO, "Sending authentication response\n"); + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); + if (!nmsg) + return -ENOMEM; + nmme = (struct gsm48_mm_event *) nmsg->data; + memcpy(nmme->sres, sres, 4); + gsm48_mmevent_msg(ms, nmsg); + + return 0; + } + + LOGP(DMM, LOGL_INFO, "Generating KEY at SIM\n"); + + /* command to SIM */ + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_key, SIM_JOB_RUN_GSM_ALGO); + if (!nmsg) + return -ENOMEM; + nsh = (struct sim_hdr *) nmsg->data; + nsh->path[0] = 0x7f20; + nsh->path[1] = 0; + + /* random */ + memcpy(msgb_put(nmsg, 16), rand, 16); + + /* store sequence */ + subscr->key_seq = key_seq; + + sim_job(ms, nmsg); + + return 0; +} + +static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct sim_hdr *sh = (struct sim_hdr *) msg->data; + uint8_t *payload = msg->data + sizeof(*sh); + uint16_t payload_len = msg->len - sizeof(*sh); + struct msgb *nmsg; + struct sim_hdr *nsh; + struct gsm48_mm_event *nmme; + uint8_t *data; + + /* error handling */ + if (sh->job_type == SIM_JOB_ERROR) { + LOGP(DMM, LOGL_NOTICE, "key generation on SIM failed " + "(cause %d)\n", *payload); + + msgb_free(msg); + + return; + } + + if (payload_len < 12) { + LOGP(DMM, LOGL_NOTICE, "response from SIM too short\n"); + return; + } + + /* store key */ + memcpy(subscr->key, payload + 4, 8); + + /* write to SIM */ + LOGP(DMM, LOGL_INFO, "Updating KC on SIM\n"); + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, + SIM_JOB_UPDATE_BINARY); + if (!nmsg) + return; + nsh = (struct sim_hdr *) nmsg->data; + nsh->path[0] = 0x7f20; + nsh->path[1] = 0; + nsh->file = 0x6f20; + data = msgb_put(nmsg, 9); + memcpy(data, subscr->key, 8); + data[8] = subscr->key_seq; + sim_job(ms, nmsg); + + /* return signed response */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); + if (!nmsg) + return; + nmme = (struct gsm48_mm_event *) nmsg->data; + memcpy(nmme->sres, payload, 4); + gsm48_mmevent_msg(ms, nmsg); + + msgb_free(msg); +} + +/* + * detach + */ + +/* Detach card */ +int gsm_subscr_remove(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_ERROR, "Cannot remove card, no card present\n"); + return -EINVAL; + } + + /* remove card */ + nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); + if (!nmsg) + return -ENOMEM; + gsm48_mmr_downmsg(ms, nmsg); + + return 0; +} + +/* + * state and lists + */ + +static const char *subscr_ustate_names[] = { + "U0_NULL", + "U1_UPDATED", + "U2_NOT_UPDATED", + "U3_ROAMING_NA" +}; + +/* change to new U state */ +void new_sim_ustate(struct gsm_subscriber *subscr, int state) +{ + LOGP(DMM, LOGL_INFO, "(ms %s) new state %s -> %s\n", subscr->ms->name, + subscr_ustate_names[subscr->ustate], + subscr_ustate_names[state]); + + subscr->ustate = state; +} + +/* del forbidden PLMN */ +int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc) +{ + struct gsm_sub_plmn_na *na; + + llist_for_each_entry(na, &subscr->plmn_na, entry) { + if (na->mcc == mcc && na->mnc == mnc) { + LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " + "PLMNs (mcc=%s, mnc=%s)\n", + gsm_print_mcc(mcc), gsm_print_mnc(mnc)); + llist_del(&na->entry); + talloc_free(na); + /* update plmn not allowed list on SIM */ + subscr_write_plmn_na(subscr->ms); + return 0; + } + } + + return -EINVAL; +} + +/* add forbidden PLMN */ +int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc, uint8_t cause) +{ + struct gsm_sub_plmn_na *na; + + /* if already in the list, remove and add to tail */ + gsm_subscr_del_forbidden_plmn(subscr, mcc, mnc); + + LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs " + "(mcc=%s, mnc=%s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc)); + na = talloc_zero(l23_ctx, struct gsm_sub_plmn_na); + if (!na) + return -ENOMEM; + na->mcc = mcc; + na->mnc = mnc; + na->cause = cause; + llist_add_tail(&na->entry, &subscr->plmn_na); + + /* don't add Home PLMN to SIM */ + if (subscr->sim_valid && gsm_match_mnc(mcc, mnc, subscr->imsi)) + return -EINVAL; + + /* update plmn not allowed list on SIM */ + subscr_write_plmn_na(subscr->ms); + + return 0; +} + +/* search forbidden PLMN */ +int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc) +{ + struct gsm_sub_plmn_na *na; + + llist_for_each_entry(na, &subscr->plmn_na, entry) { + if (na->mcc == mcc && na->mnc == mnc) + return 1; + } + + return 0; +} + +int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_sub_plmn_na *temp; + + print(priv, "MCC |MNC |cause\n"); + print(priv, "-------+-------+-------\n"); + llist_for_each_entry(temp, &subscr->plmn_na, entry) + print(priv, "%s |%s%s |#%d\n", + gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), + ((temp->mnc & 0x00f) == 0x00f) ? " ":"", temp->cause); + + return 0; +} + +/* dump subscriber */ +void gsm_subscr_dump(struct gsm_subscriber *subscr, + void (*print)(void *, const char *, ...), void *priv) +{ + int i; + struct gsm_sub_plmn_list *plmn_list; + struct gsm_sub_plmn_na *plmn_na; + + print(priv, "Mobile Subscriber of MS '%s':\n", subscr->ms->name); + + if (!subscr->sim_valid) { + print(priv, " No SIM present.\n"); + return; + } + + print(priv, " IMSI: %s\n", subscr->imsi); + if (subscr->iccid[0]) + print(priv, " ICCID: %s\n", subscr->iccid); + if (subscr->sim_spn[0]) + print(priv, " Service Provider Name: %s\n", subscr->sim_spn); + if (subscr->msisdn[0]) + print(priv, " MSISDN: %s\n", subscr->msisdn); + print(priv, " Status: %s IMSI %s", subscr_ustate_names[subscr->ustate], + (subscr->imsi_attached) ? "attached" : "detached"); + if (subscr->tmsi != 0xffffffff) + print(priv, " TSMI 0x%08x", subscr->tmsi); + if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { + print(priv, "\n"); + print(priv, " LAI: MCC %s MNC %s LAC 0x%04x " + "(%s, %s)\n", gsm_print_mcc(subscr->mcc), + gsm_print_mnc(subscr->mnc), subscr->lac, + gsm_get_mcc(subscr->mcc), + gsm_get_mnc(subscr->mcc, subscr->mnc)); + } else + print(priv, " LAI: invalid\n"); + if (subscr->key_seq != 7) { + print(priv, " Key: sequence %d ", subscr->key_seq); + for (i = 0; i < sizeof(subscr->key); i++) + print(priv, " %02x", subscr->key[i]); + print(priv, "\n"); + } + if (subscr->plmn_valid) + print(priv, " Registered PLMN: MCC %s MNC %s (%s, %s)\n", + gsm_print_mcc(subscr->plmn_mcc), + gsm_print_mnc(subscr->plmn_mnc), + gsm_get_mcc(subscr->plmn_mcc), + gsm_get_mnc(subscr->plmn_mcc, subscr->plmn_mnc)); + print(priv, " Access barred cells: %s\n", + (subscr->acc_barr) ? "yes" : "no"); + print(priv, " Access classes:"); + for (i = 0; i < 16; i++) + if ((subscr->acc_class & (1 << i))) + print(priv, " C%d", i); + print(priv, "\n"); + if (!llist_empty(&subscr->plmn_list)) { + print(priv, " List of preferred PLMNs:\n"); + print(priv, " MCC |MNC\n"); + print(priv, " -------+-------\n"); + llist_for_each_entry(plmn_list, &subscr->plmn_list, entry) + print(priv, " %s |%s (%s, %s)\n", + gsm_print_mcc(plmn_list->mcc), + gsm_print_mnc(plmn_list->mnc), + gsm_get_mcc(plmn_list->mcc), + gsm_get_mnc(plmn_list->mcc, plmn_list->mnc)); + } + if (!llist_empty(&subscr->plmn_na)) { + print(priv, " List of forbidden PLMNs:\n"); + print(priv, " MCC |MNC |cause\n"); + print(priv, " -------+-------+-------\n"); + llist_for_each_entry(plmn_na, &subscr->plmn_na, entry) + print(priv, " %s |%s%s |#%d " + "(%s, %s)\n", gsm_print_mcc(plmn_na->mcc), + gsm_print_mnc(plmn_na->mnc), + ((plmn_na->mnc & 0x00f) == 0x00f) ? " ":"", + plmn_na->cause, gsm_get_mcc(plmn_na->mcc), + gsm_get_mnc(plmn_na->mcc, plmn_na->mnc)); + } +} + diff --git a/src/host/layer23/src/mobile/support.c b/src/host/layer23/src/mobile/support.c new file mode 100644 index 00000000..97306f52 --- /dev/null +++ b/src/host/layer23/src/mobile/support.c @@ -0,0 +1,178 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include <osmocom/bb/common/osmocom_data.h> + +void gsm_support_init(struct osmocom_ms *ms) +{ + struct gsm_support *sup = &ms->support; + int i; + + memset(sup, 0, sizeof(*sup)); + sup->ms = ms; + + /* rf power capability */ + sup->class_900 = 4; /* CLASS 4: Handheld 2W */ + sup->class_dcs = 1; /* CLASS 1: Handheld 1W */ + /* controlled early classmark sending */ + sup->es_ind = 0; /* no */ + /* revision level */ + sup->rev_lev = 1; /* phase 2 mobile station */ + /* support of VGCS */ + sup->vgcs = 0; /* no */ + /* support of VBS */ + sup->vbs = 0; /* no */ + /* support of SMS */ + sup->sms_ptp = 1; /* yes */ + /* screening indicator */ + sup->ss_ind = 1; /* phase 2 error handling */ + /* pseudo synchronised capability */ + sup->ps_cap = 0; /* no */ + /* CM service prompt */ + sup->cmsp = 0; /* no */ + /* solsa support */ + sup->solsa = 0; /* no */ + /* location service support */ + sup->lcsva = 0; /* no */ + sup->loc_serv = 0; /* no */ + /* codec supprot */ + sup->a5_1 = 1; + sup->a5_2 = 1; + sup->a5_3 = 0; + sup->a5_4 = 0; + sup->a5_5 = 0; + sup->a5_6 = 0; + sup->a5_7 = 0; + /* radio support */ + sup->p_gsm = 1; /* P-GSM */ + sup->e_gsm = 1; /* E-GSM */ + sup->r_gsm = 1; /* R-GSM */ + sup->dcs = 1; + /* set supported frequencies */ + if (sup->p_gsm) + for(i = 1; i <= 124; i++) + sup->freq_map[i >> 3] |= (1 << (i & 7)); + if (sup->dcs) + for(i = 512; i <= 885; i++) + sup->freq_map[i >> 3] |= (1 << (i & 7)); + if (sup->e_gsm) { + for(i = 975; i <= 1023; i++) + sup->freq_map[i >> 3] |= (1 << (i & 7)); + sup->freq_map[0] |= 1; + } + if (sup->r_gsm) + for(i = 955; i <= 974; i++) + sup->freq_map[i >> 3] |= (1 << (i & 7)); + /* multi slot support */ + sup->ms_sup = 0; /* no */ + /* ucs2 treatment */ + sup->ucs2_treat = 0; /* default */ + /* support extended measurements */ + sup->ext_meas = 0; /* no */ + /* support switched measurement capability */ + sup->meas_cap = 0; /* no */ + //sup->sms_val = ; + //sup->sm_val = ; + + /* radio */ + sup->ch_cap = GSM_CAP_SDCCH_TCHF_TCHH; + sup->min_rxlev_db = -106; // TODO + sup->sync_to = 6; /* how long to wait sync (0.9 s) */ + sup->scan_to = 4; /* how long to wait for all sysinfos (>=4 s) */ + sup->dsc_max = 90; /* the specs defines 90 */ + + /* codec */ + sup->full_v1 = 1; + sup->full_v2 = 1; + sup->full_v3 = 0; + sup->half_v1 = 1; + sup->half_v3 = 0; +} + +/* (3.2.1) maximum channels to scan within each band */ +struct gsm_support_scan_max gsm_sup_smax[] = { + { 259, 293, 15, 0 }, /* GSM 450 */ + { 306, 340, 15, 0 }, /* GSM 480 */ + { 438, 511, 25, 0 }, + { 128, 251, 30, 0 }, + { 955, 124, 30, 0 }, /* P,E,R GSM */ + { 512, 885, 40, 0 }, /* DCS 1800 */ + { 0, 0, 0, 0 } +}; + +#define SUP_SET(item) \ + ((sup->item) ? ((set->item) ? "yes" : "disabled") : "no") +/* dump support */ +void gsm_support_dump(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm_support *sup = &ms->support; + struct gsm_settings *set = &ms->settings; + + print(priv, "Supported features of MS '%s':\n", sup->ms->name); + print(priv, " Phase %d mobile station\n", sup->rev_lev + 1); + print(priv, " R-GSM : %s\n", SUP_SET(r_gsm)); + print(priv, " E-GSM : %s\n", SUP_SET(e_gsm)); + print(priv, " P-GSM : %s\n", SUP_SET(p_gsm)); + print(priv, " GSM900 Class: %d\n", set->class_900); + print(priv, " DCS 1800 : %s\n", SUP_SET(dcs)); + print(priv, " DCS Class : %d\n", set->class_dcs); + print(priv, " CECS : %s\n", (sup->es_ind) ? "yes" : "no"); + print(priv, " VGCS : %s\n", (sup->vgcs) ? "yes" : "no"); + print(priv, " VBS : %s\n", (sup->vbs) ? "yes" : "no"); + print(priv, " SMS : %s\n", SUP_SET(sms_ptp)); + print(priv, " SS_IND : %s\n", (sup->ss_ind) ? "yes" : "no"); + print(priv, " PS_CAP : %s\n", (sup->ps_cap) ? "yes" : "no"); + print(priv, " CMSP : %s\n", (sup->cmsp) ? "yes" : "no"); + print(priv, " SoLSA : %s\n", (sup->solsa) ? "yes" : "no"); + print(priv, " LCSVA : %s\n", (sup->lcsva) ? "yes" : "no"); + print(priv, " LOC_SERV : %s\n", (sup->loc_serv) ? "yes" : "no"); + print(priv, " A5/1 : %s\n", SUP_SET(a5_1)); + print(priv, " A5/2 : %s\n", SUP_SET(a5_2)); + print(priv, " A5/3 : %s\n", SUP_SET(a5_3)); + print(priv, " A5/4 : %s\n", SUP_SET(a5_4)); + print(priv, " A5/5 : %s\n", SUP_SET(a5_5)); + print(priv, " A5/6 : %s\n", SUP_SET(a5_6)); + print(priv, " A5/7 : %s\n", SUP_SET(a5_7)); + print(priv, " A5/1 : %s\n", SUP_SET(a5_1)); + switch (set->ch_cap) { + case GSM_CAP_SDCCH: + print(priv, " Channels : SDCCH only\n"); + break; + case GSM_CAP_SDCCH_TCHF: + print(priv, " Channels : SDCCH + TCH/F\n"); + break; + case GSM_CAP_SDCCH_TCHF_TCHH: + print(priv, " Channels : SDCCH + TCH/F + TCH/H\n"); + break; + } + print(priv, " Full-Rate V1: %s\n", SUP_SET(full_v1)); + print(priv, " Full-Rate V2: %s\n", SUP_SET(full_v2)); + print(priv, " Full-Rate V3: %s\n", SUP_SET(full_v3)); + print(priv, " Half-Rate V1: %s\n", SUP_SET(half_v1)); + print(priv, " Half-Rate V3: %s\n", SUP_SET(half_v3)); + print(priv, " Min RXLEV : %d\n", set->min_rxlev_db); +} + diff --git a/src/host/layer23/src/mobile/transaction.c b/src/host/layer23/src/mobile/transaction.c new file mode 100644 index 00000000..abd3c2d1 --- /dev/null +++ b/src/host/layer23/src/mobile/transaction.c @@ -0,0 +1,143 @@ +/* GSM 04.07 Transaction handling */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +#include <osmocore/talloc.h> +#include <osmocore/timer.h> +#include <osmocore/msgb.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/mobile/mncc.h> +#include <osmocom/bb/mobile/transaction.h> + +extern void *l23_ctx; + +void _gsm48_cc_trans_free(struct gsm_trans *trans); + +struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms, + uint8_t proto, uint8_t trans_id) +{ + struct gsm_trans *trans; + + llist_for_each_entry(trans, &ms->trans_list, entry) { + if (trans->protocol == proto && + trans->transaction_id == trans_id) + return trans; + } + return NULL; +} + +struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, + uint32_t callref) +{ + struct gsm_trans *trans; + + llist_for_each_entry(trans, &ms->trans_list, entry) { + if (trans->callref == callref) + return trans; + } + return NULL; +} + +struct gsm_trans *trans_alloc(struct osmocom_ms *ms, + uint8_t protocol, uint8_t trans_id, + uint32_t callref) +{ + struct gsm_trans *trans; + + trans = talloc_zero(l23_ctx, struct gsm_trans); + if (!trans) + return NULL; + + DEBUGP(DCC, "ms %s allocates transaction (proto %d trans_id %d " + "callref %x mem %p)\n", ms->name, protocol, trans_id, callref, + trans); + + trans->ms = ms; + + trans->protocol = protocol; + trans->transaction_id = trans_id; + trans->callref = callref; + + llist_add_tail(&trans->entry, &ms->trans_list); + + return trans; +} + +void trans_free(struct gsm_trans *trans) +{ + switch (trans->protocol) { + case GSM48_PDISC_CC: + _gsm48_cc_trans_free(trans); + break; +#if 0 + case GSM48_PDISC_SS: + _gsm411_ss_trans_free(trans); + break; + case GSM48_PDISC_SMS: + _gsm411_sms_trans_free(trans); + break; +#endif + } + + DEBUGP(DCC, "ms %s frees transaction (mem %p)\n", trans->ms->name, + trans); + + llist_del(&trans->entry); + + talloc_free(trans); +} + +/* allocate an unused transaction ID + * in the given protocol using the ti_flag specified */ +int trans_assign_trans_id(struct osmocom_ms *ms, + uint8_t protocol, uint8_t ti_flag) +{ + struct gsm_trans *trans; + unsigned int used_tid_bitmask = 0; + int i, j, h; + + if (ti_flag) + ti_flag = 0x8; + + /* generate bitmask of already-used TIDs for this (proto) */ + llist_for_each_entry(trans, &ms->trans_list, entry) { + if (trans->protocol != protocol || + trans->transaction_id == 0xff) + continue; + used_tid_bitmask |= (1 << trans->transaction_id); + } + + /* find a new one, trying to go in a 'circular' pattern */ + for (h = 6; h > 0; h--) + if (used_tid_bitmask & (1 << (h | ti_flag))) + break; + for (i = 0; i < 7; i++) { + j = ((h + i) % 7) | ti_flag; + if ((used_tid_bitmask & (1 << j)) == 0) + return j; + } + + return -1; +} + diff --git a/src/host/layer23/src/mobile/vty_interface.c b/src/host/layer23/src/mobile/vty_interface.c new file mode 100644 index 00000000..4cc22094 --- /dev/null +++ b/src/host/layer23/src/mobile/vty_interface.c @@ -0,0 +1,2418 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <sys/types.h> + +#include <osmocore/utils.h> +#include <osmocore/gsm48.h> +#include <osmocore/talloc.h> +#include <osmocore/signal.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/networks.h> +#include <osmocom/bb/common/gps.h> +#include <osmocom/bb/mobile/mncc.h> +#include <osmocom/bb/mobile/transaction.h> +#include <osmocom/bb/mobile/vty.h> +#include <osmocom/bb/mobile/app_mobile.h> +#include <osmocom/vty/telnet_interface.h> + +void *l23_ctx; + +int mncc_call(struct osmocom_ms *ms, char *number); +int mncc_hangup(struct osmocom_ms *ms); +int mncc_answer(struct osmocom_ms *ms); +int mncc_hold(struct osmocom_ms *ms); +int mncc_retrieve(struct osmocom_ms *ms, int number); +int mncc_dtmf(struct osmocom_ms *ms, char *dtmf); + +extern struct llist_head ms_list; +extern struct llist_head active_connections; + +struct cmd_node ms_node = { + MS_NODE, + "%s(ms)#", + 1 +}; + +struct cmd_node testsim_node = { + TESTSIM_NODE, + "%s(test-sim)#", + 1 +}; + +struct cmd_node support_node = { + SUPPORT_NODE, + "%s(support)#", + 1 +}; + +static void print_vty(void *priv, const char *fmt, ...) +{ + char buffer[1000]; + struct vty *vty = priv; + va_list args; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + if (buffer[0]) { + if (buffer[strlen(buffer) - 1] == '\n') { + buffer[strlen(buffer) - 1] = '\0'; + vty_out(vty, "%s%s", buffer, VTY_NEWLINE); + } else + vty_out(vty, "%s", buffer); + } +} + +int vty_check_number(struct vty *vty, const char *number) +{ + int i; + + for (i = 0; i < strlen(number); i++) { + /* allow international notation with + */ + if (i == 0 && number[i] == '+') + continue; + if (!(number[i] >= '0' && number[i] <= '9') + && number[i] != '*' + && number[i] != '#' + && !(number[i] >= 'a' && number[i] <= 'c')) { + vty_out(vty, "Invalid digit '%c' of number!%s", + number[i], VTY_NEWLINE); + return -EINVAL; + } + } + if (number[0] == '\0' || (number[0] == '+' && number[1] == '\0')) { + vty_out(vty, "Given number has no digits!%s", VTY_NEWLINE); + return -EINVAL; + } + + return 0; +} + +int vty_reading = 0; + +static void vty_restart(struct vty *vty, struct osmocom_ms *ms) +{ + if (vty_reading) + return; + if (ms->shutdown != 0) + return; + vty_out(vty, "You must restart MS '%s' ('shutdown / no shutdown') for " + "change to take effect!%s", ms->name, VTY_NEWLINE); +} + +static struct osmocom_ms *get_ms(const char *name, struct vty *vty) +{ + struct osmocom_ms *ms; + + llist_for_each_entry(ms, &ms_list, entity) { + if (!strcmp(ms->name, name)) { + if (ms->shutdown) { + vty_out(vty, "MS '%s' is admin down.%s", name, + VTY_NEWLINE); + return NULL; + } + return ms; + } + } + vty_out(vty, "MS name '%s' does not exits.%s", name, VTY_NEWLINE); + + return NULL; +} + +static void gsm_ms_dump(struct osmocom_ms *ms, struct vty *vty) +{ + struct gsm_settings *set = &ms->settings; + struct gsm_trans *trans; + char *service = ""; + + if (!ms->started) + service = ", radio is not started"; + else if (ms->mmlayer.state == GSM48_MM_ST_MM_IDLE) { + /* current MM idle state */ + switch (ms->mmlayer.substate) { + case GSM48_MM_SST_NORMAL_SERVICE: + case GSM48_MM_SST_PLMN_SEARCH_NORMAL: + service = ", service is normal"; + break; + case GSM48_MM_SST_LOC_UPD_NEEDED: + case GSM48_MM_SST_ATTEMPT_UPDATE: + service = ", service is limited (pending)"; + break; + case GSM48_MM_SST_NO_CELL_AVAIL: + service = ", service is unavailable"; + break; + default: + if (ms->subscr.sim_valid) + service = ", service is limited"; + else + service = ", service is limited " + "(IMSI detached)"; + break; + } + } else + service = ", MM connection active"; + + vty_out(vty, "MS '%s' is %s%s%s%s", ms->name, + (ms->shutdown) ? "administratively " : "", + (ms->shutdown || !ms->started) ? "down" : "up", + (!ms->shutdown) ? service : "", + VTY_NEWLINE); + vty_out(vty, " IMEI: %s%s", set->imei, VTY_NEWLINE); + vty_out(vty, " IMEISV: %s%s", set->imeisv, VTY_NEWLINE); + if (set->imei_random) + vty_out(vty, " IMEI generation: random (%d trailing " + "digits)%s", set->imei_random, VTY_NEWLINE); + else + vty_out(vty, " IMEI generation: fixed%s", VTY_NEWLINE); + + if (ms->shutdown) + return; + + if (set->plmn_mode == PLMN_MODE_AUTO) + vty_out(vty, " automatic network selection state: %s%s", + plmn_a_state_names[ms->plmn.state], VTY_NEWLINE); + else + vty_out(vty, " manual network selection state: %s%s", + plmn_m_state_names[ms->plmn.state], VTY_NEWLINE); + vty_out(vty, " cell selection state: %s", + cs_state_names[ms->cellsel.state]); + if (ms->rrlayer.state == GSM48_RR_ST_IDLE && ms->cellsel.selected) + vty_out(vty, " (ARFCN %d)", ms->cellsel.sel_arfcn); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, " radio ressource layer state: %s%s", + gsm48_rr_state_names[ms->rrlayer.state], VTY_NEWLINE); + vty_out(vty, " mobility management layer state: %s", + gsm48_mm_state_names[ms->mmlayer.state]); + if (ms->mmlayer.state == GSM48_MM_ST_MM_IDLE) + vty_out(vty, ", %s", + gsm48_mm_substate_names[ms->mmlayer.substate]); + vty_out(vty, "%s", VTY_NEWLINE); + llist_for_each_entry(trans, &ms->trans_list, entry) { + vty_out(vty, " call control state: %s%s", + gsm48_cc_state_name(trans->cc.state), VTY_NEWLINE); + } +} + + +DEFUN(show_ms, show_ms_cmd, "show ms [MS_NAME]", + SHOW_STR "Display available MS entities\n") +{ + struct osmocom_ms *ms; + + if (argc) { + llist_for_each_entry(ms, &ms_list, entity) { + if (!strcmp(ms->name, argv[0])) { + gsm_ms_dump(ms, vty); + return CMD_SUCCESS; + } + } + vty_out(vty, "MS name '%s' does not exits.%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } else { + llist_for_each_entry(ms, &ms_list, entity) { + gsm_ms_dump(ms, vty); + vty_out(vty, "%s", VTY_NEWLINE); + } + } + + return CMD_SUCCESS; +} + +DEFUN(show_support, show_support_cmd, "show support [MS_NAME]", + SHOW_STR "Display information about MS support\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + if (argc) { + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + gsm_support_dump(ms, print_vty, vty); + } else { + llist_for_each_entry(ms, &ms_list, entity) { + gsm_support_dump(ms, print_vty, vty); + vty_out(vty, "%s", VTY_NEWLINE); + } + } + + return CMD_SUCCESS; +} + +DEFUN(show_subscr, show_subscr_cmd, "show subscriber [MS_NAME]", + SHOW_STR "Display information about subscriber\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + if (argc) { + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + gsm_subscr_dump(&ms->subscr, print_vty, vty); + } else { + llist_for_each_entry(ms, &ms_list, entity) { + if (!ms->shutdown) { + gsm_subscr_dump(&ms->subscr, print_vty, vty); + vty_out(vty, "%s", VTY_NEWLINE); + } + } + } + + return CMD_SUCCESS; +} + +DEFUN(show_cell, show_cell_cmd, "show cell MS_NAME", + SHOW_STR "Display information about received cells\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + gsm322_dump_cs_list(&ms->cellsel, GSM322_CS_FLAG_SYSINFO, print_vty, + vty); + + return CMD_SUCCESS; +} + +DEFUN(show_cell_si, show_cell_si_cmd, "show cell MS_NAME <0-1023>", + SHOW_STR "Display information about received cell\n" + "Name of MS (see \"show ms\")\nRadio frequency number") +{ + struct osmocom_ms *ms; + int i; + struct gsm48_sysinfo *s; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + i = atoi(argv[1]); + if (i < 0 || i > 1023) { + vty_out(vty, "Given ARFCN '%s' not in range (0..1023)%s", + argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + s = ms->cellsel.list[i].sysinfo; + if (!s) { + vty_out(vty, "Given ARFCN '%s' has no sysinfo available%s", + argv[1], VTY_NEWLINE); + return CMD_SUCCESS; + } + + gsm48_sysinfo_dump(s, i, print_vty, vty); + + return CMD_SUCCESS; +} + +DEFUN(show_ba, show_ba_cmd, "show ba MS_NAME [MCC] [MNC]", + SHOW_STR "Display information about band allocations\n" + "Name of MS (see \"show ms\")\nMobile Country Code\n" + "Mobile Network Code") +{ + struct osmocom_ms *ms; + uint16_t mcc = 0, mnc = 0; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (argc >= 3) { + mcc = gsm_input_mcc((char *)argv[1]); + mnc = gsm_input_mnc((char *)argv[2]); + if (!mcc) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!mnc) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + gsm322_dump_ba_list(&ms->cellsel, mcc, mnc, print_vty, vty); + + return CMD_SUCCESS; +} + +DEFUN(show_forb_plmn, show_forb_plmn_cmd, "show forbidden plmn MS_NAME", + SHOW_STR "Display information about forbidden cells / networks\n" + "Display forbidden PLMNs\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + gsm_subscr_dump_forbidden_plmn(ms, print_vty, vty); + + return CMD_SUCCESS; +} + +DEFUN(show_forb_la, show_forb_la_cmd, "show forbidden location-area MS_NAME", + SHOW_STR "Display information about forbidden cells / networks\n" + "Display forbidden location areas\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + gsm322_dump_forbidden_la(ms, print_vty, vty); + + return CMD_SUCCESS; +} + +DEFUN(monitor_network, monitor_network_cmd, "monitor network MS_NAME", + "Monitor...\nMonitor network information\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + gsm48_rr_start_monitor(ms); + + return CMD_SUCCESS; +} + +DEFUN(no_monitor_network, no_monitor_network_cmd, "no monitor network MS_NAME", + NO_STR "Monitor...\nDeactivate monitor of network information\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + gsm48_rr_stop_monitor(ms); + + return CMD_SUCCESS; +} + +DEFUN(sim_test, sim_test_cmd, "sim testcard MS_NAME [MCC] [MNC] [LAC] [TMSI]", + "SIM actions\nInsert test card\nName of MS (see \"show ms\")\n" + "Mobile Country Code of RPLMN\nMobile Network Code of RPLMN\n" + "Optionally locatio area code\nOptionally current assigned TMSI") +{ + struct osmocom_ms *ms; + uint16_t mcc = 0x001, mnc = 0x01f, lac = 0x0000; + uint32_t tmsi = 0xffffffff; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (ms->subscr.sim_valid) { + vty_out(vty, "SIM already presend, remove first!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc >= 3) { + mcc = gsm_input_mcc((char *)argv[1]); + mnc = gsm_input_mnc((char *)argv[2]); + if (!mcc) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!mnc) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (argc >= 4) + lac = strtoul(argv[3], NULL, 16); + + if (argc >= 5) + tmsi = strtoul(argv[4], NULL, 16); + + gsm_subscr_testcard(ms, mcc, mnc, lac, tmsi); + + return CMD_SUCCESS; +} + +DEFUN(sim_reader, sim_reader_cmd, "sim reader MS_NAME", + "SIM actions\nSelect SIM from reader\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (ms->subscr.sim_valid) { + vty_out(vty, "SIM already presend, remove first!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_simcard(ms); + + return CMD_SUCCESS; +} + +DEFUN(sim_remove, sim_remove_cmd, "sim remove MS_NAME", + "SIM actions\nRemove SIM card\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (!ms->subscr.sim_valid) { + vty_out(vty, "No Sim inserted!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_remove(ms); + + return CMD_SUCCESS; +} + +DEFUN(sim_pin, sim_pin_cmd, "sim pin MS_NAME PIN", + "SIM actions\nEnter PIN for SIM card\nName of MS (see \"show ms\")\n" + "PIN number") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { + vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!ms->subscr.sim_pin_required) { + vty_out(vty, "No PIN is required at this time!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_sim_pin(ms, (char *)argv[1], "", 0); + + return CMD_SUCCESS; +} + +DEFUN(sim_disable_pin, sim_disable_pin_cmd, "sim disable-pin MS_NAME PIN", + "SIM actions\nDisable PIN of SIM card\nName of MS (see \"show ms\")\n" + "PIN number") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { + vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_sim_pin(ms, (char *)argv[1], "", -1); + + return CMD_SUCCESS; +} + +DEFUN(sim_enable_pin, sim_enable_pin_cmd, "sim enable-pin MS_NAME PIN", + "SIM actions\nEnable PIN of SIM card\nName of MS (see \"show ms\")\n" + "PIN number") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { + vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_sim_pin(ms, (char *)argv[1], "", 1); + + return CMD_SUCCESS; +} + +DEFUN(sim_change_pin, sim_change_pin_cmd, "sim change-pin MS_NAME OLD NEW", + "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n" + "Old PIN number\nNew PIN number") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { + vty_out(vty, "Old PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) { + vty_out(vty, "New PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 2); + + return CMD_SUCCESS; +} + +DEFUN(sim_unblock_pin, sim_unblock_pin_cmd, "sim unblock-pin MS_NAME PUC NEW", + "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n" + "Personal Unblock Key\nNew PIN number") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (strlen(argv[1]) != 8) { + vty_out(vty, "PUC must be 8 digits!%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) { + vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 99); + + return CMD_SUCCESS; +} + +DEFUN(sim_lai, sim_lai_cmd, "sim lai MS_NAME MCC MNC LAC", + "SIM actions\nChange LAI of SIM card\nName of MS (see \"show ms\")\n" + "Mobile Country Code\nMobile Network Code\nLocation Area Code " + " (use 0000 to remove LAI)") +{ + struct osmocom_ms *ms; + uint16_t mcc = gsm_input_mcc((char *)argv[1]), + mnc = gsm_input_mnc((char *)argv[2]), + lac = strtoul(argv[3], NULL, 16); + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (!mcc) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!mnc) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ms->subscr.mcc = mcc; + ms->subscr.mnc = mnc; + ms->subscr.lac = lac; + ms->subscr.tmsi = 0xffffffff; + + gsm_subscr_write_loci(ms); + + return CMD_SUCCESS; +} + +DEFUN(network_select, network_select_cmd, "network select MS_NAME MCC MNC", + "Select ...\nSelect Network\nName of MS (see \"show ms\")\n" + "Mobile Country Code\nMobile Network Code") +{ + struct osmocom_ms *ms; + struct gsm322_plmn *plmn; + struct msgb *nmsg; + struct gsm322_msg *ngm; + struct gsm322_plmn_list *temp; + uint16_t mcc = gsm_input_mcc((char *)argv[1]), + mnc = gsm_input_mnc((char *)argv[2]); + int found = 0; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + plmn = &ms->plmn; + + if (!mcc) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!mnc) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) + if (temp->mcc == mcc && temp->mnc == mnc) + found = 1; + if (!found) { + vty_out(vty, "Network not in list!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CHOOSE_PLMN); + if (!nmsg) + return CMD_WARNING; + ngm = (struct gsm322_msg *) nmsg->data; + ngm->mcc = mcc; + ngm->mnc = mnc; + gsm322_plmn_sendmsg(ms, nmsg); + + return CMD_SUCCESS; +} + +DEFUN(call, call_cmd, "call MS_NAME (NUMBER|emergency|answer|hangup|hold)", + "Make a call\nName of MS (see \"show ms\")\nPhone number to call " + "(Use digits '0123456789*#abc', and '+' to dial international)\n" + "Make an emergency call\nAnswer an incomming call\nHangup a call\n" + "Hold current active call\n") +{ + struct osmocom_ms *ms; + struct gsm_settings *set; + struct gsm_settings_abbrev *abbrev; + char *number; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + set = &ms->settings; + + if (set->ch_cap == GSM_CAP_SDCCH) { + vty_out(vty, "Basic call is not supported for SDCCH only " + "mobile%s", VTY_NEWLINE); + return CMD_WARNING; + } + + number = (char *)argv[1]; + if (!strcmp(number, "emergency")) + mncc_call(ms, number); + else if (!strcmp(number, "answer")) + mncc_answer(ms); + else if (!strcmp(number, "hangup")) + mncc_hangup(ms); + else if (!strcmp(number, "hold")) + mncc_hold(ms); + else { + llist_for_each_entry(abbrev, &set->abbrev, list) { + if (!strcmp(number, abbrev->abbrev)) { + number = abbrev->number; + vty_out(vty, "Dialing number '%s'%s", number, + VTY_NEWLINE); + break; + } + } + if (vty_check_number(vty, number)) + return CMD_WARNING; + mncc_call(ms, number); + } + + return CMD_SUCCESS; +} + +DEFUN(call_retr, call_retr_cmd, "call MS_NAME retrieve [NUMBER]", + "Make a call\nName of MS (see \"show ms\")\n" + "Retrieve call on hold\nNumber of call to retrieve") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + mncc_retrieve(ms, (argc > 1) ? atoi(argv[1]) : 0); + + return CMD_SUCCESS; +} + +DEFUN(call_dtmf, call_dtmf_cmd, "call MS_NAME dtmf DIGITS", + "Make a call\nName of MS (see \"show ms\")\n" + "One or more DTMF digits to transmit") +{ + struct osmocom_ms *ms; + struct gsm_settings *set; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + set = &ms->settings; + + if (!set->cc_dtmf) { + vty_out(vty, "DTMF not supported, please enable!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + mncc_dtmf(ms, (char *)argv[1]); + + return CMD_SUCCESS; +} + +DEFUN(network_show, network_show_cmd, "network show MS_NAME", + "Network ...\nShow results of network search (again)\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + struct gsm_settings *set; + struct gsm322_plmn *plmn; + struct gsm322_plmn_list *temp; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + set = &ms->settings; + plmn = &ms->plmn; + + if (set->plmn_mode != PLMN_MODE_AUTO + && plmn->state != GSM322_M3_NOT_ON_PLMN) { + vty_out(vty, "Start network search first!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) + vty_out(vty, " Network %s, %s (%s, %s)%s", + gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), + gsm_get_mcc(temp->mcc), + gsm_get_mnc(temp->mcc, temp->mnc), VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(network_search, network_search_cmd, "network search MS_NAME", + "Network ...\nTrigger network search\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + struct msgb *nmsg; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_USER_RESEL); + if (!nmsg) + return CMD_WARNING; + gsm322_plmn_sendmsg(ms, nmsg); + + return CMD_SUCCESS; +} + +DEFUN(cfg_gps_enable, cfg_gps_enable_cmd, "gps enable", + "GPS receiver") +{ + if (gps_open()) { + gps.enable = 1; + vty_out(vty, "Failed to open GPS device!%s", VTY_NEWLINE); + return CMD_WARNING; + } + gps.enable = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_gps_enable, cfg_no_gps_enable_cmd, "no gps enable", + NO_STR "Disable GPS receiver") +{ + if (gps.enable) + gps_close(); + gps.enable = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_gps_device, cfg_gps_device_cmd, "gps device DEVICE", + "GPS receiver\nSelect serial device\n" + "Full path of serial device including /dev/") +{ + strncpy(gps.device, argv[0], sizeof(gps.device)); + gps.device[sizeof(gps.device) - 1] = '\0'; + if (gps.enable) { + gps_close(); + if (gps_open()) { + vty_out(vty, "Failed to open GPS device!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_gps_baud, cfg_gps_baud_cmd, "gps baudrate " + "(default|4800|""9600|19200|38400|57600|115200)", + "GPS receiver\nSelect baud rate\nDefault, don't modify\n\n\n\n\n\n") +{ + if (argv[0][0] == 'd') + gps.baud = 0; + else + gps.baud = atoi(argv[0]); + if (gps.enable) { + gps_close(); + if (gps_open()) { + gps.enable = 0; + vty_out(vty, "Failed to open GPS device!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +/* per MS config */ +DEFUN(cfg_ms, cfg_ms_cmd, "ms MS_NAME", + "Select a mobile station to configure\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + int found = 0; + + llist_for_each_entry(ms, &ms_list, entity) { + if (!strcmp(ms->name, argv[0])) { + found = 1; + break; + } + } + + if (!found) { + if (!vty_reading) { + vty_out(vty, "MS name '%s' does not exits, try " + "'ms %s create'%s", argv[0], argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + ms = mobile_new((char *)argv[0]); + if (!ms) { + vty_out(vty, "Failed to add MS name '%s'%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + } + + vty->index = ms; + vty->node = MS_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_create, cfg_ms_create_cmd, "ms MS_NAME create", + "Select a mobile station to configure\nName of MS (see \"show ms\")\n" + "Create if MS does not exists") +{ + struct osmocom_ms *ms; + int found = 0; + + llist_for_each_entry(ms, &ms_list, entity) { + if (!strcmp(ms->name, argv[0])) { + found = 1; + break; + } + } + + if (!found) { + ms = mobile_new((char *)argv[0]); + if (!ms) { + vty_out(vty, "Failed to add MS name '%s'%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + } + + vty->index = ms; + vty->node = MS_NODE; + + vty_out(vty, "MS '%s' created, after configuration, do 'no shutdown'%s", + argv[0], VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_rename, cfg_ms_rename_cmd, "ms MS_NAME rename MS_NAME", + "Select a mobile station to configure\nName of MS (see \"show ms\")\n" + "Rename MS\nNew name of MS") +{ + struct osmocom_ms *ms; + int found = 0; + + llist_for_each_entry(ms, &ms_list, entity) { + if (!strcmp(ms->name, argv[0])) { + found = 1; + break; + } + } + + if (!found) { + vty_out(vty, "MS name '%s' does not exist%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + strncpy(ms->name, argv[1], sizeof(ms->name) - 1); + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_ms, cfg_no_ms_cmd, "no ms MS_NAME", + NO_STR "Select a mobile station to remove\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + int found = 0; + + llist_for_each_entry(ms, &ms_list, entity) { + if (!strcmp(ms->name, argv[0])) { + found = 1; + break; + } + } + + if (!found) { + vty_out(vty, "MS name '%s' does not exist%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + mobile_delete(ms, 1); + + return CMD_SUCCESS; +} + +#define SUP_WRITE(item, cmd) \ + if (sup->item) \ + vty_out(vty, " %s%s%s", (set->item) ? "" : "no ", cmd, \ + VTY_NEWLINE); + +static void config_write_ms(struct vty *vty, struct osmocom_ms *ms) +{ + struct gsm_settings *set = &ms->settings; + struct gsm_support *sup = &ms->support; + struct gsm_settings_abbrev *abbrev; + + vty_out(vty, "ms %s%s", ms->name, VTY_NEWLINE); + vty_out(vty, " layer2-socket %s%s", set->layer2_socket_path, + VTY_NEWLINE); + vty_out(vty, " sap-socket %s%s", set->sap_socket_path, VTY_NEWLINE); + switch(set->sim_type) { + case GSM_SIM_TYPE_NONE: + vty_out(vty, " sim none%s", VTY_NEWLINE); + break; + case GSM_SIM_TYPE_READER: + vty_out(vty, " sim reader%s", VTY_NEWLINE); + break; + case GSM_SIM_TYPE_TEST: + vty_out(vty, " sim test%s", VTY_NEWLINE); + break; + } + vty_out(vty, " network-selection-mode %s%s", (set->plmn_mode + == PLMN_MODE_AUTO) ? "auto" : "manual", VTY_NEWLINE); + vty_out(vty, " imei %s %s%s", set->imei, + set->imeisv + strlen(set->imei), VTY_NEWLINE); + if (set->imei_random) + vty_out(vty, " imei-random %d%s", set->imei_random, + VTY_NEWLINE); + else + vty_out(vty, " imei-fixed%s", VTY_NEWLINE); + if (set->emergency_imsi[0]) + vty_out(vty, " emergency-imsi %s%s", set->emergency_imsi, + VTY_NEWLINE); + else + vty_out(vty, " no emergency-imsi%s", VTY_NEWLINE); + vty_out(vty, " %scall-waiting%s", (set->cw) ? "" : "no ", VTY_NEWLINE); + vty_out(vty, " %sauto-answer%s", (set->auto_answer) ? "" : "no ", + VTY_NEWLINE); + vty_out(vty, " %sclip%s", (set->clip) ? "" : "no ", VTY_NEWLINE); + vty_out(vty, " %sclir%s", (set->clir) ? "" : "no ", VTY_NEWLINE); + if (set->alter_tx_power) + if (set->alter_tx_power_value) + vty_out(vty, " tx-power %d%s", + set->alter_tx_power_value, VTY_NEWLINE); + else + vty_out(vty, " tx-power full%s", VTY_NEWLINE); + else + vty_out(vty, " tx-power auto%s", VTY_NEWLINE); + if (set->alter_delay) + vty_out(vty, " simulated-delay %d%s", set->alter_delay, + VTY_NEWLINE); + else + vty_out(vty, " no simulated-delay%s", VTY_NEWLINE); + if (set->stick) + vty_out(vty, " stick %d%s", set->stick_arfcn, + VTY_NEWLINE); + else + vty_out(vty, " no stick%s", VTY_NEWLINE); + if (set->no_lupd) + vty_out(vty, " no location-updating%s", VTY_NEWLINE); + else + vty_out(vty, " location-updating%s", VTY_NEWLINE); + if (set->full_v1 || set->full_v2 || set->full_v3) { + /* mandatory anyway */ + vty_out(vty, " codec full-speed%s%s", + (!set->half_prefer) ? " prefer" : "", + VTY_NEWLINE); + } + if (set->half_v1 || set->half_v3) { + if (set->half) + vty_out(vty, " codec half-speed%s%s", + (set->half_prefer) ? " prefer" : "", + VTY_NEWLINE); + else + vty_out(vty, " no codec half-speed%s", VTY_NEWLINE); + } + if (llist_empty(&set->abbrev)) + vty_out(vty, " no abbrev%s", VTY_NEWLINE); + else { + llist_for_each_entry(abbrev, &set->abbrev, list) + vty_out(vty, " abbrev %s %s%s%s%s", abbrev->abbrev, + abbrev->number, (abbrev->name[0]) ? " " : "", + abbrev->name, VTY_NEWLINE); + } + vty_out(vty, " support%s", VTY_NEWLINE); + SUP_WRITE(sms_ptp, "sms"); + SUP_WRITE(a5_1, "a5/1"); + SUP_WRITE(a5_2, "a5/2"); + SUP_WRITE(a5_3, "a5/3"); + SUP_WRITE(a5_4, "a5/4"); + SUP_WRITE(a5_5, "a5/5"); + SUP_WRITE(a5_6, "a5/6"); + SUP_WRITE(a5_7, "a5/7"); + SUP_WRITE(p_gsm, "p-gsm"); + SUP_WRITE(e_gsm, "e-gsm"); + SUP_WRITE(r_gsm, "r-gsm"); + SUP_WRITE(dcs, "dcs"); + vty_out(vty, " class-900 %d%s", set->class_900, VTY_NEWLINE); + vty_out(vty, " class-dcs %d%s", set->class_dcs, VTY_NEWLINE); + switch (set->ch_cap) { + case GSM_CAP_SDCCH: + vty_out(vty, " channel-capability sdcch%s", VTY_NEWLINE); + break; + case GSM_CAP_SDCCH_TCHF: + vty_out(vty, " channel-capability sdcch+tchf%s", VTY_NEWLINE); + break; + case GSM_CAP_SDCCH_TCHF_TCHH: + vty_out(vty, " channel-capability sdcch+tchf+tchh%s", + VTY_NEWLINE); + break; + } + SUP_WRITE(full_v1, "full-speech-v1"); + SUP_WRITE(full_v2, "full-speech-v2"); + SUP_WRITE(full_v3, "full-speech-v3"); + SUP_WRITE(half_v1, "half-speech-v1"); + SUP_WRITE(half_v3, "half-speech-v3"); + vty_out(vty, " min-rxlev %d%s", set->min_rxlev_db, VTY_NEWLINE); + vty_out(vty, " dsc-max %d%s", set->dsc_max, VTY_NEWLINE); + vty_out(vty, " exit%s", VTY_NEWLINE); + vty_out(vty, " test-sim%s", VTY_NEWLINE); + vty_out(vty, " imsi %s%s", set->test_imsi, VTY_NEWLINE); + switch (set->test_ki_type) { + case GSM_SIM_KEY_XOR: + vty_out(vty, " ki xor %s%s", hexdump(set->test_ki, 12), + VTY_NEWLINE); + break; + case GSM_SIM_KEY_COMP128: + vty_out(vty, " ki comp128 %s%s", hexdump(set->test_ki, 16), + VTY_NEWLINE); + break; + } + vty_out(vty, " %sbarred-access%s", (set->test_barr) ? "" : "no ", + VTY_NEWLINE); + if (set->test_rplmn_valid) { + vty_out(vty, " rplmn %s %s", + gsm_print_mcc(set->test_rplmn_mcc), + gsm_print_mnc(set->test_rplmn_mnc)); + if (set->test_lac > 0x0000 && set->test_lac < 0xfffe) + vty_out(vty, " 0x%04x", set->test_lac); + if (set->test_tmsi != 0xffffffff) + vty_out(vty, " 0x%08x", set->test_tmsi); + vty_out(vty, "%s", VTY_NEWLINE); + } else + vty_out(vty, " no rplmn%s", VTY_NEWLINE); + vty_out(vty, " hplmn-search %s%s", (set->test_always) ? "everywhere" + : "foreign-country", VTY_NEWLINE); + vty_out(vty, " exit%s", VTY_NEWLINE); + vty_out(vty, " %sshutdown%s", (ms->shutdown) ? "" : "no ", + VTY_NEWLINE); + vty_out(vty, "exit%s", VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); +} + +static int config_write(struct vty *vty) +{ + struct osmocom_ms *ms; + + vty_out(vty, "gps device %s%s", gps.device, VTY_NEWLINE); + if (gps.baud) + vty_out(vty, "gps baudrate %d%s", gps.baud, VTY_NEWLINE); + else + vty_out(vty, "gps baudrate default%s", VTY_NEWLINE); + vty_out(vty, "%sgps enable%s", (gps.enable) ? "" : "no ", VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); + + llist_for_each_entry(ms, &ms_list, entity) + config_write_ms(vty, ms); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_show_this, cfg_ms_show_this_cmd, "show this", + SHOW_STR "Show config of this MS") +{ + struct osmocom_ms *ms = vty->index; + + config_write_ms(vty, ms); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_layer2, cfg_ms_layer2_cmd, "layer2-socket PATH", + "Define socket path to connect between layer 2 and layer 1\n" + "Unix socket, default '/tmp/osmocom_l2'") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + strncpy(set->layer2_socket_path, argv[0], + sizeof(set->layer2_socket_path) - 1); + + vty_restart(vty, ms); + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_sap, cfg_ms_sap_cmd, "sap-socket PATH", + "Define socket path to connect to SIM reader\n" + "Unix socket, default '/tmp/osmocom_sap'") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + strncpy(set->sap_socket_path, argv[0], + sizeof(set->sap_socket_path) - 1); + + vty_restart(vty, ms); + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_sim, cfg_ms_sim_cmd, "sim (none|reader|test)", + "Set SIM card type when powering on\nNo SIM interted\n" + "Use SIM from reader\nTest SIM inserted") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + switch (argv[0][0]) { + case 'n': + set->sim_type = GSM_SIM_TYPE_NONE; + break; + case 'r': + set->sim_type = GSM_SIM_TYPE_READER; + break; + case 't': + set->sim_type = GSM_SIM_TYPE_TEST; + break; + default: + vty_out(vty, "unknown SIM type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vty_restart(vty, ms); + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_mode, cfg_ms_mode_cmd, "network-selection-mode (auto|manual)", + "Set network selection mode\nAutomatic network selection\n" + "Manual network selection") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + struct msgb *nmsg; + + if (!ms->plmn.state) { + if (argv[0][0] == 'a') + set->plmn_mode = PLMN_MODE_AUTO; + else + set->plmn_mode = PLMN_MODE_MANUAL; + + return CMD_SUCCESS; + } + if (argv[0][0] == 'a') + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SEL_AUTO); + else + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SEL_MANUAL); + if (!nmsg) + return CMD_WARNING; + gsm322_plmn_sendmsg(ms, nmsg); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_imei, cfg_ms_imei_cmd, "imei IMEI [SV]", + "Set IMEI (enter without control digit)\n15 Digits IMEI\n" + "Software version digit") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + char *error, *sv = "0"; + + if (argc >= 2) + sv = (char *)argv[1]; + + error = gsm_check_imei(argv[0], sv); + if (error) { + vty_out(vty, "%s%s", error, VTY_NEWLINE); + return CMD_WARNING; + } + + strcpy(set->imei, argv[0]); + strcpy(set->imeisv, argv[0]); + strcpy(set->imeisv + 15, sv); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_imei_fixed, cfg_ms_imei_fixed_cmd, "imei-fixed", + "Use fixed IMEI on every power on") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->imei_random = 0; + + vty_restart(vty, ms); + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_imei_random, cfg_ms_imei_random_cmd, "imei-random <0-15>", + "Use random IMEI on every power on\n" + "Number of trailing digits to randomize") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->imei_random = atoi(argv[0]); + + vty_restart(vty, ms); + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_emerg_imsi, cfg_ms_emerg_imsi_cmd, "emergency-imsi IMSI", + "Use special IMSI for emergency calls\n15 digits IMSI") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + char *error; + + error = gsm_check_imsi(argv[0]); + if (error) { + vty_out(vty, "%s%s", error, VTY_NEWLINE); + return CMD_WARNING; + } + strcpy(set->emergency_imsi, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_no_emerg_imsi, cfg_ms_no_emerg_imsi_cmd, "no emergency-imsi", + NO_STR "Use IMSI of SIM or IMEI for emergency calls") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->emergency_imsi[0] = '\0'; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_cw, cfg_ms_no_cw_cmd, "no call-waiting", + NO_STR "Disallow waiting calls") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->cw = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_cw, cfg_ms_cw_cmd, "call-waiting", + "Allow waiting calls") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->cw = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_auto_answer, cfg_ms_no_auto_answer_cmd, "no auto-answer", + NO_STR "Disable auto-answering calls") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->auto_answer = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_auto_answer, cfg_ms_auto_answer_cmd, "auto-answer", + "Enable auto-answering calls") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->auto_answer = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_clip, cfg_ms_clip_cmd, "clip", + "Force caller ID presentation") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->clip = 1; + set->clir = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_clir, cfg_ms_clir_cmd, "clir", + "Force caller ID restriction") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->clip = 0; + set->clir = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_clip, cfg_ms_no_clip_cmd, "no clip", + NO_STR "Disable forcing of caller ID presentation") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->clip = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_clir, cfg_ms_no_clir_cmd, "no clir", + NO_STR "Disable forcing of caller ID restriction") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->clir = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_tx_power, cfg_ms_tx_power_cmd, "tx-power (auto|full)", + "Set the way to choose transmit power\nControlled by BTS\n" + "Always full power\nFixed GSM power value if supported") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + switch (argv[0][0]) { + case 'a': + set->alter_tx_power = 0; + break; + case 'f': + set->alter_tx_power = 1; + set->alter_tx_power_value = 0; + break; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_tx_power_val, cfg_ms_tx_power_val_cmd, "tx-power <0-31>", + "Set the way to choose transmit power\n" + "Fixed GSM power value if supported") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->alter_tx_power = 1; + set->alter_tx_power_value = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_sim_delay, cfg_ms_sim_delay_cmd, "simulated-delay <-128-127>", + "Simulate a lower or higher distance from the BTS\n" + "Delay in half bits (distance in 553.85 meter steps)") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->alter_delay = atoi(argv[0]); + gsm48_rr_alter_delay(ms); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_no_sim_delay, cfg_ms_no_sim_delay_cmd, "no simulated-delay", + NO_STR "Do not simulate a lower or higher distance from the BTS") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->alter_delay = 0; + gsm48_rr_alter_delay(ms); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_stick, cfg_ms_stick_cmd, "stick <0-1023>", + "Stick to the given cell\nARFCN of the cell to stick to") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->stick = 1; + set->stick_arfcn = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_no_stick, cfg_ms_no_stick_cmd, "no stick", + NO_STR "Do not stick to any cell") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->stick = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_lupd, cfg_ms_lupd_cmd, "location-updating", + "Allow location updating") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->no_lupd = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_no_lupd, cfg_ms_no_lupd_cmd, "no location-updating", + NO_STR "Do not allow location updating") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->no_lupd = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_codec_full, cfg_ms_codec_full_cmd, "codec full-speed", + "Enable codec\nFull speed speech codec") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + if (!set->full_v1 && !set->full_v2 && !set->full_v3) { + vty_out(vty, "Full-rate codec not supported%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_codec_full_pref, cfg_ms_codec_full_pref_cmd, "codec full-speed " + "prefer", + "Enable codec\nFull speed speech codec\nPrefer this codec") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + if (!set->full_v1 && !set->full_v2 && !set->full_v3) { + vty_out(vty, "Full-rate codec not supported%s", VTY_NEWLINE); + return CMD_WARNING; + } + + set->half_prefer = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_codec_half, cfg_ms_codec_half_cmd, "codec half-speed", + "Enable codec\nHalf speed speech codec") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + if (!set->half_v1 && !set->half_v3) { + vty_out(vty, "Half-rate codec not supported%s", VTY_NEWLINE); + return CMD_WARNING; + } + + set->half = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_codec_half_pref, cfg_ms_codec_half_pref_cmd, "codec half-speed " + "prefer", + "Enable codec\nHalf speed speech codec\nPrefer this codec") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + if (!set->half_v1 && !set->half_v3) { + vty_out(vty, "Half-rate codec not supported%s", VTY_NEWLINE); + return CMD_WARNING; + } + + set->half = 1; + set->half_prefer = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_codec_half, cfg_ms_no_codec_half_cmd, "no codec half-speed", + NO_STR "Disable codec\nHalf speed speech codec") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + if (!set->half_v1 && !set->half_v3) { + vty_out(vty, "Half-rate codec not supported%s", VTY_NEWLINE); + return CMD_WARNING; + } + + set->half = 0; + set->half_prefer = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_abbrev, cfg_ms_abbrev_cmd, "abbrev ABBREVIATION NUMBER [NAME]", + "Store given abbreviation number\n1-3 digits abbreviation\n" + "Number to store for the abbreviation " + "(Use digits '0123456789*#abc', and '+' to dial international)\n" + "Name of the abbreviation") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + struct gsm_settings_abbrev *abbrev; + int i; + + llist_for_each_entry(abbrev, &set->abbrev, list) { + if (!strcmp(argv[0], abbrev->abbrev)) { + vty_out(vty, "Given abbreviation '%s' already stored, " + "delete first!%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (strlen(argv[0]) >= sizeof(abbrev->abbrev)) { + vty_out(vty, "Given abbreviation too long%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (i = 0; i < strlen(argv[0]); i++) { + if (argv[0][i] < '0' || argv[0][i] > '9') { + vty_out(vty, "Given abbreviation must have digits " + "0..9 only!%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (vty_check_number(vty, argv[1])) + return CMD_WARNING; + + abbrev = talloc_zero(l23_ctx, struct gsm_settings_abbrev); + if (!abbrev) { + vty_out(vty, "No Memory!%s", VTY_NEWLINE); + return CMD_WARNING; + } + llist_add_tail(&abbrev->list, &set->abbrev); + strncpy(abbrev->abbrev, argv[0], sizeof(abbrev->abbrev) - 1); + strncpy(abbrev->number, argv[1], sizeof(abbrev->number) - 1); + if (argc >= 3) + strncpy(abbrev->name, argv[2], sizeof(abbrev->name) - 1); + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_abbrev, cfg_ms_no_abbrev_cmd, "no abbrev [ABBREVIATION]", + NO_STR "Remove given abbreviation number or all numbers\n" + "Abbreviation number to remove") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + struct gsm_settings_abbrev *abbrev, *abbrev2; + uint8_t deleted = 0; + + llist_for_each_entry_safe(abbrev, abbrev2, &set->abbrev, list) { + if (argc < 1 || !strcmp(argv[0], abbrev->abbrev)) { + llist_del(&abbrev->list); + deleted = 1; + } + } + + if (argc >= 1 && !deleted) { + vty_out(vty, "Given abbreviation '%s' not found!%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +static int config_write_dummy(struct vty *vty) +{ + return CMD_SUCCESS; +} + +/* per support config */ +DEFUN(cfg_ms_support, cfg_ms_support_cmd, "support", + "Define supported features") +{ + vty->node = SUPPORT_NODE; + + return CMD_SUCCESS; +} + +#define SUP_EN(cfg, cfg_cmd, item, cmd, desc, restart) \ +DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \ +{ \ + struct osmocom_ms *ms = vty->index; \ + struct gsm_settings *set = &ms->settings; \ + struct gsm_support *sup = &ms->support; \ + if (!sup->item) { \ + vty_out(vty, desc " not supported%s", VTY_NEWLINE); \ + if (vty_reading) \ + return CMD_SUCCESS; \ + return CMD_WARNING; \ + } \ + if (restart) \ + vty_restart(vty, ms); \ + set->item = 1; \ + return CMD_SUCCESS; \ +} + +#define SUP_DI(cfg, cfg_cmd, item, cmd, desc, restart) \ +DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \ +{ \ + struct osmocom_ms *ms = vty->index; \ + struct gsm_settings *set = &ms->settings; \ + struct gsm_support *sup = &ms->support; \ + if (!sup->item) { \ + vty_out(vty, desc " not supported%s", VTY_NEWLINE); \ + if (vty_reading) \ + return CMD_SUCCESS; \ + return CMD_WARNING; \ + } \ + if (restart) \ + vty_restart(vty, ms); \ + set->item = 0; \ + return CMD_SUCCESS; \ +} + +#define SET_EN(cfg, cfg_cmd, item, cmd, desc, restart) \ +DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \ +{ \ + struct osmocom_ms *ms = vty->index; \ + struct gsm_settings *set = &ms->settings; \ + if (restart) \ + vty_restart(vty, ms); \ + set->item = 1; \ + return CMD_SUCCESS; \ +} + +#define SET_DI(cfg, cfg_cmd, item, cmd, desc, restart) \ +DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \ +{ \ + struct osmocom_ms *ms = vty->index; \ + struct gsm_settings *set = &ms->settings; \ + if (restart) \ + vty_restart(vty, ms); \ + set->item = 0; \ + return CMD_SUCCESS; \ +} + +SET_EN(cfg_ms_sup_dtmf, cfg_ms_sup_dtmf_cmd, cc_dtmf, "dtmf", "DTMF", 0); +SET_DI(cfg_ms_sup_no_dtmf, cfg_ms_sup_no_dtmf_cmd, cc_dtmf, "dtmf", "DTMF", 0); +SUP_EN(cfg_ms_sup_sms, cfg_ms_sup_sms_cmd, sms_ptp, "sms", "SMS", 0); +SUP_DI(cfg_ms_sup_no_sms, cfg_ms_sup_no_sms_cmd, sms_ptp, "sms", "SMS", 0); +SUP_EN(cfg_ms_sup_a5_1, cfg_ms_sup_a5_1_cmd, a5_1, "a5/1", "A5/1", 0); +SUP_DI(cfg_ms_sup_no_a5_1, cfg_ms_sup_no_a5_1_cmd, a5_1, "a5/1", "A5/1", 0); +SUP_EN(cfg_ms_sup_a5_2, cfg_ms_sup_a5_2_cmd, a5_2, "a5/2", "A5/2", 0); +SUP_DI(cfg_ms_sup_no_a5_2, cfg_ms_sup_no_a5_2_cmd, a5_2, "a5/2", "A5/2", 0); +SUP_EN(cfg_ms_sup_a5_3, cfg_ms_sup_a5_3_cmd, a5_3, "a5/3", "A5/3", 0); +SUP_DI(cfg_ms_sup_no_a5_3, cfg_ms_sup_no_a5_3_cmd, a5_3, "a5/3", "A5/3", 0); +SUP_EN(cfg_ms_sup_a5_4, cfg_ms_sup_a5_4_cmd, a5_4, "a5/4", "A5/4", 0); +SUP_DI(cfg_ms_sup_no_a5_4, cfg_ms_sup_no_a5_4_cmd, a5_4, "a5/4", "A5/4", 0); +SUP_EN(cfg_ms_sup_a5_5, cfg_ms_sup_a5_5_cmd, a5_5, "a5/5", "A5/5", 0); +SUP_DI(cfg_ms_sup_no_a5_5, cfg_ms_sup_no_a5_5_cmd, a5_5, "a5/5", "A5/5", 0); +SUP_EN(cfg_ms_sup_a5_6, cfg_ms_sup_a5_6_cmd, a5_6, "a5/6", "A5/6", 0); +SUP_DI(cfg_ms_sup_no_a5_6, cfg_ms_sup_no_a5_6_cmd, a5_6, "a5/6", "A5/6", 0); +SUP_EN(cfg_ms_sup_a5_7, cfg_ms_sup_a5_7_cmd, a5_7, "a5/7", "A5/7", 0); +SUP_DI(cfg_ms_sup_no_a5_7, cfg_ms_sup_no_a5_7_cmd, a5_7, "a5/7", "A5/7", 1); +SUP_EN(cfg_ms_sup_p_gsm, cfg_ms_sup_p_gsm_cmd, p_gsm, "p-gsm", "P-GSM (900)", + 1); +SUP_DI(cfg_ms_sup_no_p_gsm, cfg_ms_sup_no_p_gsm_cmd, p_gsm, "p-gsm", + "P-GSM (900)", 1); +SUP_EN(cfg_ms_sup_e_gsm, cfg_ms_sup_e_gsm_cmd, e_gsm, "e-gsm", "E-GSM (850)", + 1); +SUP_DI(cfg_ms_sup_no_e_gsm, cfg_ms_sup_no_e_gsm_cmd, e_gsm, "e-gsm", + "E-GSM (850)", 1); +SUP_EN(cfg_ms_sup_r_gsm, cfg_ms_sup_r_gsm_cmd, r_gsm, "r-gsm", "R-GSM (850)", + 1); +SUP_DI(cfg_ms_sup_no_r_gsm, cfg_ms_sup_no_r_gsm_cmd, r_gsm, "r-gsm", + "R-GSM (850)", 1); +SUP_EN(cfg_ms_sup_dcs, cfg_ms_sup_dcs_cmd, dcs, "dcs", "DCS (1800)", 0); +SUP_DI(cfg_ms_sup_no_dcs, cfg_ms_sup_no_dcs_cmd, dcs, "dcs", "DCS (1800)", 0); + +DEFUN(cfg_ms_sup_class_900, cfg_ms_sup_class_900_cmd, "class-900 (1|2|3|4|5)", + "Select power class for GSM 850/900\n" + "20 Watts\n" + "8 Watts\n" + "5 Watts\n" + "2 Watts\n" + "0.8 Watts") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + struct gsm_support *sup = &ms->support; + + set->class_900 = atoi(argv[0]); + + if (set->class_900 < sup->class_900 && !vty_reading) + vty_out(vty, "You selected an higher class than supported " + " by hardware!%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_sup_class_dcs, cfg_ms_sup_class_dcs_cmd, "class-dcs (1|2|3)", + "Select power class for DCS 1800\n" + "1 Watt\n" + "0.25 Watts\n" + "4 Watts") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + struct gsm_support *sup = &ms->support; + + set->class_dcs = atoi(argv[0]); + + if (((set->class_dcs + 1) & 3) < ((sup->class_dcs + 1) & 3) + && !vty_reading) + vty_out(vty, "You selected an higher class than supported " + " by hardware!%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_sup_ch_cap, cfg_ms_sup_ch_cap_cmd, "channel-capability " + "(sdcch|sdcch+tchf|sdcch+tchf+tchh)", + "Select channel capability\nSDCCH only\nSDCCH + TCH/F\nSDCCH + TCH/H") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + struct gsm_support *sup = &ms->support; + uint8_t ch_cap; + + if (!strcmp(argv[0], "sdcch+tchf+tchh")) + ch_cap = GSM_CAP_SDCCH_TCHF_TCHH; + else if (!strcmp(argv[0], "sdcch+tchf")) + ch_cap = GSM_CAP_SDCCH_TCHF; + else + ch_cap = GSM_CAP_SDCCH; + + if (ch_cap > sup->ch_cap && !vty_reading) { + vty_out(vty, "You selected an higher capability than supported " + " by hardware!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (ch_cap != set->ch_cap + && (ch_cap == GSM_CAP_SDCCH || set->ch_cap == GSM_CAP_SDCCH)) + vty_restart(vty, ms); + + set->ch_cap = ch_cap; + + return CMD_SUCCESS; +} + +SUP_EN(cfg_ms_sup_full_v1, cfg_ms_sup_full_v1_cmd, full_v1, "full-speech-v1", + "Full rate speech V1", 0); +SUP_DI(cfg_ms_sup_no_full_v1, cfg_ms_sup_no_full_v1_cmd, full_v1, + "full-speech-v1", "Full rate speech V1", 0); +SUP_EN(cfg_ms_sup_full_v2, cfg_ms_sup_full_v2_cmd, full_v2, "full-speech-v2", + "Full rate speech V2 (EFR)", 0); +SUP_DI(cfg_ms_sup_no_full_v2, cfg_ms_sup_no_full_v2_cmd, full_v2, + "full-speech-v2", "Full rate speech V2 (EFR)", 0); +SUP_EN(cfg_ms_sup_full_v3, cfg_ms_sup_full_v3_cmd, full_v3, "full-speech-v3", + "Full rate speech V3 (AMR)", 0); +SUP_DI(cfg_ms_sup_no_full_v3, cfg_ms_sup_no_full_v3_cmd, full_v3, + "full-speech-v3", "Full rate speech V3 (AMR)", 0); +SUP_EN(cfg_ms_sup_half_v1, cfg_ms_sup_half_v1_cmd, half_v1, "half-speech-v1", + "Half rate speech V1", 0); +SUP_DI(cfg_ms_sup_no_half_v1, cfg_ms_sup_no_half_v1_cmd, half_v1, + "half-speech-v1", "Half rate speech V1", 0); +SUP_EN(cfg_ms_sup_half_v3, cfg_ms_sup_half_v3_cmd, half_v3, "half-speech-v3", + "Half rate speech V3 (AMR)", 0); +SUP_DI(cfg_ms_sup_no_half_v3, cfg_ms_sup_no_half_v3_cmd, half_v3, + "half-speech-v3", "Half rate speech V3 (AMR)", 0); + +DEFUN(cfg_ms_sup_min_rxlev, cfg_ms_sup_min_rxlev_cmd, "min-rxlev <-110--47>", + "Set the minimum receive level to select a cell\n" + "Minimum receive level from -110 dBm to -47 dBm") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->min_rxlev_db = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_sup_dsc_max, cfg_ms_sup_dsc_max_cmd, "dsc-max <90-500>", + "Set the maximum DSC value. Standard is 90. Increase to make mobile " + "more reliable against bad RX signal. This increase the propability " + "of missing a paging requests\n" + "DSC initial and maximum value (standard is 90)") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->dsc_max = atoi(argv[0]); + + return CMD_SUCCESS; +} + +/* per testsim config */ +DEFUN(cfg_ms_testsim, cfg_ms_testsim_cmd, "test-sim", + "Configure test SIM emulation") +{ + vty->node = TESTSIM_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_test_imsi, cfg_test_imsi_cmd, "imsi IMSI", + "Set IMSI on test card\n15 digits IMSI") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + char *error = gsm_check_imsi(argv[0]); + + if (error) { + vty_out(vty, "%s%s", error, VTY_NEWLINE); + return CMD_WARNING; + } + + strcpy(set->test_imsi, argv[0]); + + vty_restart(vty, ms); + return CMD_SUCCESS; +} + +#define HEX_STR "\nByte as two digits hexadecimal" +DEFUN(cfg_test_ki_xor, cfg_test_ki_xor_cmd, "ki xor HEX HEX HEX HEX HEX HEX " + "HEX HEX HEX HEX HEX HEX", + "Set Key (Kc) on test card\nUse XOR algorithm" HEX_STR HEX_STR HEX_STR + HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR) +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + uint8_t ki[12]; + const char *p; + int i; + + for (i = 0; i < 12; i++) { + p = argv[i]; + if (!strncmp(p, "0x", 2)) + p += 2; + if (strlen(p) != 2) { + vty_out(vty, "Expecting two digits hex value (with or " + "without 0x in front)%s", VTY_NEWLINE); + return CMD_WARNING; + } + ki[i] = strtoul(p, NULL, 16); + } + + set->test_ki_type = GSM_SIM_KEY_XOR; + memcpy(set->test_ki, ki, 12); + return CMD_SUCCESS; +} + +DEFUN(cfg_test_ki_comp128, cfg_test_ki_comp128_cmd, "ki comp128 HEX HEX HEX " + "HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX", + "Set Key (Kc) on test card\nUse XOR algorithm" HEX_STR HEX_STR HEX_STR + HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR + HEX_STR HEX_STR HEX_STR HEX_STR) +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + uint8_t ki[16]; + const char *p; + int i; + + for (i = 0; i < 16; i++) { + p = argv[i]; + if (!strncmp(p, "0x", 2)) + p += 2; + if (strlen(p) != 2) { + vty_out(vty, "Expecting two digits hex value (with or " + "without 0x in front)%s", VTY_NEWLINE); + return CMD_WARNING; + } + ki[i] = strtoul(p, NULL, 16); + } + + set->test_ki_type = GSM_SIM_KEY_COMP128; + memcpy(set->test_ki, ki, 16); + return CMD_SUCCESS; +} + +DEFUN(cfg_test_barr, cfg_test_barr_cmd, "barred-access", + "Allow access to barred cells") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->test_barr = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_test_no_barr, cfg_test_no_barr_cmd, "no barred-access", + NO_STR "Deny access to barred cells") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->test_barr = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_test_no_rplmn, cfg_test_no_rplmn_cmd, "no rplmn", + NO_STR "Unset Registered PLMN") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->test_rplmn_valid = 0; + + vty_restart(vty, ms); + return CMD_SUCCESS; +} + +DEFUN(cfg_test_rplmn, cfg_test_rplmn_cmd, "rplmn MCC MNC [LAC] [TMSI]", + "Set Registered PLMN\nMobile Country Code\nMobile Network Code\n" + "Optionally set locatio area code\n" + "Optionally set current assigned TMSI") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + uint16_t mcc = gsm_input_mcc((char *)argv[0]), + mnc = gsm_input_mnc((char *)argv[1]); + + if (!mcc) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!mnc) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + set->test_rplmn_valid = 1; + set->test_rplmn_mcc = mcc; + set->test_rplmn_mnc = mnc; + + if (argc >= 3) + set->test_lac = strtoul(argv[2], NULL, 16); + else + set->test_lac = 0xfffe; + + if (argc >= 4) + set->test_tmsi = strtoul(argv[3], NULL, 16); + else + set->test_tmsi = 0xffffffff; + + vty_restart(vty, ms); + return CMD_SUCCESS; +} + +DEFUN(cfg_test_hplmn, cfg_test_hplmn_cmd, "hplmn-search (everywhere|foreign-country)", + "Set Home PLMN search mode\n" + "Search for HPLMN when on any other network\n" + "Search for HPLMN when in a different country") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + switch (argv[0][0]) { + case 'e': + set->test_always = 1; + break; + case 'f': + set->test_always = 0; + break; + } + + vty_restart(vty, ms); + return CMD_SUCCESS; +} + +DEFUN(cfg_no_shutdown, cfg_ms_no_shutdown_cmd, "no shutdown", + NO_STR "Activate and run MS") +{ + struct osmocom_ms *ms = vty->index, *tmp; + int rc; + + if (ms->shutdown != 2) + return CMD_SUCCESS; + + llist_for_each_entry(tmp, &ms_list, entity) { + if (tmp->shutdown == 2) + continue; + if (!strcmp(ms->settings.layer2_socket_path, + tmp->settings.layer2_socket_path)) { + vty_out(vty, "Cannot start MS '%s', because MS '%s' " + "use the same layer2-socket.%sPlease shutdown " + "MS '%s' first.%s", ms->name, tmp->name, + VTY_NEWLINE, tmp->name, VTY_NEWLINE); + return CMD_WARNING; + } + if (!strcmp(ms->settings.sap_socket_path, + tmp->settings.sap_socket_path)) { + vty_out(vty, "Cannot start MS '%s', because MS '%s' " + "use the same sap-socket.%sPlease shutdown " + "MS '%s' first.%s", ms->name, tmp->name, + VTY_NEWLINE, tmp->name, VTY_NEWLINE); + return CMD_WARNING; + } + } + + rc = mobile_init(ms); + if (rc < 0) { + vty_out(vty, "Connection to layer 1 failed!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_shutdown, cfg_ms_shutdown_cmd, "shutdown", + "Shut down and deactivate MS") +{ + struct osmocom_ms *ms = vty->index; + + if (ms->shutdown == 0) + mobile_exit(ms, 0); + + return CMD_SUCCESS; +} + +DEFUN(cfg_shutdown_force, cfg_ms_shutdown_force_cmd, "shutdown force", + "Shut down and deactivate MS\nDo not perform IMSI detach") +{ + struct osmocom_ms *ms = vty->index; + + if (ms->shutdown <= 1) + mobile_exit(ms, 1); + + return CMD_SUCCESS; +} + +enum node_type ms_vty_go_parent(struct vty *vty) +{ + switch (vty->node) { + case MS_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case TESTSIM_NODE: + case SUPPORT_NODE: + vty->node = MS_NODE; + break; + default: + vty->node = CONFIG_NODE; + } + + return vty->node; +} + +/* Down vty node level. */ +gDEFUN(ournode_exit, + ournode_exit_cmd, "exit", "Exit current mode and down to previous mode\n") +{ + switch (vty->node) { + case MS_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case TESTSIM_NODE: + case SUPPORT_NODE: + vty->node = MS_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* End of configuration. */ +gDEFUN(ournode_end, + ournode_end_cmd, "end", "End current mode and change to enable mode.") +{ + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case VTY_NODE: + case MS_NODE: + case TESTSIM_NODE: + case SUPPORT_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + vty->index = NULL; + vty->index_sub = NULL; + break; + default: + break; + } + return CMD_SUCCESS; +} + +DEFUN(off, off_cmd, "off", + "Turn mobiles off (shutdown) and exit") +{ + dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL); + + return CMD_SUCCESS; +} + +#define SUP_NODE(item) \ + install_element(SUPPORT_NODE, &cfg_ms_sup_item_cmd); + +int ms_vty_init(void) +{ + install_element_ve(&show_ms_cmd); + install_element_ve(&show_subscr_cmd); + install_element_ve(&show_support_cmd); + install_element_ve(&show_cell_cmd); + install_element_ve(&show_cell_si_cmd); + install_element_ve(&show_ba_cmd); + install_element_ve(&show_forb_la_cmd); + install_element_ve(&show_forb_plmn_cmd); + install_element_ve(&monitor_network_cmd); + install_element_ve(&no_monitor_network_cmd); + install_element(ENABLE_NODE, &off_cmd); + + install_element(ENABLE_NODE, &sim_test_cmd); + install_element(ENABLE_NODE, &sim_reader_cmd); + install_element(ENABLE_NODE, &sim_remove_cmd); + install_element(ENABLE_NODE, &sim_pin_cmd); + install_element(ENABLE_NODE, &sim_disable_pin_cmd); + install_element(ENABLE_NODE, &sim_enable_pin_cmd); + install_element(ENABLE_NODE, &sim_change_pin_cmd); + install_element(ENABLE_NODE, &sim_unblock_pin_cmd); + install_element(ENABLE_NODE, &sim_lai_cmd); + install_element(ENABLE_NODE, &network_search_cmd); + install_element(ENABLE_NODE, &network_show_cmd); + install_element(ENABLE_NODE, &network_select_cmd); + install_element(ENABLE_NODE, &call_cmd); + install_element(ENABLE_NODE, &call_retr_cmd); + install_element(ENABLE_NODE, &call_dtmf_cmd); + + install_element(CONFIG_NODE, &cfg_gps_device_cmd); + install_element(CONFIG_NODE, &cfg_gps_baud_cmd); + install_element(CONFIG_NODE, &cfg_gps_enable_cmd); + install_element(CONFIG_NODE, &cfg_no_gps_enable_cmd); + + install_element(CONFIG_NODE, &cfg_ms_cmd); + install_element(CONFIG_NODE, &cfg_ms_create_cmd); + install_element(CONFIG_NODE, &cfg_ms_rename_cmd); + install_element(CONFIG_NODE, &cfg_no_ms_cmd); + install_element(CONFIG_NODE, &ournode_end_cmd); + install_node(&ms_node, config_write); + install_default(MS_NODE); + install_element(MS_NODE, &ournode_exit_cmd); + install_element(MS_NODE, &ournode_end_cmd); + install_element(MS_NODE, &cfg_ms_show_this_cmd); + install_element(MS_NODE, &cfg_ms_layer2_cmd); + install_element(MS_NODE, &cfg_ms_sap_cmd); + install_element(MS_NODE, &cfg_ms_sim_cmd); + install_element(MS_NODE, &cfg_ms_mode_cmd); + install_element(MS_NODE, &cfg_ms_imei_cmd); + install_element(MS_NODE, &cfg_ms_imei_fixed_cmd); + install_element(MS_NODE, &cfg_ms_imei_random_cmd); + install_element(MS_NODE, &cfg_ms_no_emerg_imsi_cmd); + install_element(MS_NODE, &cfg_ms_emerg_imsi_cmd); + install_element(MS_NODE, &cfg_ms_cw_cmd); + install_element(MS_NODE, &cfg_ms_no_cw_cmd); + install_element(MS_NODE, &cfg_ms_auto_answer_cmd); + install_element(MS_NODE, &cfg_ms_no_auto_answer_cmd); + install_element(MS_NODE, &cfg_ms_clip_cmd); + install_element(MS_NODE, &cfg_ms_clir_cmd); + install_element(MS_NODE, &cfg_ms_no_clip_cmd); + install_element(MS_NODE, &cfg_ms_no_clir_cmd); + install_element(MS_NODE, &cfg_ms_tx_power_cmd); + install_element(MS_NODE, &cfg_ms_tx_power_val_cmd); + install_element(MS_NODE, &cfg_ms_sim_delay_cmd); + install_element(MS_NODE, &cfg_ms_no_sim_delay_cmd); + install_element(MS_NODE, &cfg_ms_stick_cmd); + install_element(MS_NODE, &cfg_ms_no_stick_cmd); + install_element(MS_NODE, &cfg_ms_lupd_cmd); + install_element(MS_NODE, &cfg_ms_no_lupd_cmd); + install_element(MS_NODE, &cfg_ms_codec_full_cmd); + install_element(MS_NODE, &cfg_ms_codec_full_pref_cmd); + install_element(MS_NODE, &cfg_ms_codec_half_cmd); + install_element(MS_NODE, &cfg_ms_codec_half_pref_cmd); + install_element(MS_NODE, &cfg_ms_no_codec_half_cmd); + install_element(MS_NODE, &cfg_ms_abbrev_cmd); + install_element(MS_NODE, &cfg_ms_no_abbrev_cmd); + install_element(MS_NODE, &cfg_ms_testsim_cmd); + install_element(MS_NODE, &cfg_ms_support_cmd); + install_node(&support_node, config_write_dummy); + install_default(SUPPORT_NODE); + install_element(SUPPORT_NODE, &ournode_exit_cmd); + install_element(SUPPORT_NODE, &ournode_end_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_dtmf_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_dtmf_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_sms_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_sms_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_a5_1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_a5_2_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_2_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_a5_3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_a5_4_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_4_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_a5_5_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_5_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_a5_6_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_6_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_a5_7_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_7_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_p_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_p_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_e_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_e_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_r_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_r_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_dcs_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_dcs_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_class_900_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_class_dcs_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_ch_cap_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_full_v1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_full_v2_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v2_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_full_v3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_half_v1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_half_v1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_half_v3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_no_half_v3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_min_rxlev_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_dsc_max_cmd); + install_node(&testsim_node, config_write_dummy); + install_default(TESTSIM_NODE); + install_element(TESTSIM_NODE, &ournode_exit_cmd); + install_element(TESTSIM_NODE, &ournode_end_cmd); + install_element(TESTSIM_NODE, &cfg_test_imsi_cmd); + install_element(TESTSIM_NODE, &cfg_test_ki_xor_cmd); + install_element(TESTSIM_NODE, &cfg_test_ki_comp128_cmd); + install_element(TESTSIM_NODE, &cfg_test_barr_cmd); + install_element(TESTSIM_NODE, &cfg_test_no_barr_cmd); + install_element(TESTSIM_NODE, &cfg_test_no_rplmn_cmd); + install_element(TESTSIM_NODE, &cfg_test_rplmn_cmd); + install_element(TESTSIM_NODE, &cfg_test_hplmn_cmd); + install_element(MS_NODE, &cfg_ms_shutdown_cmd); + install_element(MS_NODE, &cfg_ms_shutdown_force_cmd); + install_element(MS_NODE, &cfg_ms_no_shutdown_cmd); + + return 0; +} + +void vty_notify(struct osmocom_ms *ms, const char *fmt, ...) +{ + struct telnet_connection *connection; + char buffer[1000]; + va_list args; + struct vty *vty; + + if (fmt) { + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + if (!buffer[0]) + return; + } + + llist_for_each_entry(connection, &active_connections, entry) { + vty = connection->vty; + if (!vty) + continue; + if (!fmt) { + vty_out(vty, "%s%% (MS %s)%s", VTY_NEWLINE, ms->name, + VTY_NEWLINE); + continue; + } + if (buffer[strlen(buffer) - 1] == '\n') { + buffer[strlen(buffer) - 1] = '\0'; + vty_out(vty, "%% %s%s", buffer, VTY_NEWLINE); + buffer[strlen(buffer)] = '\n'; + } else + vty_out(vty, "%% %s", buffer); + } +} + diff --git a/src/host/osmocon/.gitignore b/src/host/osmocon/.gitignore new file mode 100644 index 00000000..ad061b7f --- /dev/null +++ b/src/host/osmocon/.gitignore @@ -0,0 +1,36 @@ +# autoreconf by-products +*.in + +aclocal.m4 +autom4te.cache/ +configure +depcomp +install-sh +missing + +# configure by-products +.deps/ +Makefile + +config.status +version.h + +# build by-products +*.o + +osmocon +osmoload + +# various +.version +.tarball-version + +# IDA file +*.id* +*.nam +*.til + +# Other test files +*.dump +*.bin +*.log diff --git a/src/host/osmocon/COPYING b/src/host/osmocon/COPYING new file mode 100644 index 00000000..d511905c --- /dev/null +++ b/src/host/osmocon/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/src/host/osmocon/Makefile.am b/src/host/osmocon/Makefile.am new file mode 100644 index 00000000..8b0d4bf3 --- /dev/null +++ b/src/host/osmocon/Makefile.am @@ -0,0 +1,21 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 + +# versioning magic +BUILT_SOURCES = $(top_srcdir)/.version +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) + +sbin_PROGRAMS = osmocon osmoload + +# FIXME: sercomm needs to move into libosmocore or another shared lib +INCLUDES += -I../../target/firmware/include/comm -I../../target/firmware/apps -DHOST_BUILD +osmocon_SOURCES = osmocon.c tpu_debug.c ../../target/firmware/comm/sercomm.c +osmocon_LDADD = $(LIBOSMOCORE_LIBS) + +osmoload_SOURCE = osmoload.c ../../target/firmware/comm/sercomm.c +osmoload_LDADD = $(LIBOSMOCORE_LIBS) diff --git a/src/host/osmocon/configure.ac b/src/host/osmocon/configure.ac new file mode 100644 index 00000000..41308003 --- /dev/null +++ b/src/host/osmocon/configure.ac @@ -0,0 +1,25 @@ +dnl Process this file with autoconf to produce a configure script +AC_INIT([osmocon], + m4_esyscmd([./git-version-gen .tarball-version]), + [baseband-devel@lists.osmocom.org]) + +AM_INIT_AUTOMAKE([dist-bzip2]) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL + +dnl checks for libraries +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) + +dnl checks for header files +AC_HEADER_STDC + +dnl Checks for typedefs, structures and compiler characteristics + +AC_OUTPUT( + Makefile) diff --git a/src/host/osmocon/git-version-gen b/src/host/osmocon/git-version-gen new file mode 100755 index 00000000..652fac68 --- /dev/null +++ b/src/host/osmocon/git-version-gen @@ -0,0 +1,151 @@ +#!/bin/sh +# Print a version string. +scriptversion=2010-01-28.01 + +# Copyright (C) 2007-2010 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# It is probably wise to add these two files to .gitignore, so that you +# don't accidentally commit either generated file. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .tarball-version will +# exist in distribution tarballs. +# +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + +case $# in + 1) ;; + *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; +esac + +tarball_version_file=$1 +nl=' +' + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || exit 1 + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test -z "$v" \ + && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 +fi + +if test -n "$v" +then + : # use $v +elif + v=`git describe --abbrev=4 --match='osmocon_v*' HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && case $v in + osmocon_[0-9]*) ;; + osmocon_v[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + case $v in + *-*-*) : git describe is okay three part flavor ;; + *-*) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + numcommits=`git rev-list "$vtag"..HEAD | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + ;; + esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/;s/^osmocon_//'`; +else + v="UNKNOWN" +fi + +v=`echo "$v" |sed 's/^v//'` + +# Don't declare a version "dirty" merely because a time stamp has changed. +git status > /dev/null 2>&1 + +dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= +case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; +esac + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d '\012' + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/src/host/osmocon/memdump_convert.pl b/src/host/osmocon/memdump_convert.pl new file mode 100755 index 00000000..3d18a0b3 --- /dev/null +++ b/src/host/osmocon/memdump_convert.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +my $num_line = 0; +my $num_hex = 0; +my $oldaddr; + +while (my $line = <STDIN>) { + chomp($line); + $num_line++; + my (@hex) = $line =~ /(\w{8}): (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8})/; + my $addr = hex(shift @hex); + if ($addr != 0 && $addr != $oldaddr + 0x20) { + printf(STDERR "gap of %u between 0x%08x and 0x%08x\n%s\n", + $addr - $oldaddr, $addr, $oldaddr, $line); + } + foreach my $h (@hex) { + $num_hex++; + # poor mans endian conversion + my ($a, $b, $c, $d) = $h =~/(\w\w)(\w\w)(\w\w)(\w\w)/; + my $h_reorder = $d . $c . $b . $a; + # convert into actual binary number + my $tmp = pack('H8', $h_reorder); + syswrite(STDOUT, $tmp, 4); + } + $oldaddr = $addr; +} + +printf(STDERR "num lines/num hex: %u/%u\n", $num_line, $num_hex); + diff --git a/src/host/osmocon/osmocon.c b/src/host/osmocon/osmocon.c new file mode 100644 index 00000000..6f6f5669 --- /dev/null +++ b/src/host/osmocon/osmocon.c @@ -0,0 +1,1560 @@ +/* osmocon */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by Steve Markgraf <steve@steve-m.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdint.h> +#include <fcntl.h> +#include <errno.h> +#include <termios.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> + +#include <sercomm.h> + +#include <osmocore/linuxlist.h> +#include <osmocore/select.h> +#include <osmocore/talloc.h> +#include <osmocore/timer.h> + +#include <arpa/inet.h> + +#define MODEM_BAUDRATE B115200 +#define MAX_DNLOAD_SIZE 0xFFFF +#define MAX_HDR_SIZE 128 +#define MAGIC_OFFSET 0x3be2 + +#define DEFAULT_BEACON_INTERVAL 50000 +#define ROMLOAD_INIT_BAUDRATE B19200 +#define ROMLOAD_DL_BAUDRATE B115200 +#define ROMLOAD_BLOCK_HDR_LEN 10 +#define ROMLOAD_ADDRESS 0x820000 + +#define MTK_INIT_BAUDRATE B19200 +#define MTK_ADDRESS 0x40001400 +#define MTK_BLOCK_SIZE 1024 + +struct tool_server *tool_server_for_dlci[256]; + +/** + * a connection from some other tool + */ +struct tool_connection { + struct tool_server *server; + struct llist_head entry; + struct bsc_fd fd; +}; + +/** + * server for a tool + */ +struct tool_server { + struct bsc_fd bfd; + uint8_t dlci; + struct llist_head connections; +}; + + +enum dnload_state { + WAITING_PROMPT1, + WAITING_PROMPT2, + DOWNLOADING, +}; + +enum romload_state { + WAITING_IDENTIFICATION, + WAITING_PARAM_ACK, + SENDING_BLOCKS, + SENDING_LAST_BLOCK, + LAST_BLOCK_SENT, + WAITING_BLOCK_ACK, + WAITING_CHECKSUM_ACK, + WAITING_BRANCH_ACK, + FINISHED, +}; + +enum mtk_state { + MTK_INIT_1, + MTK_INIT_2, + MTK_INIT_3, + MTK_INIT_4, + MTK_WAIT_WRITE_ACK, + MTK_WAIT_ADDR_ACK, + MTK_WAIT_SIZE_ACK, + MTK_SENDING_BLOCKS, + MTK_WAIT_BRANCH_CMD_ACK, + MTK_WAIT_BRANCH_ADDR_ACK, + MTK_FINISHED, +}; + +enum dnload_mode { + MODE_C123, + MODE_C123xor, + MODE_C140, + MODE_C140xor, + MODE_C155, + MODE_ROMLOAD, + MODE_MTK, +}; + +struct dnload { + enum dnload_state state; + enum romload_state romload_state; + enum mtk_state mtk_state; + enum dnload_mode mode; + struct bsc_fd serial_fd; + char *filename; + char *chainload_filename; + + int expect_hdlc; + + int dump_rx; + int dump_tx; + int beacon_interval; + + /* data to be downloaded */ + uint8_t *data; + int data_len; + + uint8_t *write_ptr; + + /* romload: block to be downloaded */ + uint8_t *block; + int block_len; + uint8_t block_number; + uint16_t block_payload_size; + int romload_dl_checksum; + uint8_t *block_ptr; + uint8_t load_address[4]; + + uint8_t mtk_send_size[4]; + int block_count; + int echo_bytecount; + + struct tool_server layer2_server; + struct tool_server loader_server; +}; + + +static struct dnload dnload; +static struct timer_list tick_timer; + +/* Compal ramloader specific */ +static const uint8_t phone_prompt1[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x01, 0x40 }; +static const uint8_t dnload_cmd[] = { 0x1b, 0xf6, 0x02, 0x00, 0x52, 0x01, 0x53 }; +static const uint8_t phone_prompt2[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x02, 0x43 }; +static const uint8_t phone_ack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x42 }; +static const uint8_t phone_nack_magic[]= { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x57 }; +static const uint8_t phone_nack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x45, 0x53, 0x16 }; +static const uint8_t ftmtool[] = { 0x66, 0x74, 0x6d, 0x74, 0x6f, 0x6f, 0x6c }; +static const uint8_t phone_magic[] = { 0x31, 0x30, 0x30, 0x33 }; /* "1003" */ + +/* The C123 has a hard-coded check inside the ramloader that requires the + * following bytes to be always the first four bytes of the image */ +static const uint8_t data_hdr_c123[] = { 0xee, 0x4c, 0x9f, 0x63 }; + +/* The C155 doesn't have some strange restriction on what the first four bytes + * have to be, but it starts the ramloader in THUMB mode. We use the following + * four bytes to switch back to ARM mode: + 800100: 4778 bx pc + 800102: 46c0 nop ; (mov r8, r8) + */ +static const uint8_t data_hdr_c155[] = { 0x78, 0x47, 0xc0, 0x46 }; + +/* Calypso romloader specific */ +static const uint8_t romload_ident_cmd[] = { 0x3c, 0x69 }; /* <i */ +static const uint8_t romload_abort_cmd[] = { 0x3c, 0x61 }; /* <a */ +static const uint8_t romload_write_cmd[] = { 0x3c, 0x77 }; /* <w */ +static const uint8_t romload_checksum_cmd[] = { 0x3c, 0x63 }; /* <c */ +static const uint8_t romload_branch_cmd[] = { 0x3c, 0x62 }; /* <b */ +static const uint8_t romload_ident_ack[] = { 0x3e, 0x69 }; /* >i */ +static const uint8_t romload_param_ack[] = { 0x3e, 0x70 }; /* >p */ +static const uint8_t romload_param_nack[] = { 0x3e, 0x50 }; /* >P */ +static const uint8_t romload_block_ack[] = { 0x3e, 0x77 }; /* >w */ +static const uint8_t romload_block_nack[] = { 0x3e, 0x57 }; /* >W */ +static const uint8_t romload_checksum_ack[] = { 0x3e, 0x63 }; /* >c */ +static const uint8_t romload_checksum_nack[] = { 0x3e, 0x43 }; /* >C */ +static const uint8_t romload_branch_ack[] = { 0x3e, 0x62 }; /* >b */ +static const uint8_t romload_branch_nack[] = { 0x3e, 0x42 }; /* >B */ + +/* romload_param: {"<p", uint8_t baudrate, uint8_t dpll, uint16_t memory_config, + * uint8_t strobe_af, uint32_t uart_timeout} */ + +static const uint8_t romload_param[] = { 0x3c, 0x70, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MTK romloader specific */ +static const uint8_t mtk_init_cmd[] = { 0xa0, 0x0a, 0x50, 0x05 }; +static const uint8_t mtk_init_resp[] = { 0x5f, 0xf5, 0xaf, 0xfa }; +static const uint8_t mtk_command[] = { 0xa1, 0xa2, 0xa4, 0xa8 }; + +/* FIXME: this routine is more or less what openbsc/src/rs232:rs232_setup() + * does, we should move it to libosmocore at some point */ +static int serial_init(const char *serial_port) +{ + int rc, serial_fd, v24; + struct termios tio; + + serial_fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY); + if (serial_fd < 0) { + perror("cannot open serial port"); + return serial_fd; + } + + //fcntl(serial_fd, F_SETFL, 0); + + /* Configure serial interface */ + rc = tcgetattr(serial_fd, &tio); + if (rc < 0) { + perror("tcgetattr()"); + return rc; + } + cfsetispeed(&tio, MODEM_BAUDRATE); + cfsetospeed(&tio, MODEM_BAUDRATE); + tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS); + tio.c_cflag |= (CREAD | CLOCAL | CS8); + tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + tio.c_iflag |= (INPCK | ISTRIP); + tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR); + tio.c_oflag &= ~(OPOST | ONLCR); + rc = tcsetattr(serial_fd, TCSANOW, &tio); + if (rc < 0) { + perror("tcsetattr()"); + return rc; + } + + /* set ready to read/write */ + v24 = TIOCM_DTR | TIOCM_RTS; + rc = ioctl(serial_fd, TIOCMBIS, &v24); + if (rc < 0) { + perror("ioctl(TIOCMBIS)"); + return rc; + } + + return serial_fd; +} + +static int serial_set_baudrate(speed_t baudrate) +{ + int rc; + struct termios tio; + + rc = tcgetattr(dnload.serial_fd.fd, &tio); + if (rc < 0) { + perror("tcgetattr()"); + return rc; + } + cfsetispeed(&tio, baudrate); + cfsetospeed(&tio, baudrate); + + rc = tcsetattr(dnload.serial_fd.fd, TCSANOW, &tio); + return rc; +} + +static void beacon_timer_cb(void *p) +{ + int rc; + + if (dnload.romload_state == WAITING_IDENTIFICATION) { + printf("Sending Calypso romloader beacon...\n"); + rc = write(dnload.serial_fd.fd, romload_ident_cmd, + sizeof(romload_ident_cmd)); + + if (!(rc == sizeof(romload_ident_cmd))) + printf("Error sending identification beacon\n"); + + bsc_schedule_timer(p, 0, dnload.beacon_interval); + } +} + +static void mtk_timer_cb(void *p) +{ + int rc; + + if (dnload.mtk_state == MTK_INIT_1) { + printf("Sending MTK romloader beacon...\n"); + rc = write(dnload.serial_fd.fd, &mtk_init_cmd[0], 1); + + if (!(rc == 1)) + printf("Error sending identification beacon\n"); + + bsc_schedule_timer(p, 0, dnload.beacon_interval); + } +} + +/* Read the to-be-downloaded file, prepend header and length, append XOR sum */ +int read_file(const char *filename) +{ + int fd, rc, i; + struct stat st; + const uint8_t *hdr = NULL; + int payload_size; + int hdr_len = 0; + uint8_t *file_data; + uint16_t tot_len; + uint8_t nibble; + uint8_t running_xor = 0x02; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("opening file"); + exit(1); + } + + rc = fstat(fd, &st); + if (st.st_size > MAX_DNLOAD_SIZE) { + fprintf(stderr, "The maximum file size is 64kBytes (%u bytes)\n", + MAX_DNLOAD_SIZE); + return -EFBIG; + } + + if (dnload.data) { + free(dnload.data); + dnload.data = NULL; + } + + if (dnload.mode == MODE_C140 || dnload.mode == MODE_C140xor) { + if (st.st_size < (MAGIC_OFFSET + sizeof(phone_magic))) + payload_size = MAGIC_OFFSET + sizeof(phone_magic); + else { + printf("\nThe filesize is larger than 15kb, code on " + "the magic address will be overwritten!\nUse " + "loader.bin and upload the application with " + "osmoload instead!\n\n"); + payload_size = st.st_size; + } + } else + payload_size = st.st_size; + + dnload.data = malloc(MAX_HDR_SIZE + payload_size); + + if (!dnload.data) { + close(fd); + fprintf(stderr, "No memory\n"); + return -ENOMEM; + } + + /* copy in the header, if any */ + switch (dnload.mode) { + case MODE_C155: + hdr = data_hdr_c155; + hdr_len = sizeof(data_hdr_c155); + break; + case MODE_C140: + case MODE_C140xor: + case MODE_C123: + case MODE_C123xor: + hdr = data_hdr_c123; + hdr_len = sizeof(data_hdr_c123); + break; + case MODE_ROMLOAD: + break; + default: + break; + } + + if (hdr && hdr_len) + memcpy(dnload.data, hdr, hdr_len); + + /* 2 bytes for length + header */ + file_data = dnload.data + 2 + hdr_len; + + /* write the length, keep running XOR */ + tot_len = hdr_len + payload_size; + nibble = tot_len >> 8; + dnload.data[0] = nibble; + running_xor ^= nibble; + nibble = tot_len & 0xff; + dnload.data[1] = nibble; + running_xor ^= nibble; + + if (hdr_len && hdr) { + memcpy(dnload.data+2, hdr, hdr_len); + + for (i = 0; i < hdr_len; i++) + running_xor ^= hdr[i]; + } + + rc = read(fd, file_data, st.st_size); + if (rc < 0) { + perror("error reading file\n"); + free(dnload.data); + dnload.data = NULL; + close(fd); + return -EIO; + } + if (rc < st.st_size) { + free(dnload.data); + dnload.data = NULL; + close(fd); + fprintf(stderr, "Short read of file (%d < %d)\n", + rc, (int)st.st_size); + return -EIO; + } + + close(fd); + + dnload.data_len = (file_data+payload_size) - dnload.data; + + /* fill memory between data end and magic, add magic */ + if(dnload.mode == MODE_C140 || dnload.mode == MODE_C140xor) { + if (st.st_size < MAGIC_OFFSET) + memset(file_data + st.st_size, 0x00, + payload_size - st.st_size); + memcpy(dnload.data + MAGIC_OFFSET, phone_magic, + sizeof(phone_magic)); + } + + /* calculate XOR sum */ + for (i = 0; i < payload_size; i++) + running_xor ^= file_data[i]; + + dnload.data[dnload.data_len++] = running_xor; + + /* initialize write pointer to start of data */ + dnload.write_ptr = dnload.data; + + printf("read_file(%s): file_size=%u, hdr_len=%u, dnload_len=%u\n", + filename, (int)st.st_size, hdr_len, dnload.data_len); + + return 0; +} + +static void hexdump(const uint8_t *data, unsigned int len) +{ + int n; + + for (n=0; n < len; n++) + printf("%02x ", data[n]); + printf(" "); + for (n=0; n < len; n++) + if (isprint(data[n])) + putchar(data[n]); + else + putchar('.'); + printf("\n"); +} + +static int romload_prepare_block(void) +{ + int i; + + int block_checksum = 5; + int remaining_bytes; + int fill_bytes; + uint8_t *block_data; + uint32_t block_address; + + dnload.block_len = ROMLOAD_BLOCK_HDR_LEN + dnload.block_payload_size; + + /* if first block, allocate memory */ + if (!dnload.block_number) { + dnload.block = malloc(dnload.block_len); + if (!dnload.block) { + fprintf(stderr, "No memory\n"); + return -ENOMEM; + } + dnload.romload_dl_checksum = 0; + /* initialize write pointer to start of data */ + dnload.write_ptr = dnload.data; + } + + block_address = ROMLOAD_ADDRESS + + (dnload.block_number * dnload.block_payload_size); + + /* prepare our block header (10 bytes) */ + memcpy(dnload.block, romload_write_cmd, sizeof(romload_write_cmd)); + dnload.block[2] = 0x01; /* block index */ + /* should normally be the block number, but hangs when sending !0x01 */ + dnload.block[3] = 0x01; /* dnload.block_number+1 */ + dnload.block[4] = (dnload.block_payload_size >> 8) & 0xff; + dnload.block[5] = dnload.block_payload_size & 0xff; + dnload.block[6] = (block_address >> 24) & 0xff; + dnload.block[7] = (block_address >> 16) & 0xff; + dnload.block[8] = (block_address >> 8) & 0xff; + dnload.block[9] = block_address & 0xff; + + block_data = dnload.block + ROMLOAD_BLOCK_HDR_LEN; + dnload.write_ptr = dnload.data + 2 + + (dnload.block_payload_size * dnload.block_number); + + remaining_bytes = dnload.data_len - 3 - + (dnload.block_payload_size * dnload.block_number); + + memcpy(block_data, dnload.write_ptr, dnload.block_payload_size); + + if (remaining_bytes <= dnload.block_payload_size) { + fill_bytes = (dnload.block_payload_size - remaining_bytes); + printf("Preparing the last block, filling %i bytes,", + fill_bytes); + memset(block_data + remaining_bytes, 0x00, fill_bytes); + dnload.romload_state = SENDING_LAST_BLOCK; + } else { + dnload.romload_state = SENDING_BLOCKS; + printf("Preparing block %i,", dnload.block_number+1); + } + + /* block checksum is lsb of ~(5 + block_size_lsb + all bytes of + * block_address + all data bytes) */ + for (i = 5; i < ROMLOAD_BLOCK_HDR_LEN + dnload.block_payload_size; i++) + block_checksum += dnload.block[i]; + + /* checksum is lsb of ~(sum of LSBs of all block checksums) */ + printf(" block checksum is 0x%02x \n", ~(block_checksum) & 0xff); + dnload.romload_dl_checksum += ~(block_checksum) & 0xff; + + /* initialize block pointer to start of block */ + dnload.block_ptr = dnload.block; + + dnload.block_number++; + dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE; + return 0; +} + +static int mtk_prepare_block(void) +{ + int rc, i; + + int remaining_bytes; + int fill_bytes; + uint8_t *block_data; + uint8_t tmp_byteswap; + uint32_t tmp_size; + + dnload.block_len = MTK_BLOCK_SIZE; + dnload.echo_bytecount = 0; + + /* if first block, allocate memory */ + if (!dnload.block_number) { + dnload.block = malloc(dnload.block_len); + if (!dnload.block) { + fprintf(stderr, "No memory\n"); + return -ENOMEM; + } + + /* calculate the number of blocks we need to send */ + dnload.block_count = (dnload.data_len-3) / MTK_BLOCK_SIZE; + /* add one more block if no multiple of blocksize */ + if((dnload.data_len-3) % MTK_BLOCK_SIZE) + dnload.block_count++; + + /* divide by 2, since we have to tell the mtk loader the size + * as count of uint16 (odd transfer sizes are not possible) */ + tmp_size = (dnload.block_count * MTK_BLOCK_SIZE)/2; + dnload.mtk_send_size[0] = (tmp_size >> 24) & 0xff; + dnload.mtk_send_size[1] = (tmp_size >> 16) & 0xff; + dnload.mtk_send_size[2] = (tmp_size >> 8) & 0xff; + dnload.mtk_send_size[3] = tmp_size & 0xff; + + /* initialize write pointer to start of data */ + dnload.write_ptr = dnload.data; + } + + block_data = dnload.block; + dnload.write_ptr = dnload.data + 2 + + (dnload.block_len * dnload.block_number); + + remaining_bytes = dnload.data_len - 3 - + (dnload.block_len * dnload.block_number); + + memcpy(block_data, dnload.write_ptr, MTK_BLOCK_SIZE); + + if (remaining_bytes <= MTK_BLOCK_SIZE) { + fill_bytes = (MTK_BLOCK_SIZE - remaining_bytes); + printf("Preparing the last block, filling %i bytes\n", + fill_bytes); + memset(block_data + remaining_bytes, 0x00, fill_bytes); + dnload.romload_state = SENDING_LAST_BLOCK; + } else { + dnload.romload_state = SENDING_BLOCKS; + printf("Preparing block %i\n", dnload.block_number+1); + } + + /* for the mtk romloader we need to swap MSB <-> LSB */ + for (i = 0; i < dnload.block_len; i += 2) { + tmp_byteswap = dnload.block[i]; + dnload.block[i] = dnload.block[i+1]; + dnload.block[i+1] = tmp_byteswap; + } + + /* initialize block pointer to start of block */ + dnload.block_ptr = dnload.block; + + dnload.block_number++; + return rc; +} + +static int handle_write_block(void) +{ + int bytes_left, write_len, rc; + + printf("handle_write_block(): "); + + if (dnload.block_ptr >= dnload.block + dnload.block_len) { + printf("Block %i finished\n", dnload.block_number); + dnload.write_ptr = dnload.data; + dnload.serial_fd.when &= ~BSC_FD_WRITE; + if (dnload.romload_state == SENDING_LAST_BLOCK) { + dnload.romload_state = LAST_BLOCK_SENT; + printf("Finished, sent %i blocks in total\n", + dnload.block_number); + } else { + dnload.romload_state = WAITING_BLOCK_ACK; + } + + return 0; + } + + /* try to write a maximum of block_len bytes */ + bytes_left = (dnload.block + dnload.block_len) - dnload.block_ptr; + write_len = dnload.block_len; + if (bytes_left < dnload.block_len) + write_len = bytes_left; + + rc = write(dnload.serial_fd.fd, dnload.block_ptr, write_len); + if (rc < 0) { + perror("Error during write"); + return rc; + } + + dnload.block_ptr += rc; + + printf("%u bytes (%tu/%u)\n", rc, dnload.block_ptr - dnload.block, + dnload.block_len); + + return 0; +} + +#define WRITE_BLOCK 4096 + +static int handle_write_dnload(void) +{ + int bytes_left, write_len, rc; + uint8_t xor_init = 0x02; + + printf("handle_write(): "); + if (dnload.write_ptr == dnload.data) { + /* no bytes have been transferred yet */ + switch (dnload.mode) { + case MODE_C155: + case MODE_C140xor: + case MODE_C123xor: + rc = write(dnload.serial_fd.fd, &xor_init, 1); + break; + default: + break; + } + } else if (dnload.write_ptr >= dnload.data + dnload.data_len) { + printf("finished\n"); + dnload.write_ptr = dnload.data; + dnload.serial_fd.when &= ~BSC_FD_WRITE; + return 1; + } + + /* try to write a maximum of WRITE_BLOCK bytes */ + bytes_left = (dnload.data + dnload.data_len) - dnload.write_ptr; + write_len = WRITE_BLOCK; + if (bytes_left < WRITE_BLOCK) + write_len = bytes_left; + + rc = write(dnload.serial_fd.fd, dnload.write_ptr, write_len); + if (rc < 0) { + perror("Error during write"); + return rc; + } + + dnload.write_ptr += rc; + + printf("%u bytes (%tu/%u)\n", rc, dnload.write_ptr - dnload.data, + dnload.data_len); + + return 0; +} + +static int handle_sercomm_write(void) +{ + uint8_t c; + + if (sercomm_drv_pull(&c) != 0) { + if (write(dnload.serial_fd.fd, &c, 1) != 1) + perror("short write"); + } else + dnload.serial_fd.when &= ~BSC_FD_WRITE; + + return 0; +} + +static int handle_write(void) +{ + /* TODO: simplify this again (global state: downloading, sercomm) */ + switch (dnload.mode) { + case MODE_ROMLOAD: + switch (dnload.romload_state) { + case SENDING_BLOCKS: + case SENDING_LAST_BLOCK: + return handle_write_block(); + default: + return handle_sercomm_write(); + } + break; + case MODE_MTK: + switch (dnload.mtk_state) { + case MTK_SENDING_BLOCKS: + return handle_write_block(); + default: + return handle_sercomm_write(); + } + break; + default: + switch (dnload.state) { + case DOWNLOADING: + return handle_write_dnload(); + default: + return handle_sercomm_write(); + } + } + + return 0; +} + +static uint8_t buffer[sizeof(phone_prompt1)]; +static uint8_t *bufptr = buffer; + +static void hdlc_send_to_phone(uint8_t dlci, uint8_t *data, int len) +{ + struct msgb *msg; + uint8_t *dest; + + if(dnload.dump_tx) { + printf("hdlc_send(dlci=%u): ", dlci); + hexdump(data, len); + } + + if (len > 512) { + fprintf(stderr, "Too much data to send. %u\n", len); + return; + } + + /* push the message into the stack */ + msg = sercomm_alloc_msgb(512); + if (!msg) { + fprintf(stderr, "Failed to create data for the frame.\n"); + return; + } + + /* copy the data */ + dest = msgb_put(msg, len); + memcpy(dest, data, len); + + sercomm_sendmsg(dlci, msg); + + dnload.serial_fd.when |= BSC_FD_WRITE; +} + +static void hdlc_console_cb(uint8_t dlci, struct msgb *msg) +{ + int rc; + + rc = write(1, msg->data, msg->len); + msgb_free(msg); +} + +static void hdlc_tool_cb(uint8_t dlci, struct msgb *msg) +{ + struct tool_server *srv = tool_server_for_dlci[dlci]; + + if(dnload.dump_rx) { + printf("hdlc_recv(dlci=%u): ", dlci); + hexdump(msg->data, msg->len); + } + + if(srv) { + struct tool_connection *con; + uint16_t *len; + + len = (uint16_t *) msgb_push(msg, 2); + *len = htons(msg->len - sizeof(*len)); + + llist_for_each_entry(con, &srv->connections, entry) { + if (write(con->fd.fd, msg->data, msg->len) != msg->len) { + fprintf(stderr, + "Failed to write msg to the socket..\n"); + continue; + } + } + } + + msgb_free(msg); +} + +static int handle_buffer(int buf_used_len) +{ + int nbytes, buf_left, i; + + buf_left = buf_used_len - (bufptr - buffer); + if (buf_left <= 0) { + memmove(buffer, buffer+1, buf_used_len-1); + bufptr -= 1; + buf_left = 1; + } + + nbytes = read(dnload.serial_fd.fd, bufptr, buf_left); + if (nbytes <= 0) + return nbytes; + + if (!dnload.expect_hdlc) { + printf("got %i bytes from modem, ", nbytes); + printf("data looks like: "); + hexdump(bufptr, nbytes); + } else { + for (i = 0; i < nbytes; ++i) + if (sercomm_drv_rx_char(bufptr[i]) == 0) + printf("Dropping sample '%c'\n", bufptr[i]); + } + + return nbytes; +} + +/* Compal ramloader */ +static int handle_read(void) +{ + int rc, nbytes; + + nbytes = handle_buffer(sizeof(buffer)); + if (nbytes <= 0) + return nbytes; + + if (!memcmp(buffer, phone_prompt1, sizeof(phone_prompt1))) { + printf("Received PROMPT1 from phone, responding with CMD\n"); + dnload.expect_hdlc = 0; + dnload.state = WAITING_PROMPT2; + if(dnload.filename) { + rc = write(dnload.serial_fd.fd, dnload_cmd, sizeof(dnload_cmd)); + + /* re-read file */ + rc = read_file(dnload.filename); + if (rc < 0) { + fprintf(stderr, "read_file(%s) failed with %d\n", + dnload.filename, rc); + exit(1); + } + } + } else if (!memcmp(buffer, phone_prompt2, sizeof(phone_prompt2))) { + printf("Received PROMPT2 from phone, starting download\n"); + dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE; + dnload.state = DOWNLOADING; + } else if (!memcmp(buffer, phone_ack, sizeof(phone_ack))) { + printf("Received DOWNLOAD ACK from phone, your code is" + " running now!\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + dnload.expect_hdlc = 1; + + /* check for romloader chainloading mode used as a workaround + * for the magic on the C139/C140 and J100i */ + if (dnload.chainload_filename != NULL) { + printf("Enabled Compal ramloader -> Calypso romloader" + " chainloading mode\n"); + bufptr = buffer; + dnload.filename = dnload.chainload_filename; + dnload.mode = MODE_ROMLOAD; + serial_set_baudrate(ROMLOAD_INIT_BAUDRATE); + tick_timer.cb = &beacon_timer_cb; + tick_timer.data = &tick_timer; + bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval); + } + } else if (!memcmp(buffer, phone_nack, sizeof(phone_nack))) { + printf("Received DOWNLOAD NACK from phone, something went" + " wrong :(\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + } else if (!memcmp(buffer, phone_nack_magic, sizeof(phone_nack_magic))) { + printf("Received MAGIC NACK from phone, you need to" + " have \"1003\" at 0x803ce0\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + } else if (!memcmp(buffer, ftmtool, sizeof(ftmtool))) { + printf("Received FTMTOOL from phone, ramloader has aborted\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + } + bufptr += nbytes; + + return nbytes; +} + +/* "Calypso non-secure romloader" */ +static int handle_read_romload(void) +{ + int rc, nbytes, buf_used_len; + + /* virtually limit buffer length for romloader, since responses + * are shorter and vary in length */ + + switch (dnload.romload_state) { + case WAITING_PARAM_ACK: + buf_used_len = 4; /* ">p" + uint16_t len */ + break; + case WAITING_CHECKSUM_ACK: + buf_used_len = 3; /* ">c" + uint8_t checksum */ + break; + case FINISHED: + buf_used_len = sizeof(buffer); + break; + default: + buf_used_len = 2; /* ">*" */ + } + + nbytes = handle_buffer(buf_used_len); + if (nbytes <= 0) + return nbytes; + + switch (dnload.romload_state) { + case WAITING_IDENTIFICATION: + if (memcmp(buffer, romload_ident_ack, + sizeof(romload_ident_ack))) + break; + + printf("Received ident ack from phone, sending " + "parameter sequence\n"); + dnload.expect_hdlc = 1; + dnload.romload_state = WAITING_PARAM_ACK; + rc = write(dnload.serial_fd.fd, romload_param, + sizeof(romload_param)); + /* re-read file */ + rc = read_file(dnload.filename); + if (rc < 0) { + fprintf(stderr, "read_file(%s) failed with %d\n", + dnload.filename, rc); + exit(1); + } + break; + case WAITING_PARAM_ACK: + if (memcmp(buffer, romload_param_ack, + sizeof(romload_param_ack))) + break; + + printf("Received parameter ack from phone, " + "starting download\n"); + serial_set_baudrate(ROMLOAD_DL_BAUDRATE); + + /* using the max blocksize the phone tells us */ + dnload.block_payload_size = ((buffer[3] << 8) + buffer[2]); + printf("Used blocksize for download is %i bytes\n", + dnload.block_payload_size); + dnload.block_payload_size -= ROMLOAD_BLOCK_HDR_LEN; + dnload.romload_state = SENDING_BLOCKS; + dnload.block_number = 0; + romload_prepare_block(); + bufptr -= 2; + break; + case WAITING_BLOCK_ACK: + case LAST_BLOCK_SENT: + if (!memcmp(buffer, romload_block_ack, + sizeof(romload_block_ack))) { + printf("Received block ack from phone\n"); + if (dnload.romload_state == LAST_BLOCK_SENT) { + /* send the checksum */ + uint8_t final_checksum = + (~(dnload.romload_dl_checksum) & 0xff); + printf("Sending checksum: 0x%02x \n", + final_checksum); + rc = write(dnload.serial_fd.fd, + romload_checksum_cmd, + sizeof(romload_checksum_cmd)); + rc = write(dnload.serial_fd.fd, + &final_checksum, 1); + dnload.romload_state = WAITING_CHECKSUM_ACK; + } else + romload_prepare_block(); + } else if (!memcmp(buffer, romload_block_nack, + sizeof(romload_block_nack))) { + printf("Received block nack from phone, " + "something went wrong, aborting\n"); + serial_set_baudrate(ROMLOAD_INIT_BAUDRATE); + dnload.romload_state = WAITING_IDENTIFICATION; + bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval); + } + break; + case WAITING_CHECKSUM_ACK: + if (!memcmp(buffer, romload_checksum_ack, + sizeof(romload_checksum_ack))) { + printf("Checksum on phone side matches, " + "let's branch to your code\n"); + printf("Branching to 0x%08x\n", ROMLOAD_ADDRESS); + + rc = write(dnload.serial_fd.fd, romload_branch_cmd, + sizeof(romload_branch_cmd)); + rc = write(dnload.serial_fd.fd, &dnload.load_address, + sizeof(dnload.load_address)); + dnload.romload_state = WAITING_BRANCH_ACK; + bufptr -= 1; + } else if (!memcmp(buffer, romload_checksum_nack, + sizeof(romload_checksum_nack))) { + printf("Checksum on phone side (0x%02x) doesn't " + "match ours, aborting\n", ~buffer[2]); + serial_set_baudrate(ROMLOAD_INIT_BAUDRATE); + dnload.romload_state = WAITING_IDENTIFICATION; + bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval); + bufptr -= 1; + } + break; + case WAITING_BRANCH_ACK: + if (!memcmp(buffer, romload_branch_ack, + sizeof(romload_branch_ack))) { + printf("Received branch ack, your code is running now!\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.romload_state = FINISHED; + dnload.write_ptr = dnload.data; + dnload.expect_hdlc = 1; + } else if (!memcmp(buffer, romload_branch_nack, + sizeof(romload_branch_nack))) { + printf("Received branch nack, aborting\n"); + serial_set_baudrate(ROMLOAD_INIT_BAUDRATE); + dnload.romload_state = WAITING_IDENTIFICATION; + bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval); + } + break; + default: + break; + } + + bufptr += nbytes; + return nbytes; +} + +/* MTK romloader */ +static int handle_read_mtk(void) +{ + int rc, nbytes, buf_used_len; + + switch (dnload.mtk_state) { + case MTK_WAIT_ADDR_ACK: + case MTK_WAIT_SIZE_ACK: + case MTK_WAIT_BRANCH_ADDR_ACK: + buf_used_len = 4; + break; + case MTK_FINISHED: + buf_used_len = sizeof(buffer); + break; + default: + buf_used_len = 1; + } + + nbytes = handle_buffer(buf_used_len); + if (nbytes <= 0) + return nbytes; + + switch (dnload.mtk_state) { + case MTK_INIT_1: + if (!(buffer[0] == mtk_init_resp[0])) + break; + dnload.mtk_state = MTK_INIT_2; + printf("Received init magic byte 1\n"); + rc = write(dnload.serial_fd.fd, &mtk_init_cmd[1], 1); + break; + case MTK_INIT_2: + if (!(buffer[0] == mtk_init_resp[1])) + break; + dnload.mtk_state = MTK_INIT_3; + printf("Received init magic byte 2\n"); + rc = write(dnload.serial_fd.fd, &mtk_init_cmd[2], 1); + break; + case MTK_INIT_3: + if (!(buffer[0] == mtk_init_resp[2])) + break; + dnload.mtk_state = MTK_INIT_4; + printf("Received init magic byte 3\n"); + rc = write(dnload.serial_fd.fd, &mtk_init_cmd[3], 1); + break; + case MTK_INIT_4: + if (!(buffer[0] == mtk_init_resp[3])) + break; + dnload.mtk_state = MTK_WAIT_WRITE_ACK; + printf("Received init magic byte 4, requesting write\n"); + rc = write(dnload.serial_fd.fd, &mtk_command[0], 1); + break; + case MTK_WAIT_WRITE_ACK: + if (!(buffer[0] == mtk_command[0])) + break; + dnload.mtk_state = MTK_WAIT_ADDR_ACK; + printf("Received write ack, sending load address\n"); + + rc = write(dnload.serial_fd.fd, &dnload.load_address, + sizeof(dnload.load_address)); + break; + case MTK_WAIT_ADDR_ACK: + if (memcmp(buffer, dnload.load_address, + sizeof(dnload.load_address))) + break; + printf("Received address ack from phone, sending loadsize\n"); + /* re-read file */ + rc = read_file(dnload.filename); + if (rc < 0) { + fprintf(stderr, "read_file(%s) failed with %d\n", + dnload.filename, rc); + exit(1); + } + dnload.block_number = 0; + mtk_prepare_block(); + dnload.mtk_state = MTK_WAIT_SIZE_ACK; + rc = write(dnload.serial_fd.fd, &dnload.mtk_send_size, + sizeof(dnload.mtk_send_size)); + break; + case MTK_WAIT_SIZE_ACK: + if (memcmp(buffer, dnload.mtk_send_size, + sizeof(dnload.mtk_send_size))) + break; + printf("Received size ack\n"); + dnload.expect_hdlc = 1; + dnload.mtk_state = MTK_SENDING_BLOCKS; + dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE; + bufptr -= 3; + break; + case MTK_SENDING_BLOCKS: + if (!(buffer[0] == dnload.block[dnload.echo_bytecount])) + printf("Warning: Byte %i of Block %i doesn't match," + " check your serial connection!\n", + dnload.echo_bytecount, dnload.block_number); + dnload.echo_bytecount++; + + if ((dnload.echo_bytecount+1) > MTK_BLOCK_SIZE) { + if ( dnload.block_number == dnload.block_count) { + rc = write(dnload.serial_fd.fd, + &mtk_command[3], 1); + printf("Sending branch command\n"); + dnload.expect_hdlc = 0; + dnload.mtk_state = MTK_WAIT_BRANCH_CMD_ACK; + break; + } + printf("Received Block %i preparing next block\n", + dnload.block_number); + mtk_prepare_block(); + dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE; + } + break; + case MTK_WAIT_BRANCH_CMD_ACK: + if (!(buffer[0] == mtk_command[3])) + break; + dnload.mtk_state = MTK_WAIT_BRANCH_ADDR_ACK; + printf("Received branch command ack, sending address\n"); + + rc = write(dnload.serial_fd.fd, &dnload.load_address, + sizeof(dnload.load_address)); + break; + case MTK_WAIT_BRANCH_ADDR_ACK: + if (memcmp(buffer, dnload.load_address, + sizeof(dnload.load_address))) + break; + printf("Received branch address ack, code should run now\n"); + serial_set_baudrate(MODEM_BAUDRATE); + dnload.serial_fd.when = BSC_FD_READ; + dnload.mtk_state = MTK_FINISHED; + dnload.write_ptr = dnload.data; + dnload.expect_hdlc = 1; + break; + default: + break; + } + + bufptr += nbytes; + return nbytes; +} + +static int serial_read(struct bsc_fd *fd, unsigned int flags) +{ + int rc; + if (flags & BSC_FD_READ) { + switch (dnload.mode) { + case MODE_ROMLOAD: + rc = handle_read_romload(); + break; + case MODE_MTK: + rc = handle_read_mtk(); + break; + default: + rc = handle_read(); + break; + } + if (rc == 0) + exit(2); + } + + if (flags & BSC_FD_WRITE) { + rc = handle_write(); + if (rc == 1) + dnload.state = WAITING_PROMPT1; + } + return 0; +} + +static int parse_mode(const char *arg) +{ + if (!strcasecmp(arg, "c123")) + return MODE_C123; + else if (!strcasecmp(arg, "c123xor")) + return MODE_C123xor; + else if (!strcasecmp(arg, "c140")) + return MODE_C140; + else if (!strcasecmp(arg, "c140xor")) + return MODE_C140xor; + else if (!strcasecmp(arg, "c155")) + return MODE_C155; + else if (!strcasecmp(arg, "romload")) + return MODE_ROMLOAD; + else if (!strcasecmp(arg, "mtk")) + return MODE_MTK; + + return -1; +} + +#define HELP_TEXT \ + "[ -v | -h ] [ -d [t][r] ] [ -p /dev/ttyXXXX ]\n" \ + "\t\t [ -s /tmp/osmocom_l2 ]\n" \ + "\t\t [ -l /tmp/osmocom_loader ]\n" \ + "\t\t [ -m {c123,c123xor,c140,c140xor,c155,romload,mtk} ]\n" \ + "\t\t [ -c /to-be-chainloaded-file.bin ]\n" \ + "\t\t [ -i beacon-interval (mS) ]\n" \ + "\t\t file.bin\n\n" \ + "* Open serial port /dev/ttyXXXX (connected to your phone)\n" \ + "* Perform handshaking with the ramloader in the phone\n" \ + "* Download file.bin to the attached phone (base address 0x00800100)\n" + +static int usage(const char *name) +{ + printf("Usage: %s ", name); + printf(HELP_TEXT); + exit(2); +} + +static int version(const char *name) +{ + printf("%s version %s\n", name, PACKAGE_VERSION); + exit(2); +} + +static int un_tool_read(struct bsc_fd *fd, unsigned int flags) +{ + int rc, c; + uint16_t length = 0xffff; + uint8_t buf[4096]; + struct tool_connection *con = (struct tool_connection *)fd->data; + + c = 0; + while(c < 2) { + rc = read(fd->fd, &buf + c, 2 - c); + if(rc == 0) { + // disconnect + goto close; + } + if(rc < 0) { + if(errno == EAGAIN) { + continue; + } + fprintf(stderr, "Err from socket: %s\n", strerror(errno)); + goto close; + } + c += rc; + } + + length = ntohs(*(uint16_t*)buf); + + c = 0; + while(c < length) { + rc = read(fd->fd, &buf + c, length - c); + if(rc == 0) { + // disconnect + goto close; + } + if(rc < 0) { + if(errno == EAGAIN) { + continue; + } + fprintf(stderr, "Err from socket: %s\n", strerror(errno)); + goto close; + } + c += rc; + } + + hdlc_send_to_phone(con->server->dlci, buf, length); + + return 0; +close: + + close(fd->fd); + bsc_unregister_fd(fd); + llist_del(&con->entry); + talloc_free(con); + return -1; +} + +/* accept a new connection */ +static int tool_accept(struct bsc_fd *fd, unsigned int flags) +{ + struct tool_server *srv = (struct tool_server *)fd->data; + struct tool_connection *con; + struct sockaddr_un un_addr; + socklen_t len; + int rc; + + len = sizeof(un_addr); + rc = accept(fd->fd, (struct sockaddr *) &un_addr, &len); + if (rc < 0) { + fprintf(stderr, "Failed to accept a new connection.\n"); + return -1; + } + + con = talloc_zero(NULL, struct tool_connection); + if (!con) { + fprintf(stderr, "Failed to create tool connection.\n"); + return -1; + } + + con->server = srv; + + con->fd.fd = rc; + con->fd.when = BSC_FD_READ; + con->fd.cb = un_tool_read; + con->fd.data = con; + if (bsc_register_fd(&con->fd) != 0) { + fprintf(stderr, "Failed to register the fd.\n"); + return -1; + } + + llist_add(&con->entry, &srv->connections); + return 0; +} + +/* + * Register and start a tool server + */ +static int register_tool_server(struct tool_server *ts, + const char *path, + uint8_t dlci) +{ + struct bsc_fd *bfd = &ts->bfd; + struct sockaddr_un local; + unsigned int namelen; + int rc; + + bfd->fd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (bfd->fd < 0) { + fprintf(stderr, "Failed to create Unix Domain Socket.\n"); + return -1; + } + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + unlink(local.sun_path); + + /* we use the same magic that X11 uses in Xtranssock.c for + * calculating the proper length of the sockaddr */ +#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) + local.sun_len = strlen(local.sun_path); +#endif +#if defined(BSD44SOCKETS) || defined(SUN_LEN) + namelen = SUN_LEN(&local); +#else + namelen = strlen(local.sun_path) + + offsetof(struct sockaddr_un, sun_path); +#endif + + rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); + if (rc != 0) { + fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n", + local.sun_path); + return -1; + } + + if (listen(bfd->fd, 0) != 0) { + fprintf(stderr, "Failed to listen.\n"); + return -1; + } + + bfd->when = BSC_FD_READ; + bfd->cb = tool_accept; + bfd->data = ts; + + ts->dlci = dlci; + INIT_LLIST_HEAD(&ts->connections); + + tool_server_for_dlci[dlci] = ts; + + sercomm_register_rx_cb(dlci, hdlc_tool_cb); + + if (bsc_register_fd(bfd) != 0) { + fprintf(stderr, "Failed to register the bfd.\n"); + return -1; + } + + return 0; +} + +extern void hdlc_tpudbg_cb(uint8_t dlci, struct msgb *msg); + +void parse_debug(const char *str) +{ + while(*str) { + switch(*str) { + case 't': + dnload.dump_tx = 1; + break; + case 'r': + dnload.dump_rx = 1; + break; + default: + printf("Unknown debug flag %c\n", *str); + abort(); + break; + } + str++; + } +} + +int main(int argc, char **argv) +{ + int opt, flags; + uint32_t tmp_load_address = ROMLOAD_ADDRESS; + const char *serial_dev = "/dev/ttyUSB1"; + const char *layer2_un_path = "/tmp/osmocom_l2"; + const char *loader_un_path = "/tmp/osmocom_loader"; + + dnload.mode = MODE_C123; + dnload.chainload_filename = NULL; + dnload.beacon_interval = DEFAULT_BEACON_INTERVAL; + + while ((opt = getopt(argc, argv, "d:hl:p:m:c:s:i:v")) != -1) { + switch (opt) { + case 'p': + serial_dev = optarg; + break; + case 'm': + dnload.mode = parse_mode(optarg); + if (dnload.mode < 0) + usage(argv[0]); + break; + case 's': + layer2_un_path = optarg; + break; + case 'l': + loader_un_path = optarg; + break; + case 'v': + version(argv[0]); + break; + case 'd': + parse_debug(optarg); + break; + case 'c': + dnload.chainload_filename = optarg; + break; + case 'i': + dnload.beacon_interval = atoi(optarg) * 1000; + break; + case 'h': + default: + usage(argv[0]); + break; + } + } + + if (argc <= optind) { + dnload.filename = NULL; + } else { + dnload.filename = argv[optind]; + } + + dnload.serial_fd.fd = serial_init(serial_dev); + if (dnload.serial_fd.fd < 0) { + fprintf(stderr, "Cannot open serial device %s\n", serial_dev); + exit(1); + } + + if (bsc_register_fd(&dnload.serial_fd) != 0) { + fprintf(stderr, "Failed to register the serial.\n"); + exit(1); + } + + /* Set serial socket to non-blocking mode of operation */ + flags = fcntl(dnload.serial_fd.fd, F_GETFL); + flags |= O_NONBLOCK; + fcntl(dnload.serial_fd.fd, F_SETFL, flags); + + dnload.serial_fd.when = BSC_FD_READ; + dnload.serial_fd.cb = serial_read; + + /* initialize the HDLC layer */ + sercomm_init(); + sercomm_register_rx_cb(SC_DLCI_CONSOLE, hdlc_console_cb); + sercomm_register_rx_cb(SC_DLCI_DEBUG, hdlc_tpudbg_cb); + + /* unix domain socket handling */ + if (register_tool_server(&dnload.layer2_server, layer2_un_path, + SC_DLCI_L1A_L23) != 0) + exit(1); + + if (register_tool_server(&dnload.loader_server, loader_un_path, + SC_DLCI_LOADER) != 0) + exit(1); + + /* if in romload mode, start our beacon timer */ + if (dnload.mode == MODE_ROMLOAD) { + tmp_load_address = ROMLOAD_ADDRESS; + serial_set_baudrate(ROMLOAD_INIT_BAUDRATE); + tick_timer.cb = &beacon_timer_cb; + tick_timer.data = &tick_timer; + bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval); + } + else if (dnload.mode == MODE_MTK) { + tmp_load_address = MTK_ADDRESS; + serial_set_baudrate(MTK_INIT_BAUDRATE); + tick_timer.cb = &mtk_timer_cb; + tick_timer.data = &tick_timer; + bsc_schedule_timer(&tick_timer, 0, dnload.beacon_interval); + } + + dnload.load_address[0] = (tmp_load_address >> 24) & 0xff; + dnload.load_address[1] = (tmp_load_address >> 16) & 0xff; + dnload.load_address[2] = (tmp_load_address >> 8) & 0xff; + dnload.load_address[3] = tmp_load_address & 0xff; + + while (1) + bsc_select_main(0); + + close(dnload.serial_fd.fd); + + exit(0); +} diff --git a/src/host/osmocon/osmoload.c b/src/host/osmocon/osmoload.c new file mode 100644 index 00000000..b0754637 --- /dev/null +++ b/src/host/osmocon/osmoload.c @@ -0,0 +1,1216 @@ +/* control utility for the Calypso bootloader */ + +/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <errno.h> +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <getopt.h> + +#include <arpa/inet.h> + +#include <sys/stat.h> + +#include <sys/socket.h> +#include <sys/un.h> + +#include <osmocore/msgb.h> +#include <osmocore/select.h> +#include <osmocore/timer.h> +#include <osmocore/crc16.h> + +#include <loader/protocol.h> + +#define MSGB_MAX 256 + +#define MEM_MSG_MAX (MSGB_MAX - 16) + +#define DEFAULT_SOCKET "/tmp/osmocom_loader" + +static struct bsc_fd connection; + +enum { + STATE_INIT, + STATE_QUERY_PENDING, + STATE_DUMP_IN_PROGRESS, + STATE_LOAD_IN_PROGRESS, + STATE_FLASHRANGE_GET_INFO, + STATE_FLASHRANGE_IN_PROGRESS, + STATE_PROGRAM_GET_INFO, + STATE_PROGRAM_IN_PROGRESS, + STATE_DUMPING, +}; + +struct flashblock { + uint8_t fb_chip; + uint32_t fb_offset; + uint32_t fb_addr; + uint32_t fb_size; +}; + +static struct { + /* debug flags */ + unsigned char print_requests; + unsigned char print_replies; + + /* quit flag for main loop */ + unsigned char quit; + + /* main state machine */ + int state; + + /* pending query command */ + uint8_t command; + + /* general timeout */ + struct timer_list timeout; + + /* binary i/o for firmware images */ + FILE *binfile; + /* buffer containing binfile data */ + char *binbuf; + + /* memory operation state */ + uint8_t memchip; /* target chip (for flashes) */ + uint32_t membase; /* target base address of operation */ + uint32_t memlen; /* length of entire operation */ + uint32_t memoff; /* offset for next request */ + uint16_t memcrc; /* crc for current request */ + uint16_t memreq; /* length of current request */ + + /* array of all flash blocks */ + uint8_t flashcommand; + uint32_t numblocks; + struct flashblock blocks[512]; +} osmoload; + +static int usage(const char *name) +{ + printf("Usage: %s [ -v | -h ] [ -d tr ] [ -m {c123,c155} ] [ -l /tmp/osmocom_loader ] COMMAND ...\n", name); + + puts("\n Memory commands:"); + puts(" memget <hex-address> <hex-length> - Peek at memory"); + puts(" memput <hex-address> <hex-bytes> - Poke at memory"); + puts(" memdump <hex-address> <hex-length> <file>- Dump memory to file"); + puts(" memload <hex-address> <file> - Load file into memory"); + + puts("\n Flash commands:"); + puts(" finfo - Information about flash chips"); + puts(" funlock <address> <length> - Unlock flash block"); + puts(" flock <address> <length> - Lock flash block"); + puts(" flockdown <address> <length> - Lock down flash block"); + puts(" fgetlock <address> <length> - Get locking state of block"); + puts(" ferase <address> <length> - Erase flash range"); + puts(" fprogram <chip> <address> <file> - Program file into flash"); + + puts("\n Execution commands:"); + puts(" jump <hex-address> - Jump to address"); + puts(" jumpflash - Jump to flash loader"); + puts(" jumprom - Jump to rom loader"); + + puts("\n Device lifecycle:"); + puts(" ping - Ping the loader"); + puts(" reset - Reset device"); + puts(" off - Power off device"); + + puts("\n Debug:"); + puts(" dump - Dump loader traffic to console"); + + exit(2); +} + +static int version(const char *name) +{ + //printf("\n%s version %s\n", name, VERSION); + exit(2); +} + +static void hexdump(const uint8_t *data, unsigned int len) +{ + const uint8_t *bufptr = data; + const uint8_t const *endptr = bufptr + len; + int n, m, i, hexchr; + + for (n=0; n < len; n+=32, bufptr += 32) { + hexchr = 0; + for(m = 0; m < 32 && bufptr < endptr; m++, bufptr++) { + if((m) && !(m%4)) { + putchar(' '); + hexchr++; + } + printf("%02x", *bufptr); + hexchr+=2; + } + bufptr -= m; + int n = 71 - hexchr; + for(i = 0; i < n; i++) { + putchar(' '); + } + + putchar(' '); + + for(m = 0; m < 32 && bufptr < endptr; m++, bufptr++) { + if(isgraph(*bufptr)) { + putchar(*bufptr); + } else { + putchar('.'); + } + } + bufptr -= m; + + putchar('\n'); + } +} + +static void +loader_send_request(struct msgb *msg) { + int rc; + u_int16_t len = htons(msg->len); + + if(osmoload.print_requests) { + printf("Sending %d bytes:\n", msg->len); + hexdump(msg->data, msg->len); + } + + rc = write(connection.fd, &len, sizeof(len)); + if(rc != sizeof(len)) { + fprintf(stderr, "Error writing.\n"); + exit(2); + } + + rc = write(connection.fd, msg->data, msg->len); + if(rc != msg->len) { + fprintf(stderr, "Error writing.\n"); + exit(2); + } +} + +static void loader_do_memdump(uint16_t crc, void *address, size_t length); +static void loader_do_memload(); +static void loader_do_fprogram(); +static void loader_do_flashrange(uint8_t cmd, struct msgb *msg, uint8_t chip, uint32_t address, uint32_t status); + +static void memop_timeout(void *dummy) { + switch(osmoload.state) { + case STATE_LOAD_IN_PROGRESS: + printf("Timeout. Repeating."); + osmoload.memoff -= osmoload.memreq; + loader_do_memload(); + break; + default: + break; + } + return; +} + +static void +loader_parse_flash_info(struct msgb *msg) { + uint8_t nchips; + + nchips = msgb_get_u8(msg); + + osmoload.numblocks = 0; + + int chip; + for(chip = 0; chip < nchips; chip++) { + + uint32_t address; + address = msgb_get_u32(msg); + + uint32_t chipsize; + chipsize = msgb_get_u32(msg); + + uint8_t nregions; + nregions = msgb_get_u8(msg); + + printf(" chip %d at 0x%8.8x of %d bytes in %d regions\n", chip, address, chipsize, nregions); + + uint32_t curoffset = 0; + int region; + for(region = 0; region < nregions; region++) { + uint16_t blockcount = msgb_get_u32(msg); + uint32_t blocksize = msgb_get_u32(msg); + + printf(" region %d with %d blocks of %d bytes each\n", region, blockcount, blocksize); + + int block; + for(block = 0; block < blockcount; block++) { + osmoload.blocks[osmoload.numblocks].fb_chip = chip; + osmoload.blocks[osmoload.numblocks].fb_offset = curoffset; + osmoload.blocks[osmoload.numblocks].fb_addr = address + curoffset; + osmoload.blocks[osmoload.numblocks].fb_size = blocksize; + + printf(" block %d with %d bytes at 0x%8.8x on chip %d\n", + osmoload.numblocks, blocksize, address + curoffset, chip); + + curoffset += blocksize; + + osmoload.numblocks++; + } + } + } +} + + +static void +loader_handle_reply(struct msgb *msg) { + if(osmoload.print_replies) { + printf("Received %d bytes:\n", msg->len); + hexdump(msg->data, msg->len); + } + + uint8_t cmd = msgb_get_u8(msg); + + uint8_t chip; + uint8_t length; + uint16_t crc; + uint32_t address; + uint32_t entrypoint; + uint32_t status; + + void *data; + + switch(cmd) { + case LOADER_INIT: + address = msgb_get_u32(msg); + entrypoint = msgb_get_u32(msg); + printf("Loader at entry %x has been started, requesting load to %x\n", entrypoint, address); + break; + case LOADER_PING: + case LOADER_RESET: + case LOADER_POWEROFF: + case LOADER_ENTER_ROM_LOADER: + case LOADER_ENTER_FLASH_LOADER: + break; + case LOADER_MEM_READ: + length = msgb_get_u8(msg); + crc = msgb_get_u16(msg); + address = msgb_get_u32(msg); + data = msgb_get(msg, length); + break; + case LOADER_MEM_WRITE: + length = msgb_get_u8(msg); + crc = msgb_get_u16(msg); + address = msgb_get_u32(msg); + break; + case LOADER_JUMP: + address = msgb_get_u32(msg); + break; + case LOADER_FLASH_INFO: + break; + case LOADER_FLASH_GETLOCK: + case LOADER_FLASH_ERASE: + case LOADER_FLASH_UNLOCK: + case LOADER_FLASH_LOCK: + case LOADER_FLASH_LOCKDOWN: + chip = msgb_get_u8(msg); + address = msgb_get_u32(msg); + status = msgb_get_u32(msg); + break; + case LOADER_FLASH_PROGRAM: + length = msgb_get_u8(msg); + crc = msgb_get_u16(msg); + msgb_get_u8(msg); // XXX align + chip = msgb_get_u8(msg); + address = msgb_get_u32(msg); + status = msgb_get_u32(msg); + break; + default: + printf("Received unknown reply %d:\n", cmd); + hexdump(msg->data, msg->len); + osmoload.quit = 1; + return; + } + + switch(osmoload.state) { + case STATE_QUERY_PENDING: + case STATE_DUMPING: + switch(cmd) { + case LOADER_PING: + printf("Received pong.\n"); + break; + case LOADER_RESET: + printf("Reset confirmed.\n"); + break; + case LOADER_POWEROFF: + printf("Poweroff confirmed.\n"); + break; + case LOADER_ENTER_ROM_LOADER: + printf("Jump to ROM loader confirmed.\n"); + break; + case LOADER_ENTER_FLASH_LOADER: + printf("Jump to flash loader confirmed.\n"); + break; + case LOADER_MEM_READ: + printf("Received memory dump of %d bytes at 0x%x:\n", length, address); + hexdump(data, length); + break; + case LOADER_MEM_WRITE: + printf("Confirmed memory write of %d bytes at 0x%x.\n", length, address); + break; + case LOADER_JUMP: + printf("Confirmed jump to 0x%x.\n", address); + break; + case LOADER_FLASH_ERASE: + printf("Confirmed flash erase of chip %d address 0x%8.8x, status %s\n", + chip, address, status ? "FAILED" : "ok"); + break; + case LOADER_FLASH_GETLOCK: + printf("Lock state of chip %d address 0x%8.8x is %s\n", + chip, address, (status == LOADER_FLASH_LOCKED ? "locked" + : (status == LOADER_FLASH_LOCKED_DOWN ? "locked down" + : (status == LOADER_FLASH_UNLOCKED ? "unlocked" + : "UNKNOWN")))); + break; + case LOADER_FLASH_UNLOCK: + printf("Confirmed flash unlock of chip %d address 0x%8.8x, status %s\n", + chip, address, status ? "FAILED" : "ok"); + break; + case LOADER_FLASH_LOCK: + printf("Confirmed flash lock of chip %d address 0x%8.8x, status %s\n", + chip, address, status ? "FAILED" : "ok"); + break; + case LOADER_FLASH_LOCKDOWN: + printf("Confirmed flash lockdown of chip %d address 0x%8.8x, status %s\n", + chip, address, status ? "FAILED" : "ok"); + break; + case LOADER_FLASH_INFO: + loader_parse_flash_info(msg); + break; + default: + break; + } + if(osmoload.state == STATE_QUERY_PENDING) { + if(osmoload.command == cmd) { + osmoload.quit = 1; + } + } + break; + case STATE_DUMP_IN_PROGRESS: + if(cmd == LOADER_MEM_READ) { + loader_do_memdump(crc, data, length); + } + break; + case STATE_LOAD_IN_PROGRESS: + if(cmd == LOADER_MEM_WRITE) { + if(osmoload.memcrc != crc) { + osmoload.memoff -= osmoload.memreq; + printf("\nbad crc %4.4x (not %4.4x) at offset 0x%8.8x", crc, osmoload.memcrc, osmoload.memoff); + } else { + putchar('.'); + } + loader_do_memload(); + } + break; + case STATE_PROGRAM_GET_INFO: + case STATE_PROGRAM_IN_PROGRESS: + if(cmd == LOADER_FLASH_PROGRAM) { + if(osmoload.memcrc != crc) { + osmoload.memoff -= osmoload.memreq; + printf("\nbad crc %4.4x (not %4.4x) at offset 0x%8.8x", crc, osmoload.memcrc, osmoload.memoff); + } else { + putchar('.'); + } + if(((int)status) != 0) { + printf("\nstatus %d, aborting\n", status); + exit(1); + } + loader_do_fprogram(); + } + break; + case STATE_FLASHRANGE_GET_INFO: + case STATE_FLASHRANGE_IN_PROGRESS: + loader_do_flashrange(cmd, msg, chip, address, status); + break; + default: + break; + } + + fflush(stdout); +} + +static int +loader_read_cb(struct bsc_fd *fd, unsigned int flags) { + struct msgb *msg; + u_int16_t len; + int rc; + + msg = msgb_alloc(MSGB_MAX, "loader"); + if (!msg) { + fprintf(stderr, "Failed to allocate msg.\n"); + return -1; + } + + rc = read(fd->fd, &len, sizeof(len)); + if (rc < sizeof(len)) { + fprintf(stderr, "Short read. Error.\n"); + exit(2); + } + + if (ntohs(len) > MSGB_MAX) { + fprintf(stderr, "Length is too big: %u\n", ntohs(len)); + msgb_free(msg); + return -1; + } + + /* blocking read for the poor... we can starve in here... */ + msg->l2h = msgb_put(msg, ntohs(len)); + rc = read(fd->fd, msg->l2h, msgb_l2len(msg)); + if (rc != msgb_l2len(msg)) { + fprintf(stderr, "Can not read data: rc: %d errno: %d\n", rc, errno); + msgb_free(msg); + return -1; + } + + loader_handle_reply(msg); + + msgb_free(msg); + + return 0; +} + +static void +loader_connect(const char *socket_path) { + int rc; + struct sockaddr_un local; + struct bsc_fd *conn = &connection; + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, socket_path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + + conn->fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (conn->fd < 0) { + fprintf(stderr, "Failed to create unix domain socket.\n"); + exit(1); + } + + rc = connect(conn->fd, (struct sockaddr *) &local, + sizeof(local.sun_family) + strlen(local.sun_path)); + if (rc < 0) { + fprintf(stderr, "Failed to connect to '%s'.\n", local.sun_path); + exit(1); + } + + conn->when = BSC_FD_READ; + conn->cb = loader_read_cb; + conn->data = NULL; + + if (bsc_register_fd(conn) != 0) { + fprintf(stderr, "Failed to register fd.\n"); + exit(1); + } +} + +static void +loader_send_simple(uint8_t command) { + struct msgb *msg = msgb_alloc(MSGB_MAX, "loader"); + msgb_put_u8(msg, command); + loader_send_request(msg); + msgb_free(msg); + + osmoload.command = command; +} + +static void +loader_start_query(uint8_t command) { + loader_send_simple(command); + osmoload.state = STATE_QUERY_PENDING; +} + +static void +loader_send_flash_query(uint8_t command, uint8_t chip, uint32_t address) { + struct msgb *msg = msgb_alloc(MSGB_MAX, "loader"); + msgb_put_u8(msg, command); + msgb_put_u8(msg, chip); + msgb_put_u32(msg, address); + loader_send_request(msg); + msgb_free(msg); + + osmoload.command = command; +} + +static void +loader_start_flash_query(uint8_t command, uint8_t chip, uint32_t address) { + loader_send_flash_query(command, chip, address); + osmoload.state = STATE_QUERY_PENDING; +} + +static void +loader_start_memget(uint8_t length, uint32_t address) { + struct msgb *msg = msgb_alloc(MSGB_MAX, "loader"); + msgb_put_u8(msg, LOADER_MEM_READ); + msgb_put_u8(msg, length); + msgb_put_u32(msg, address); + loader_send_request(msg); + msgb_free(msg); + + osmoload.state = STATE_QUERY_PENDING; + osmoload.command = LOADER_MEM_READ; +} + +static void +loader_start_memput(uint8_t length, uint32_t address, void *data) { + struct msgb *msg = msgb_alloc(MSGB_MAX, "loader"); + msgb_put_u8(msg, LOADER_MEM_WRITE); + msgb_put_u8(msg, length); + msgb_put_u16(msg, crc16(0, data, length)); + msgb_put_u32(msg, address); + memcpy(msgb_put(msg, length), data, length); + loader_send_request(msg); + msgb_free(msg); + + osmoload.state = STATE_QUERY_PENDING; + osmoload.command = LOADER_MEM_WRITE; +} + +static void +loader_start_jump(uint32_t address) { + struct msgb *msg = msgb_alloc(MSGB_MAX, "loader"); + msgb_put_u8(msg, LOADER_JUMP); + msgb_put_u32(msg, address); + loader_send_request(msg); + msgb_free(msg); + + osmoload.state = STATE_QUERY_PENDING; + osmoload.command = LOADER_JUMP; +} + + +static void +loader_do_memdump(uint16_t crc, void *data, size_t length) { + int rc; + + if(data && length) { + osmoload.memcrc = crc16(0, data, length); + if(osmoload.memcrc != crc) { + osmoload.memoff -= osmoload.memreq; + printf("\nbad crc %4.4x (not %4.4x) at offset 0x%8.8x", crc, osmoload.memcrc, osmoload.memoff); + } else { + putchar('.'); + } + + memcpy(osmoload.binbuf + osmoload.memoff, data, length); + osmoload.memoff += length; + } + + uint32_t rembytes = osmoload.memlen - osmoload.memoff; + + if(!rembytes) { + puts("done."); + osmoload.quit = 1; + + unsigned c = osmoload.memlen; + char *p = osmoload.binbuf; + while(c) { + rc = fwrite(p, 1, c, osmoload.binfile); + if(ferror(osmoload.binfile)) { + printf("Could not read from file: %s\n", strerror(errno)); + exit(1); + } + c -= rc; + p += rc; + } + fclose(osmoload.binfile); + osmoload.binfile = NULL; + + free(osmoload.binbuf); + + return; + } + + uint8_t reqbytes = (rembytes < MEM_MSG_MAX) ? rembytes : MEM_MSG_MAX; + + struct msgb *msg = msgb_alloc(MSGB_MAX, "loader"); + + msgb_put_u8(msg, LOADER_MEM_READ); + msgb_put_u8(msg, reqbytes); + msgb_put_u32(msg, osmoload.membase + osmoload.memoff); + loader_send_request(msg); + msgb_free(msg); +} + +static void +loader_do_memload() { + uint32_t rembytes = osmoload.memlen - osmoload.memoff; + + if(!rembytes) { + puts("done."); + osmoload.quit = 1; + return; + } + + bsc_schedule_timer(&osmoload.timeout, 0, 500000); + + uint8_t reqbytes = (rembytes < MEM_MSG_MAX) ? rembytes : MEM_MSG_MAX; + + osmoload.memcrc = crc16(0, (uint8_t *) osmoload.binbuf + osmoload.memoff, reqbytes); + osmoload.memreq = reqbytes; + + struct msgb *msg = msgb_alloc(MSGB_MAX, "loader"); + + msgb_put_u8(msg, LOADER_MEM_WRITE); + msgb_put_u8(msg, reqbytes); + msgb_put_u16(msg, osmoload.memcrc); + msgb_put_u32(msg, osmoload.membase + osmoload.memoff); + + unsigned char *p = msgb_put(msg, reqbytes); + memcpy(p, osmoload.binbuf + osmoload.memoff, reqbytes); + +#if 0 + printf("Sending %u bytes at offset %u to address %x with crc %x\n", + reqbytes, osmoload.memoff, osmoload.membase + osmoload.memoff, + osmoload.memcrc); +#endif + + loader_send_request(msg); + + msgb_free(msg); + + osmoload.memoff += reqbytes; +} + +static void +loader_do_fprogram() { + uint32_t rembytes = osmoload.memlen - osmoload.memoff; + + if(!rembytes) { + puts("done."); + osmoload.quit = 1; + return; + } + + bsc_schedule_timer(&osmoload.timeout, 0, 10000000); + + uint8_t reqbytes = (rembytes < MEM_MSG_MAX) ? rembytes : MEM_MSG_MAX; + + osmoload.memcrc = crc16(0, (uint8_t *) osmoload.binbuf + osmoload.memoff, reqbytes); + osmoload.memreq = reqbytes; + + struct msgb *msg = msgb_alloc(MSGB_MAX, "loader"); + + msgb_put_u8(msg, LOADER_FLASH_PROGRAM); + msgb_put_u8(msg, reqbytes); + msgb_put_u16(msg, osmoload.memcrc); + msgb_put_u8(msg, 0); // XXX: align data to 16bit + msgb_put_u8(msg, osmoload.memchip); + msgb_put_u32(msg, osmoload.membase + osmoload.memoff); + + unsigned char *p = msgb_put(msg, reqbytes); + memcpy(p, osmoload.binbuf + osmoload.memoff, reqbytes); + +#if 0 + printf("Sending %u bytes at offset %u to address %x with crc %x\n", + reqbytes, osmoload.memoff, osmoload.membase + osmoload.memoff, + osmoload.memcrc); +#endif + + loader_send_request(msg); + + msgb_free(msg); + + osmoload.memoff += reqbytes; +} + +static void +loader_start_memdump(uint32_t length, uint32_t address, char *file) { + printf("Dumping %u bytes of memory at 0x%x to file %s\n", length, address, file); + + osmoload.binbuf = malloc(length); + if(!osmoload.binbuf) { + printf("Could not allocate %u bytes for %s.\n", length, file); + exit(1); + } + + osmoload.binfile = fopen(file, "wb"); + if(!osmoload.binfile) { + printf("Could not open %s: %s\n", file, strerror(errno)); + exit(1); + } + + osmoload.membase = address; + osmoload.memlen = length; + osmoload.memoff = 0; + + osmoload.state = STATE_DUMP_IN_PROGRESS; + loader_do_memdump(0, NULL, 0); +} + +static void +loader_start_memload(uint32_t address, char *file) { + int rc; + struct stat st; + + rc = stat(file, &st); + if(rc < 0) { + printf("Could not stat %s: %s\n", file, strerror(errno)); + exit(1); + } + + uint32_t length = st.st_size; + + printf("Loading %u bytes of memory to address 0x%x from file %s\n", length, address, file); + + osmoload.binbuf = malloc(length); + if(!osmoload.binbuf) { + printf("Could not allocate %u bytes for %s.\n", length, file); + exit(1); + } + + osmoload.binfile = fopen(file, "rb"); + if(!osmoload.binfile) { + printf("Could not open %s: %s\n", file, strerror(errno)); + exit(1); + } + + unsigned c = length; + char *p = osmoload.binbuf; + while(c) { + rc = fread(p, 1, c, osmoload.binfile); + if(ferror(osmoload.binfile)) { + printf("Could not read from file: %s\n", strerror(errno)); + exit(1); + } + c -= rc; + p += rc; + } + fclose(osmoload.binfile); + osmoload.binfile = NULL; + + osmoload.membase = address; + osmoload.memlen = length; + osmoload.memoff = 0; + + osmoload.state = STATE_LOAD_IN_PROGRESS; + loader_do_memload(); +} + +static void +loader_start_flashrange(uint8_t command, uint32_t address, uint32_t length) { + switch(command) { + case LOADER_FLASH_ERASE: + printf("Erasing %u bytes of flash at 0x%x\n", length, address); + break; + case LOADER_FLASH_LOCK: + printf("Locking %u bytes of flash at 0x%x\n", length, address); + break; + case LOADER_FLASH_LOCKDOWN: + printf("Locking down %u bytes of flash at 0x%x\n", length, address); + break; + case LOADER_FLASH_UNLOCK: + printf("Unlocking %u bytes of flash at 0x%x\n", length, address); + break; + case LOADER_FLASH_GETLOCK: + printf("Getlocking %u bytes of flash at 0x%x\n", length, address); + break; + default: + puts("Unknown range command"); + abort(); + break; + } + + osmoload.flashcommand = command; + + osmoload.membase = address; + osmoload.memlen = length; + osmoload.memoff = 0; + + printf(" requesting flash info to determine block layout\n"); + + osmoload.state = STATE_FLASHRANGE_GET_INFO; + + loader_send_simple(LOADER_FLASH_INFO); +} + +static void +loader_do_flashrange(uint8_t cmd, struct msgb *msg, uint8_t chip, uint32_t address, uint32_t status) { + switch(osmoload.state) { + case STATE_FLASHRANGE_GET_INFO: + if(cmd == LOADER_FLASH_INFO) { + loader_parse_flash_info(msg); + osmoload.state = STATE_FLASHRANGE_IN_PROGRESS; + loader_do_flashrange(0, NULL, 0, 0, 0); + } + break; + case STATE_FLASHRANGE_IN_PROGRESS: + { + if(msg) { + if(cmd == osmoload.flashcommand) { + if(cmd == LOADER_FLASH_GETLOCK) { + printf(" lock state of chip %d address 0x%8.8x is %s\n", + chip, address, (status == LOADER_FLASH_LOCKED ? "locked" + : (status == LOADER_FLASH_LOCKED_DOWN ? "locked down" + : (status == LOADER_FLASH_UNLOCKED ? "unlocked" + : "UNKNOWN")))); + } else { + printf(" confirmed operation on chip %d address 0x%8.8x, status %s\n", + chip, address, status ? "FAILED" : "ok"); + } + } else { + break; + } + } + + uint32_t addr = osmoload.membase + osmoload.memoff; + + if(osmoload.memoff >= osmoload.memlen) { + puts(" operation done"); + osmoload.quit = 1; + break; + } + + uint8_t found = 0; + int i; + for(i = 0; i < osmoload.numblocks; i++) { + struct flashblock *b = &osmoload.blocks[i]; + if(b->fb_addr == addr) { + loader_send_flash_query(osmoload.flashcommand, b->fb_chip, b->fb_offset); + osmoload.memoff += b->fb_size; + found = 1; + break; + } + } + if(!found) { + puts("Oops!? Block not found?"); // XXX message + abort(); + } + } + break; + } +} + +static void +loader_start_fprogram(uint8_t chip, uint32_t address, char *file) { + int rc; + struct stat st; + + rc = stat(file, &st); + if(rc < 0) { + printf("Could not stat %s: %s\n", file, strerror(errno)); + exit(1); + } + + uint32_t length = st.st_size; + + printf("Loading %u bytes of memory at 0x%x in chip %d from file %s\n", length, address, chip, file); + + osmoload.binbuf = malloc(length); + if(!osmoload.binbuf) { + printf("Could not allocate %u bytes for %s.\n", length, file); + exit(1); + } + + osmoload.binfile = fopen(file, "rb"); + if(!osmoload.binfile) { + printf("Could not open %s: %s\n", file, strerror(errno)); + exit(1); + } + + unsigned c = length; + char *p = osmoload.binbuf; + while(c) { + rc = fread(p, 1, c, osmoload.binfile); + if(ferror(osmoload.binfile)) { + printf("Could not read from file: %s\n", strerror(errno)); + exit(1); + } + c -= rc; + p += rc; + } + fclose(osmoload.binfile); + osmoload.binfile = NULL; + + osmoload.memchip = chip; + osmoload.membase = address; + osmoload.memlen = length; + osmoload.memoff = 0; + + osmoload.state = STATE_PROGRAM_IN_PROGRESS; + + loader_do_fprogram(); +} + +static void +query_timeout(void *dummy) { + puts("Query timed out."); + exit(2); +} + +static void +loader_command(char *name, int cmdc, char **cmdv) { + if(!cmdc) { + usage(name); + } + + char *cmd = cmdv[0]; + + char buf[MEM_MSG_MAX]; + memset(buf, 23, sizeof(buf)); + + if(!strcmp(cmd, "dump")) { + osmoload.state = STATE_DUMPING; + } else if(!strcmp(cmd, "ping")) { + loader_start_query(LOADER_PING); + } else if(!strcmp(cmd, "off")) { + loader_start_query(LOADER_POWEROFF); + } else if(!strcmp(cmd, "reset")) { + loader_start_query(LOADER_RESET); + } else if(!strcmp(cmd, "jumprom")) { + loader_start_query(LOADER_ENTER_ROM_LOADER); + } else if(!strcmp(cmd, "jumpflash")) { + loader_start_query(LOADER_ENTER_FLASH_LOADER); + } else if(!strcmp(cmd, "finfo")) { + puts("Requesting flash layout info"); + loader_start_query(LOADER_FLASH_INFO); + } else if(!strcmp(cmd, "memput")) { + uint32_t address; + + if(cmdc < 3) { + usage(name); + } + + address = strtoul(cmdv[1], NULL, 16); + + unsigned int i; + char *hex = cmdv[2]; + if(strlen(hex)&1) { + puts("Invalid hex string."); + exit(2); + } + for(i = 0; i <= sizeof(buf) && i < strlen(hex)/2; i++) { + if(i >= sizeof(buf)) { + puts("Value too long for single message"); + exit(2); + } + unsigned int byte; + int count = sscanf(hex + i * 2, "%02x", &byte); + if(count != 1) { + puts("Invalid hex string."); + exit(2); + } + buf[i] = byte & 0xFF; + } + + loader_start_memput(i & 0xFF, address, buf); + } else if(!strcmp(cmd, "memget")) { + uint32_t address; + uint8_t length; + + if(cmdc < 3) { + usage(name); + } + + address = strtoul(cmdv[1], NULL, 16); + length = strtoul(cmdv[2], NULL, 16); + + if(length > MEM_MSG_MAX) { + puts("Too many bytes"); + exit(2); + } + + loader_start_memget(length, address); + } else if(!strcmp(cmd, "jump")) { + uint32_t address; + + if(cmdc < 2) { + usage(name); + } + + address = strtoul(cmdv[1], NULL, 16); + + loader_start_jump(address); + } else if(!strcmp(cmd, "memdump")) { + uint32_t address; + uint32_t length; + + if(cmdc < 4) { + usage(name); + } + + address = strtoul(cmdv[1], NULL, 16); + length = strtoul(cmdv[2], NULL, 16); + + loader_start_memdump(length, address, cmdv[3]); + } else if(!strcmp(cmd, "memload")) { + uint32_t address; + + if(cmdc < 3) { + usage(name); + } + + address = strtoul(cmdv[1], NULL, 16); + + loader_start_memload(address, cmdv[2]); + } else if(!strcmp(cmd, "fprogram")) { + uint8_t chip; + uint32_t address; + + if(cmdc < 4) { + usage(name); + } + + chip = strtoul(cmdv[1], NULL, 10); + address = strtoul(cmdv[2], NULL, 16); + + loader_start_fprogram(chip, address, cmdv[3]); + } else if(!strcmp(cmd, "ferase")) { + uint32_t address; + uint32_t length; + + if(cmdc < 3) { + usage(name); + } + + address = strtoul(cmdv[1], NULL, 16); + length = strtoul(cmdv[2], NULL, 16); + + loader_start_flashrange(LOADER_FLASH_ERASE, address, length); + } else if(!strcmp(cmd, "flock")) { + uint32_t address; + uint32_t length; + + if(cmdc < 3) { + usage(name); + } + + address = strtoul(cmdv[1], NULL, 16); + length = strtoul(cmdv[2], NULL, 16); + + loader_start_flashrange(LOADER_FLASH_LOCK, address, length); + } else if(!strcmp(cmd, "flockdown")) { + uint32_t address; + uint32_t length; + + if(cmdc < 3) { + usage(name); + } + + address = strtoul(cmdv[1], NULL, 16); + length = strtoul(cmdv[2], NULL, 16); + + loader_start_flashrange(LOADER_FLASH_LOCKDOWN, address, length); + } else if(!strcmp(cmd, "funlock")) { + uint32_t address; + uint32_t length; + + if(cmdc < 3) { + usage(name); + } + + address = strtoul(cmdv[1], NULL, 16); + length = strtoul(cmdv[2], NULL, 16); + + loader_start_flashrange(LOADER_FLASH_UNLOCK, address, length); + } else if(!strcmp(cmd, "fgetlock")) { + uint32_t address; + uint32_t length; + + if(cmdc < 3) { + usage(name); + } + + address = strtoul(cmdv[1], NULL, 16); + length = strtoul(cmdv[2], NULL, 16); + + loader_start_flashrange(LOADER_FLASH_GETLOCK, address, length); + } else if(!strcmp(cmd, "help")) { + usage(name); + } else { + printf("Unknown command '%s'\n", cmd); + usage(name); + } + + if(osmoload.state == STATE_QUERY_PENDING) { + osmoload.timeout.cb = &query_timeout; + bsc_schedule_timer(&osmoload.timeout, 0, 5000000); + } + if(osmoload.state == STATE_LOAD_IN_PROGRESS) { + osmoload.timeout.cb = &memop_timeout; + } + +} + +void +setdebug(const char *name, char c) { + switch(c) { + case 't': + osmoload.print_requests = 1; + break; + case 'r': + osmoload.print_replies = 1; + break; + default: + usage(name); + break; + } +} + +int +main(int argc, char **argv) { + int opt; + char *loader_un_path = "/tmp/osmocom_loader"; + const char *debugopt; + + while((opt = getopt(argc, argv, "d:hl:m:v")) != -1) { + switch(opt) { + case 'd': + debugopt = optarg; + while(*debugopt) { + setdebug(argv[0], *debugopt); + debugopt++; + } + break; + case 'l': + loader_un_path = optarg; + break; + case 'm': + puts("model selection not implemented"); + exit(2); + break; + case 'v': + version(argv[0]); + break; + case 'h': + default: + usage(argv[0]); + break; + } + } + + osmoload.quit = 0; + + loader_connect(loader_un_path); + + loader_command(argv[0], argc - optind, argv + optind); + + while(!osmoload.quit) { + bsc_select_main(0); + } + + if(osmoload.binfile) { + fclose(osmoload.binfile); + } + + return 0; +} diff --git a/src/host/osmocon/tpu_debug.c b/src/host/osmocon/tpu_debug.c new file mode 100644 index 00000000..f54bd40f --- /dev/null +++ b/src/host/osmocon/tpu_debug.c @@ -0,0 +1,138 @@ +/* Calypso TPU debugger, displays and decodes TPU instruction RAM */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> + +#include <osmocore/msgb.h> + +/* TPU disassembler begin */ + +static const char *tpu_instr_name[] = { + [0] = "SLEEP", + [1] = "AT", + [2] = "OFFSET", + [3] = "SYNCHRO", + [4] = "MOVE", + [5] = "WAIT", + [6] = "UNDEFINED6", + [7] = "UNDEFINED7", +}; + +static const char *tpu_addr_name[0x1f] = { + [0] = "TSP_CTLR1", + [1] = "TSP_CTRL2", + [4] = "TSP_TX_1", + [3] = "TSP_TX_2", + [2] = "TSP_TX_3", + [5] = "TSP_TX_4", + [6] = "TSPACT_L", + [7] = "TSPACT_H", + [9] = "TSP_SET1", + [0xa] = "TSP_SET2", + [0xb] = "TSP_SET3", + [0x10] = "DSP_INT_PG", + [0x11] = "GAUGING_EN", +}; + +static uint8_t tpu_reg_cache[0x1f]; +static uint16_t tpu_qbit; + +static void tpu_show_instr(uint16_t tpu) +{ + uint16_t instr = tpu >> 13; + uint16_t param = tpu & 0x1fff; + uint16_t addr, data, bitlen; + uint32_t tsp_data; + + tpu_qbit++; + + printf("\t %04u %04x %s ", tpu_qbit, tpu, tpu_instr_name[instr]); + switch (instr) { + case 0: + tpu_qbit = 0; + default: + break; + case 1: + tpu_qbit = param; + printf("%u ", param); + break; + case 5: + tpu_qbit += param; + printf("%u ", param); + break; + case 2: + case 3: + printf("%u ", param); + break; + case 4: + addr = param & 0x1f; + data = param >> 5; + tpu_reg_cache[addr] = data; + printf("%10s=0x%04x ", tpu_addr_name[addr], data); + switch (addr) { + case 0: + bitlen = (data & 0x1f) + 1; + printf("DEV_IDX=%u, BITLEN=%u ", data >> 5, bitlen); + if (bitlen <= 8) { + tsp_data = tpu_reg_cache[4]; + printf(" TSP_DATA=0x%02x ", tsp_data); + } else if (bitlen <= 16) { + tsp_data = tpu_reg_cache[3]; + tsp_data |= tpu_reg_cache[4] << 8; + printf(" TSP_DATA=0x%04x ", tsp_data); + } else if (bitlen <= 24) { + tsp_data = tpu_reg_cache[2]; + tsp_data |= tpu_reg_cache[3] << 8; + tsp_data |= tpu_reg_cache[4] << 16; + printf(" TSP_DATA=0x%06x ", tsp_data); + } else { + tsp_data = tpu_reg_cache[5]; + tsp_data |= tpu_reg_cache[2] << 8; + tsp_data |= tpu_reg_cache[3] << 16; + tsp_data |= tpu_reg_cache[4] << 24; + printf(" TSP_DATA=0x%08x ", tsp_data); + } + break; + case 1: + if (data & 0x01) + printf("READ "); + if (data & 0x02) + printf("WRITE "); + break; + } + } + printf("\n"); +} + +void hdlc_tpudbg_cb(uint8_t dlci, struct msgb *msg) +{ + uint32_t *fn = (uint32_t *) msg->data; + uint16_t *tpu; + + printf("TPU FN %u\n", *fn); + for (tpu = (uint16_t *) (msg->data + 4); tpu < (uint16_t *) msg->tail; tpu++) + tpu_show_instr(*tpu); + + msgb_free(msg); +} diff --git a/src/host/rita_pll/mtk_pll.pl b/src/host/rita_pll/mtk_pll.pl new file mode 100755 index 00000000..ff931c5c --- /dev/null +++ b/src/host/rita_pll/mtk_pll.pl @@ -0,0 +1,79 @@ +#!/usr/bin/perl + +# Rx Mode and Tx Mode: +# N = Nint + (Nfrac / 130) = (0.5*Fvco) / 26M +# where 0 <= Nfrac < 130 +# where 0 <= Ninit <= 127 (as Nint is 7 bit) +# where Fvco = 4 * Fch (GSM 850/900), Fvco = 2 * Fch (GSM 1800/1900) + +# (Nint + (Nfrac / 130)) * 52 MHz = Fvco + +sub mtk_fvco($$) { + my ($nint, $nfrac) = @_; + return ($nint + ($nfrac / 130)) * (26 * 2) +} + +sub hr() { + printf("======================================================================\n"); +} + +sub vco_print($$$) +{ + my ($nint, $nfrac, $hiband) = @_; + my $fvco = mtk_fvco($nint, $nfrac); + my $mult; + + if ($hiband == 1) { + $mult = 2; + } else { + $mult = 4; + } + + printf("Fch=%4.2f (Fvco=%4.2f, Nint=%03u, Nfrac=%03u)\n", + $fvco/$mult, $fvco, $nint, $nfrac); +} + +#for (my $nint = 0; $nint <= 127; $nint++) { +# for (my $nfrac = 0; $nfrac <= 130; $nfrac++) { +# vco_print($nint, $nfrac); +# } +#} + +printf("PLL Rx Low Band:\n"); +for (my $nint = 68; $nint <= 73; $nint++) { +#for GSM 810 +#for (my $b = 132; $b <= 150; $b++) { + for (my $nfrac = 0; $nfrac <= 130; $nfrac++) { + vco_print($nint, $nfrac, 0); + } +} + +hr(); +printf("PLL Rx High Band:\n"); +for (my $nint = 69; $nint <= 79; $nint++) { + for (my $nfrac = 0; $nfrac <= 130; $nfrac++) { + vco_print($nint, $nfrac, 1); + } +} + +hr(); +printf("PLL Tx Low Band:\n"); +for (my $nint = 63; $nint <= 70; $nint++) { + for (my $nfrac = 0; $nfrac <= 130; $nfrac++) { + vco_print($nint, $nfrac, 0); + } +} + + +hr(); +printf("PLL Tx High Band\n"); +for (my $nint = 65; $nint <= 73; $nint++) { + for (my $nfrac = 0; $nfrac <= 130; $nfrac++) { + vco_print($nint, $nfrac, 1); + } +} + + + +exit(0); + diff --git a/src/host/rita_pll/mtk_pll.txt b/src/host/rita_pll/mtk_pll.txt new file mode 100644 index 00000000..a008656f --- /dev/null +++ b/src/host/rita_pll/mtk_pll.txt @@ -0,0 +1,4461 @@ +PLL Rx Low Band: +Fch=884.00 (Fvco=3536.00, Nint=068, Nfrac=000) +Fch=884.10 (Fvco=3536.40, Nint=068, Nfrac=001) +Fch=884.20 (Fvco=3536.80, Nint=068, Nfrac=002) +Fch=884.30 (Fvco=3537.20, Nint=068, Nfrac=003) +Fch=884.40 (Fvco=3537.60, Nint=068, Nfrac=004) +Fch=884.50 (Fvco=3538.00, Nint=068, Nfrac=005) +Fch=884.60 (Fvco=3538.40, Nint=068, Nfrac=006) +Fch=884.70 (Fvco=3538.80, Nint=068, Nfrac=007) +Fch=884.80 (Fvco=3539.20, Nint=068, Nfrac=008) +Fch=884.90 (Fvco=3539.60, Nint=068, Nfrac=009) +Fch=885.00 (Fvco=3540.00, Nint=068, Nfrac=010) +Fch=885.10 (Fvco=3540.40, Nint=068, Nfrac=011) +Fch=885.20 (Fvco=3540.80, Nint=068, Nfrac=012) +Fch=885.30 (Fvco=3541.20, Nint=068, Nfrac=013) +Fch=885.40 (Fvco=3541.60, Nint=068, Nfrac=014) +Fch=885.50 (Fvco=3542.00, Nint=068, Nfrac=015) +Fch=885.60 (Fvco=3542.40, Nint=068, Nfrac=016) +Fch=885.70 (Fvco=3542.80, Nint=068, Nfrac=017) +Fch=885.80 (Fvco=3543.20, Nint=068, Nfrac=018) +Fch=885.90 (Fvco=3543.60, Nint=068, Nfrac=019) +Fch=886.00 (Fvco=3544.00, Nint=068, Nfrac=020) +Fch=886.10 (Fvco=3544.40, Nint=068, Nfrac=021) +Fch=886.20 (Fvco=3544.80, Nint=068, Nfrac=022) +Fch=886.30 (Fvco=3545.20, Nint=068, Nfrac=023) +Fch=886.40 (Fvco=3545.60, Nint=068, Nfrac=024) +Fch=886.50 (Fvco=3546.00, Nint=068, Nfrac=025) +Fch=886.60 (Fvco=3546.40, Nint=068, Nfrac=026) +Fch=886.70 (Fvco=3546.80, Nint=068, Nfrac=027) +Fch=886.80 (Fvco=3547.20, Nint=068, Nfrac=028) +Fch=886.90 (Fvco=3547.60, Nint=068, Nfrac=029) +Fch=887.00 (Fvco=3548.00, Nint=068, Nfrac=030) +Fch=887.10 (Fvco=3548.40, Nint=068, Nfrac=031) +Fch=887.20 (Fvco=3548.80, Nint=068, Nfrac=032) +Fch=887.30 (Fvco=3549.20, Nint=068, Nfrac=033) +Fch=887.40 (Fvco=3549.60, Nint=068, Nfrac=034) +Fch=887.50 (Fvco=3550.00, Nint=068, Nfrac=035) +Fch=887.60 (Fvco=3550.40, Nint=068, Nfrac=036) +Fch=887.70 (Fvco=3550.80, Nint=068, Nfrac=037) +Fch=887.80 (Fvco=3551.20, Nint=068, Nfrac=038) +Fch=887.90 (Fvco=3551.60, Nint=068, Nfrac=039) +Fch=888.00 (Fvco=3552.00, Nint=068, Nfrac=040) +Fch=888.10 (Fvco=3552.40, Nint=068, Nfrac=041) +Fch=888.20 (Fvco=3552.80, Nint=068, Nfrac=042) +Fch=888.30 (Fvco=3553.20, Nint=068, Nfrac=043) +Fch=888.40 (Fvco=3553.60, Nint=068, Nfrac=044) +Fch=888.50 (Fvco=3554.00, Nint=068, Nfrac=045) +Fch=888.60 (Fvco=3554.40, Nint=068, Nfrac=046) +Fch=888.70 (Fvco=3554.80, Nint=068, Nfrac=047) +Fch=888.80 (Fvco=3555.20, Nint=068, Nfrac=048) +Fch=888.90 (Fvco=3555.60, Nint=068, Nfrac=049) +Fch=889.00 (Fvco=3556.00, Nint=068, Nfrac=050) +Fch=889.10 (Fvco=3556.40, Nint=068, Nfrac=051) +Fch=889.20 (Fvco=3556.80, Nint=068, Nfrac=052) +Fch=889.30 (Fvco=3557.20, Nint=068, Nfrac=053) +Fch=889.40 (Fvco=3557.60, Nint=068, Nfrac=054) +Fch=889.50 (Fvco=3558.00, Nint=068, Nfrac=055) +Fch=889.60 (Fvco=3558.40, Nint=068, Nfrac=056) +Fch=889.70 (Fvco=3558.80, Nint=068, Nfrac=057) +Fch=889.80 (Fvco=3559.20, Nint=068, Nfrac=058) +Fch=889.90 (Fvco=3559.60, Nint=068, Nfrac=059) +Fch=890.00 (Fvco=3560.00, Nint=068, Nfrac=060) +Fch=890.10 (Fvco=3560.40, Nint=068, Nfrac=061) +Fch=890.20 (Fvco=3560.80, Nint=068, Nfrac=062) +Fch=890.30 (Fvco=3561.20, Nint=068, Nfrac=063) +Fch=890.40 (Fvco=3561.60, Nint=068, Nfrac=064) +Fch=890.50 (Fvco=3562.00, Nint=068, Nfrac=065) +Fch=890.60 (Fvco=3562.40, Nint=068, Nfrac=066) +Fch=890.70 (Fvco=3562.80, Nint=068, Nfrac=067) +Fch=890.80 (Fvco=3563.20, Nint=068, Nfrac=068) +Fch=890.90 (Fvco=3563.60, Nint=068, Nfrac=069) +Fch=891.00 (Fvco=3564.00, Nint=068, Nfrac=070) +Fch=891.10 (Fvco=3564.40, Nint=068, Nfrac=071) +Fch=891.20 (Fvco=3564.80, Nint=068, Nfrac=072) +Fch=891.30 (Fvco=3565.20, Nint=068, Nfrac=073) +Fch=891.40 (Fvco=3565.60, Nint=068, Nfrac=074) +Fch=891.50 (Fvco=3566.00, Nint=068, Nfrac=075) +Fch=891.60 (Fvco=3566.40, Nint=068, Nfrac=076) +Fch=891.70 (Fvco=3566.80, Nint=068, Nfrac=077) +Fch=891.80 (Fvco=3567.20, Nint=068, Nfrac=078) +Fch=891.90 (Fvco=3567.60, Nint=068, Nfrac=079) +Fch=892.00 (Fvco=3568.00, Nint=068, Nfrac=080) +Fch=892.10 (Fvco=3568.40, Nint=068, Nfrac=081) +Fch=892.20 (Fvco=3568.80, Nint=068, Nfrac=082) +Fch=892.30 (Fvco=3569.20, Nint=068, Nfrac=083) +Fch=892.40 (Fvco=3569.60, Nint=068, Nfrac=084) +Fch=892.50 (Fvco=3570.00, Nint=068, Nfrac=085) +Fch=892.60 (Fvco=3570.40, Nint=068, Nfrac=086) +Fch=892.70 (Fvco=3570.80, Nint=068, Nfrac=087) +Fch=892.80 (Fvco=3571.20, Nint=068, Nfrac=088) +Fch=892.90 (Fvco=3571.60, Nint=068, Nfrac=089) +Fch=893.00 (Fvco=3572.00, Nint=068, Nfrac=090) +Fch=893.10 (Fvco=3572.40, Nint=068, Nfrac=091) +Fch=893.20 (Fvco=3572.80, Nint=068, Nfrac=092) +Fch=893.30 (Fvco=3573.20, Nint=068, Nfrac=093) +Fch=893.40 (Fvco=3573.60, Nint=068, Nfrac=094) +Fch=893.50 (Fvco=3574.00, Nint=068, Nfrac=095) +Fch=893.60 (Fvco=3574.40, Nint=068, Nfrac=096) +Fch=893.70 (Fvco=3574.80, Nint=068, Nfrac=097) +Fch=893.80 (Fvco=3575.20, Nint=068, Nfrac=098) +Fch=893.90 (Fvco=3575.60, Nint=068, Nfrac=099) +Fch=894.00 (Fvco=3576.00, Nint=068, Nfrac=100) +Fch=894.10 (Fvco=3576.40, Nint=068, Nfrac=101) +Fch=894.20 (Fvco=3576.80, Nint=068, Nfrac=102) +Fch=894.30 (Fvco=3577.20, Nint=068, Nfrac=103) +Fch=894.40 (Fvco=3577.60, Nint=068, Nfrac=104) +Fch=894.50 (Fvco=3578.00, Nint=068, Nfrac=105) +Fch=894.60 (Fvco=3578.40, Nint=068, Nfrac=106) +Fch=894.70 (Fvco=3578.80, Nint=068, Nfrac=107) +Fch=894.80 (Fvco=3579.20, Nint=068, Nfrac=108) +Fch=894.90 (Fvco=3579.60, Nint=068, Nfrac=109) +Fch=895.00 (Fvco=3580.00, Nint=068, Nfrac=110) +Fch=895.10 (Fvco=3580.40, Nint=068, Nfrac=111) +Fch=895.20 (Fvco=3580.80, Nint=068, Nfrac=112) +Fch=895.30 (Fvco=3581.20, Nint=068, Nfrac=113) +Fch=895.40 (Fvco=3581.60, Nint=068, Nfrac=114) +Fch=895.50 (Fvco=3582.00, Nint=068, Nfrac=115) +Fch=895.60 (Fvco=3582.40, Nint=068, Nfrac=116) +Fch=895.70 (Fvco=3582.80, Nint=068, Nfrac=117) +Fch=895.80 (Fvco=3583.20, Nint=068, Nfrac=118) +Fch=895.90 (Fvco=3583.60, Nint=068, Nfrac=119) +Fch=896.00 (Fvco=3584.00, Nint=068, Nfrac=120) +Fch=896.10 (Fvco=3584.40, Nint=068, Nfrac=121) +Fch=896.20 (Fvco=3584.80, Nint=068, Nfrac=122) +Fch=896.30 (Fvco=3585.20, Nint=068, Nfrac=123) +Fch=896.40 (Fvco=3585.60, Nint=068, Nfrac=124) +Fch=896.50 (Fvco=3586.00, Nint=068, Nfrac=125) +Fch=896.60 (Fvco=3586.40, Nint=068, Nfrac=126) +Fch=896.70 (Fvco=3586.80, Nint=068, Nfrac=127) +Fch=896.80 (Fvco=3587.20, Nint=068, Nfrac=128) +Fch=896.90 (Fvco=3587.60, Nint=068, Nfrac=129) +Fch=897.00 (Fvco=3588.00, Nint=068, Nfrac=130) +Fch=897.00 (Fvco=3588.00, Nint=069, Nfrac=000) +Fch=897.10 (Fvco=3588.40, Nint=069, Nfrac=001) +Fch=897.20 (Fvco=3588.80, Nint=069, Nfrac=002) +Fch=897.30 (Fvco=3589.20, Nint=069, Nfrac=003) +Fch=897.40 (Fvco=3589.60, Nint=069, Nfrac=004) +Fch=897.50 (Fvco=3590.00, Nint=069, Nfrac=005) +Fch=897.60 (Fvco=3590.40, Nint=069, Nfrac=006) +Fch=897.70 (Fvco=3590.80, Nint=069, Nfrac=007) +Fch=897.80 (Fvco=3591.20, Nint=069, Nfrac=008) +Fch=897.90 (Fvco=3591.60, Nint=069, Nfrac=009) +Fch=898.00 (Fvco=3592.00, Nint=069, Nfrac=010) +Fch=898.10 (Fvco=3592.40, Nint=069, Nfrac=011) +Fch=898.20 (Fvco=3592.80, Nint=069, Nfrac=012) +Fch=898.30 (Fvco=3593.20, Nint=069, Nfrac=013) +Fch=898.40 (Fvco=3593.60, Nint=069, Nfrac=014) +Fch=898.50 (Fvco=3594.00, Nint=069, Nfrac=015) +Fch=898.60 (Fvco=3594.40, Nint=069, Nfrac=016) +Fch=898.70 (Fvco=3594.80, Nint=069, Nfrac=017) +Fch=898.80 (Fvco=3595.20, Nint=069, Nfrac=018) +Fch=898.90 (Fvco=3595.60, Nint=069, Nfrac=019) +Fch=899.00 (Fvco=3596.00, Nint=069, Nfrac=020) +Fch=899.10 (Fvco=3596.40, Nint=069, Nfrac=021) +Fch=899.20 (Fvco=3596.80, Nint=069, Nfrac=022) +Fch=899.30 (Fvco=3597.20, Nint=069, Nfrac=023) +Fch=899.40 (Fvco=3597.60, Nint=069, Nfrac=024) +Fch=899.50 (Fvco=3598.00, Nint=069, Nfrac=025) +Fch=899.60 (Fvco=3598.40, Nint=069, Nfrac=026) +Fch=899.70 (Fvco=3598.80, Nint=069, Nfrac=027) +Fch=899.80 (Fvco=3599.20, Nint=069, Nfrac=028) +Fch=899.90 (Fvco=3599.60, Nint=069, Nfrac=029) +Fch=900.00 (Fvco=3600.00, Nint=069, Nfrac=030) +Fch=900.10 (Fvco=3600.40, Nint=069, Nfrac=031) +Fch=900.20 (Fvco=3600.80, Nint=069, Nfrac=032) +Fch=900.30 (Fvco=3601.20, Nint=069, Nfrac=033) +Fch=900.40 (Fvco=3601.60, Nint=069, Nfrac=034) +Fch=900.50 (Fvco=3602.00, Nint=069, Nfrac=035) +Fch=900.60 (Fvco=3602.40, Nint=069, Nfrac=036) +Fch=900.70 (Fvco=3602.80, Nint=069, Nfrac=037) +Fch=900.80 (Fvco=3603.20, Nint=069, Nfrac=038) +Fch=900.90 (Fvco=3603.60, Nint=069, Nfrac=039) +Fch=901.00 (Fvco=3604.00, Nint=069, Nfrac=040) +Fch=901.10 (Fvco=3604.40, Nint=069, Nfrac=041) +Fch=901.20 (Fvco=3604.80, Nint=069, Nfrac=042) +Fch=901.30 (Fvco=3605.20, Nint=069, Nfrac=043) +Fch=901.40 (Fvco=3605.60, Nint=069, Nfrac=044) +Fch=901.50 (Fvco=3606.00, Nint=069, Nfrac=045) +Fch=901.60 (Fvco=3606.40, Nint=069, Nfrac=046) +Fch=901.70 (Fvco=3606.80, Nint=069, Nfrac=047) +Fch=901.80 (Fvco=3607.20, Nint=069, Nfrac=048) +Fch=901.90 (Fvco=3607.60, Nint=069, Nfrac=049) +Fch=902.00 (Fvco=3608.00, Nint=069, Nfrac=050) +Fch=902.10 (Fvco=3608.40, Nint=069, Nfrac=051) +Fch=902.20 (Fvco=3608.80, Nint=069, Nfrac=052) +Fch=902.30 (Fvco=3609.20, Nint=069, Nfrac=053) +Fch=902.40 (Fvco=3609.60, Nint=069, Nfrac=054) +Fch=902.50 (Fvco=3610.00, Nint=069, Nfrac=055) +Fch=902.60 (Fvco=3610.40, Nint=069, Nfrac=056) +Fch=902.70 (Fvco=3610.80, Nint=069, Nfrac=057) +Fch=902.80 (Fvco=3611.20, Nint=069, Nfrac=058) +Fch=902.90 (Fvco=3611.60, Nint=069, Nfrac=059) +Fch=903.00 (Fvco=3612.00, Nint=069, Nfrac=060) +Fch=903.10 (Fvco=3612.40, Nint=069, Nfrac=061) +Fch=903.20 (Fvco=3612.80, Nint=069, Nfrac=062) +Fch=903.30 (Fvco=3613.20, Nint=069, Nfrac=063) +Fch=903.40 (Fvco=3613.60, Nint=069, Nfrac=064) +Fch=903.50 (Fvco=3614.00, Nint=069, Nfrac=065) +Fch=903.60 (Fvco=3614.40, Nint=069, Nfrac=066) +Fch=903.70 (Fvco=3614.80, Nint=069, Nfrac=067) +Fch=903.80 (Fvco=3615.20, Nint=069, Nfrac=068) +Fch=903.90 (Fvco=3615.60, Nint=069, Nfrac=069) +Fch=904.00 (Fvco=3616.00, Nint=069, Nfrac=070) +Fch=904.10 (Fvco=3616.40, Nint=069, Nfrac=071) +Fch=904.20 (Fvco=3616.80, Nint=069, Nfrac=072) +Fch=904.30 (Fvco=3617.20, Nint=069, Nfrac=073) +Fch=904.40 (Fvco=3617.60, Nint=069, Nfrac=074) +Fch=904.50 (Fvco=3618.00, Nint=069, Nfrac=075) +Fch=904.60 (Fvco=3618.40, Nint=069, Nfrac=076) +Fch=904.70 (Fvco=3618.80, Nint=069, Nfrac=077) +Fch=904.80 (Fvco=3619.20, Nint=069, Nfrac=078) +Fch=904.90 (Fvco=3619.60, Nint=069, Nfrac=079) +Fch=905.00 (Fvco=3620.00, Nint=069, Nfrac=080) +Fch=905.10 (Fvco=3620.40, Nint=069, Nfrac=081) +Fch=905.20 (Fvco=3620.80, Nint=069, Nfrac=082) +Fch=905.30 (Fvco=3621.20, Nint=069, Nfrac=083) +Fch=905.40 (Fvco=3621.60, Nint=069, Nfrac=084) +Fch=905.50 (Fvco=3622.00, Nint=069, Nfrac=085) +Fch=905.60 (Fvco=3622.40, Nint=069, Nfrac=086) +Fch=905.70 (Fvco=3622.80, Nint=069, Nfrac=087) +Fch=905.80 (Fvco=3623.20, Nint=069, Nfrac=088) +Fch=905.90 (Fvco=3623.60, Nint=069, Nfrac=089) +Fch=906.00 (Fvco=3624.00, Nint=069, Nfrac=090) +Fch=906.10 (Fvco=3624.40, Nint=069, Nfrac=091) +Fch=906.20 (Fvco=3624.80, Nint=069, Nfrac=092) +Fch=906.30 (Fvco=3625.20, Nint=069, Nfrac=093) +Fch=906.40 (Fvco=3625.60, Nint=069, Nfrac=094) +Fch=906.50 (Fvco=3626.00, Nint=069, Nfrac=095) +Fch=906.60 (Fvco=3626.40, Nint=069, Nfrac=096) +Fch=906.70 (Fvco=3626.80, Nint=069, Nfrac=097) +Fch=906.80 (Fvco=3627.20, Nint=069, Nfrac=098) +Fch=906.90 (Fvco=3627.60, Nint=069, Nfrac=099) +Fch=907.00 (Fvco=3628.00, Nint=069, Nfrac=100) +Fch=907.10 (Fvco=3628.40, Nint=069, Nfrac=101) +Fch=907.20 (Fvco=3628.80, Nint=069, Nfrac=102) +Fch=907.30 (Fvco=3629.20, Nint=069, Nfrac=103) +Fch=907.40 (Fvco=3629.60, Nint=069, Nfrac=104) +Fch=907.50 (Fvco=3630.00, Nint=069, Nfrac=105) +Fch=907.60 (Fvco=3630.40, Nint=069, Nfrac=106) +Fch=907.70 (Fvco=3630.80, Nint=069, Nfrac=107) +Fch=907.80 (Fvco=3631.20, Nint=069, Nfrac=108) +Fch=907.90 (Fvco=3631.60, Nint=069, Nfrac=109) +Fch=908.00 (Fvco=3632.00, Nint=069, Nfrac=110) +Fch=908.10 (Fvco=3632.40, Nint=069, Nfrac=111) +Fch=908.20 (Fvco=3632.80, Nint=069, Nfrac=112) +Fch=908.30 (Fvco=3633.20, Nint=069, Nfrac=113) +Fch=908.40 (Fvco=3633.60, Nint=069, Nfrac=114) +Fch=908.50 (Fvco=3634.00, Nint=069, Nfrac=115) +Fch=908.60 (Fvco=3634.40, Nint=069, Nfrac=116) +Fch=908.70 (Fvco=3634.80, Nint=069, Nfrac=117) +Fch=908.80 (Fvco=3635.20, Nint=069, Nfrac=118) +Fch=908.90 (Fvco=3635.60, Nint=069, Nfrac=119) +Fch=909.00 (Fvco=3636.00, Nint=069, Nfrac=120) +Fch=909.10 (Fvco=3636.40, Nint=069, Nfrac=121) +Fch=909.20 (Fvco=3636.80, Nint=069, Nfrac=122) +Fch=909.30 (Fvco=3637.20, Nint=069, Nfrac=123) +Fch=909.40 (Fvco=3637.60, Nint=069, Nfrac=124) +Fch=909.50 (Fvco=3638.00, Nint=069, Nfrac=125) +Fch=909.60 (Fvco=3638.40, Nint=069, Nfrac=126) +Fch=909.70 (Fvco=3638.80, Nint=069, Nfrac=127) +Fch=909.80 (Fvco=3639.20, Nint=069, Nfrac=128) +Fch=909.90 (Fvco=3639.60, Nint=069, Nfrac=129) +Fch=910.00 (Fvco=3640.00, Nint=069, Nfrac=130) +Fch=910.00 (Fvco=3640.00, Nint=070, Nfrac=000) +Fch=910.10 (Fvco=3640.40, Nint=070, Nfrac=001) +Fch=910.20 (Fvco=3640.80, Nint=070, Nfrac=002) +Fch=910.30 (Fvco=3641.20, Nint=070, Nfrac=003) +Fch=910.40 (Fvco=3641.60, Nint=070, Nfrac=004) +Fch=910.50 (Fvco=3642.00, Nint=070, Nfrac=005) +Fch=910.60 (Fvco=3642.40, Nint=070, Nfrac=006) +Fch=910.70 (Fvco=3642.80, Nint=070, Nfrac=007) +Fch=910.80 (Fvco=3643.20, Nint=070, Nfrac=008) +Fch=910.90 (Fvco=3643.60, Nint=070, Nfrac=009) +Fch=911.00 (Fvco=3644.00, Nint=070, Nfrac=010) +Fch=911.10 (Fvco=3644.40, Nint=070, Nfrac=011) +Fch=911.20 (Fvco=3644.80, Nint=070, Nfrac=012) +Fch=911.30 (Fvco=3645.20, Nint=070, Nfrac=013) +Fch=911.40 (Fvco=3645.60, Nint=070, Nfrac=014) +Fch=911.50 (Fvco=3646.00, Nint=070, Nfrac=015) +Fch=911.60 (Fvco=3646.40, Nint=070, Nfrac=016) +Fch=911.70 (Fvco=3646.80, Nint=070, Nfrac=017) +Fch=911.80 (Fvco=3647.20, Nint=070, Nfrac=018) +Fch=911.90 (Fvco=3647.60, Nint=070, Nfrac=019) +Fch=912.00 (Fvco=3648.00, Nint=070, Nfrac=020) +Fch=912.10 (Fvco=3648.40, Nint=070, Nfrac=021) +Fch=912.20 (Fvco=3648.80, Nint=070, Nfrac=022) +Fch=912.30 (Fvco=3649.20, Nint=070, Nfrac=023) +Fch=912.40 (Fvco=3649.60, Nint=070, Nfrac=024) +Fch=912.50 (Fvco=3650.00, Nint=070, Nfrac=025) +Fch=912.60 (Fvco=3650.40, Nint=070, Nfrac=026) +Fch=912.70 (Fvco=3650.80, Nint=070, Nfrac=027) +Fch=912.80 (Fvco=3651.20, Nint=070, Nfrac=028) +Fch=912.90 (Fvco=3651.60, Nint=070, Nfrac=029) +Fch=913.00 (Fvco=3652.00, Nint=070, Nfrac=030) +Fch=913.10 (Fvco=3652.40, Nint=070, Nfrac=031) +Fch=913.20 (Fvco=3652.80, Nint=070, Nfrac=032) +Fch=913.30 (Fvco=3653.20, Nint=070, Nfrac=033) +Fch=913.40 (Fvco=3653.60, Nint=070, Nfrac=034) +Fch=913.50 (Fvco=3654.00, Nint=070, Nfrac=035) +Fch=913.60 (Fvco=3654.40, Nint=070, Nfrac=036) +Fch=913.70 (Fvco=3654.80, Nint=070, Nfrac=037) +Fch=913.80 (Fvco=3655.20, Nint=070, Nfrac=038) +Fch=913.90 (Fvco=3655.60, Nint=070, Nfrac=039) +Fch=914.00 (Fvco=3656.00, Nint=070, Nfrac=040) +Fch=914.10 (Fvco=3656.40, Nint=070, Nfrac=041) +Fch=914.20 (Fvco=3656.80, Nint=070, Nfrac=042) +Fch=914.30 (Fvco=3657.20, Nint=070, Nfrac=043) +Fch=914.40 (Fvco=3657.60, Nint=070, Nfrac=044) +Fch=914.50 (Fvco=3658.00, Nint=070, Nfrac=045) +Fch=914.60 (Fvco=3658.40, Nint=070, Nfrac=046) +Fch=914.70 (Fvco=3658.80, Nint=070, Nfrac=047) +Fch=914.80 (Fvco=3659.20, Nint=070, Nfrac=048) +Fch=914.90 (Fvco=3659.60, Nint=070, Nfrac=049) +Fch=915.00 (Fvco=3660.00, Nint=070, Nfrac=050) +Fch=915.10 (Fvco=3660.40, Nint=070, Nfrac=051) +Fch=915.20 (Fvco=3660.80, Nint=070, Nfrac=052) +Fch=915.30 (Fvco=3661.20, Nint=070, Nfrac=053) +Fch=915.40 (Fvco=3661.60, Nint=070, Nfrac=054) +Fch=915.50 (Fvco=3662.00, Nint=070, Nfrac=055) +Fch=915.60 (Fvco=3662.40, Nint=070, Nfrac=056) +Fch=915.70 (Fvco=3662.80, Nint=070, Nfrac=057) +Fch=915.80 (Fvco=3663.20, Nint=070, Nfrac=058) +Fch=915.90 (Fvco=3663.60, Nint=070, Nfrac=059) +Fch=916.00 (Fvco=3664.00, Nint=070, Nfrac=060) +Fch=916.10 (Fvco=3664.40, Nint=070, Nfrac=061) +Fch=916.20 (Fvco=3664.80, Nint=070, Nfrac=062) +Fch=916.30 (Fvco=3665.20, Nint=070, Nfrac=063) +Fch=916.40 (Fvco=3665.60, Nint=070, Nfrac=064) +Fch=916.50 (Fvco=3666.00, Nint=070, Nfrac=065) +Fch=916.60 (Fvco=3666.40, Nint=070, Nfrac=066) +Fch=916.70 (Fvco=3666.80, Nint=070, Nfrac=067) +Fch=916.80 (Fvco=3667.20, Nint=070, Nfrac=068) +Fch=916.90 (Fvco=3667.60, Nint=070, Nfrac=069) +Fch=917.00 (Fvco=3668.00, Nint=070, Nfrac=070) +Fch=917.10 (Fvco=3668.40, Nint=070, Nfrac=071) +Fch=917.20 (Fvco=3668.80, Nint=070, Nfrac=072) +Fch=917.30 (Fvco=3669.20, Nint=070, Nfrac=073) +Fch=917.40 (Fvco=3669.60, Nint=070, Nfrac=074) +Fch=917.50 (Fvco=3670.00, Nint=070, Nfrac=075) +Fch=917.60 (Fvco=3670.40, Nint=070, Nfrac=076) +Fch=917.70 (Fvco=3670.80, Nint=070, Nfrac=077) +Fch=917.80 (Fvco=3671.20, Nint=070, Nfrac=078) +Fch=917.90 (Fvco=3671.60, Nint=070, Nfrac=079) +Fch=918.00 (Fvco=3672.00, Nint=070, Nfrac=080) +Fch=918.10 (Fvco=3672.40, Nint=070, Nfrac=081) +Fch=918.20 (Fvco=3672.80, Nint=070, Nfrac=082) +Fch=918.30 (Fvco=3673.20, Nint=070, Nfrac=083) +Fch=918.40 (Fvco=3673.60, Nint=070, Nfrac=084) +Fch=918.50 (Fvco=3674.00, Nint=070, Nfrac=085) +Fch=918.60 (Fvco=3674.40, Nint=070, Nfrac=086) +Fch=918.70 (Fvco=3674.80, Nint=070, Nfrac=087) +Fch=918.80 (Fvco=3675.20, Nint=070, Nfrac=088) +Fch=918.90 (Fvco=3675.60, Nint=070, Nfrac=089) +Fch=919.00 (Fvco=3676.00, Nint=070, Nfrac=090) +Fch=919.10 (Fvco=3676.40, Nint=070, Nfrac=091) +Fch=919.20 (Fvco=3676.80, Nint=070, Nfrac=092) +Fch=919.30 (Fvco=3677.20, Nint=070, Nfrac=093) +Fch=919.40 (Fvco=3677.60, Nint=070, Nfrac=094) +Fch=919.50 (Fvco=3678.00, Nint=070, Nfrac=095) +Fch=919.60 (Fvco=3678.40, Nint=070, Nfrac=096) +Fch=919.70 (Fvco=3678.80, Nint=070, Nfrac=097) +Fch=919.80 (Fvco=3679.20, Nint=070, Nfrac=098) +Fch=919.90 (Fvco=3679.60, Nint=070, Nfrac=099) +Fch=920.00 (Fvco=3680.00, Nint=070, Nfrac=100) +Fch=920.10 (Fvco=3680.40, Nint=070, Nfrac=101) +Fch=920.20 (Fvco=3680.80, Nint=070, Nfrac=102) +Fch=920.30 (Fvco=3681.20, Nint=070, Nfrac=103) +Fch=920.40 (Fvco=3681.60, Nint=070, Nfrac=104) +Fch=920.50 (Fvco=3682.00, Nint=070, Nfrac=105) +Fch=920.60 (Fvco=3682.40, Nint=070, Nfrac=106) +Fch=920.70 (Fvco=3682.80, Nint=070, Nfrac=107) +Fch=920.80 (Fvco=3683.20, Nint=070, Nfrac=108) +Fch=920.90 (Fvco=3683.60, Nint=070, Nfrac=109) +Fch=921.00 (Fvco=3684.00, Nint=070, Nfrac=110) +Fch=921.10 (Fvco=3684.40, Nint=070, Nfrac=111) +Fch=921.20 (Fvco=3684.80, Nint=070, Nfrac=112) +Fch=921.30 (Fvco=3685.20, Nint=070, Nfrac=113) +Fch=921.40 (Fvco=3685.60, Nint=070, Nfrac=114) +Fch=921.50 (Fvco=3686.00, Nint=070, Nfrac=115) +Fch=921.60 (Fvco=3686.40, Nint=070, Nfrac=116) +Fch=921.70 (Fvco=3686.80, Nint=070, Nfrac=117) +Fch=921.80 (Fvco=3687.20, Nint=070, Nfrac=118) +Fch=921.90 (Fvco=3687.60, Nint=070, Nfrac=119) +Fch=922.00 (Fvco=3688.00, Nint=070, Nfrac=120) +Fch=922.10 (Fvco=3688.40, Nint=070, Nfrac=121) +Fch=922.20 (Fvco=3688.80, Nint=070, Nfrac=122) +Fch=922.30 (Fvco=3689.20, Nint=070, Nfrac=123) +Fch=922.40 (Fvco=3689.60, Nint=070, Nfrac=124) +Fch=922.50 (Fvco=3690.00, Nint=070, Nfrac=125) +Fch=922.60 (Fvco=3690.40, Nint=070, Nfrac=126) +Fch=922.70 (Fvco=3690.80, Nint=070, Nfrac=127) +Fch=922.80 (Fvco=3691.20, Nint=070, Nfrac=128) +Fch=922.90 (Fvco=3691.60, Nint=070, Nfrac=129) +Fch=923.00 (Fvco=3692.00, Nint=070, Nfrac=130) +Fch=923.00 (Fvco=3692.00, Nint=071, Nfrac=000) +Fch=923.10 (Fvco=3692.40, Nint=071, Nfrac=001) +Fch=923.20 (Fvco=3692.80, Nint=071, Nfrac=002) +Fch=923.30 (Fvco=3693.20, Nint=071, Nfrac=003) +Fch=923.40 (Fvco=3693.60, Nint=071, Nfrac=004) +Fch=923.50 (Fvco=3694.00, Nint=071, Nfrac=005) +Fch=923.60 (Fvco=3694.40, Nint=071, Nfrac=006) +Fch=923.70 (Fvco=3694.80, Nint=071, Nfrac=007) +Fch=923.80 (Fvco=3695.20, Nint=071, Nfrac=008) +Fch=923.90 (Fvco=3695.60, Nint=071, Nfrac=009) +Fch=924.00 (Fvco=3696.00, Nint=071, Nfrac=010) +Fch=924.10 (Fvco=3696.40, Nint=071, Nfrac=011) +Fch=924.20 (Fvco=3696.80, Nint=071, Nfrac=012) +Fch=924.30 (Fvco=3697.20, Nint=071, Nfrac=013) +Fch=924.40 (Fvco=3697.60, Nint=071, Nfrac=014) +Fch=924.50 (Fvco=3698.00, Nint=071, Nfrac=015) +Fch=924.60 (Fvco=3698.40, Nint=071, Nfrac=016) +Fch=924.70 (Fvco=3698.80, Nint=071, Nfrac=017) +Fch=924.80 (Fvco=3699.20, Nint=071, Nfrac=018) +Fch=924.90 (Fvco=3699.60, Nint=071, Nfrac=019) +Fch=925.00 (Fvco=3700.00, Nint=071, Nfrac=020) +Fch=925.10 (Fvco=3700.40, Nint=071, Nfrac=021) +Fch=925.20 (Fvco=3700.80, Nint=071, Nfrac=022) +Fch=925.30 (Fvco=3701.20, Nint=071, Nfrac=023) +Fch=925.40 (Fvco=3701.60, Nint=071, Nfrac=024) +Fch=925.50 (Fvco=3702.00, Nint=071, Nfrac=025) +Fch=925.60 (Fvco=3702.40, Nint=071, Nfrac=026) +Fch=925.70 (Fvco=3702.80, Nint=071, Nfrac=027) +Fch=925.80 (Fvco=3703.20, Nint=071, Nfrac=028) +Fch=925.90 (Fvco=3703.60, Nint=071, Nfrac=029) +Fch=926.00 (Fvco=3704.00, Nint=071, Nfrac=030) +Fch=926.10 (Fvco=3704.40, Nint=071, Nfrac=031) +Fch=926.20 (Fvco=3704.80, Nint=071, Nfrac=032) +Fch=926.30 (Fvco=3705.20, Nint=071, Nfrac=033) +Fch=926.40 (Fvco=3705.60, Nint=071, Nfrac=034) +Fch=926.50 (Fvco=3706.00, Nint=071, Nfrac=035) +Fch=926.60 (Fvco=3706.40, Nint=071, Nfrac=036) +Fch=926.70 (Fvco=3706.80, Nint=071, Nfrac=037) +Fch=926.80 (Fvco=3707.20, Nint=071, Nfrac=038) +Fch=926.90 (Fvco=3707.60, Nint=071, Nfrac=039) +Fch=927.00 (Fvco=3708.00, Nint=071, Nfrac=040) +Fch=927.10 (Fvco=3708.40, Nint=071, Nfrac=041) +Fch=927.20 (Fvco=3708.80, Nint=071, Nfrac=042) +Fch=927.30 (Fvco=3709.20, Nint=071, Nfrac=043) +Fch=927.40 (Fvco=3709.60, Nint=071, Nfrac=044) +Fch=927.50 (Fvco=3710.00, Nint=071, Nfrac=045) +Fch=927.60 (Fvco=3710.40, Nint=071, Nfrac=046) +Fch=927.70 (Fvco=3710.80, Nint=071, Nfrac=047) +Fch=927.80 (Fvco=3711.20, Nint=071, Nfrac=048) +Fch=927.90 (Fvco=3711.60, Nint=071, Nfrac=049) +Fch=928.00 (Fvco=3712.00, Nint=071, Nfrac=050) +Fch=928.10 (Fvco=3712.40, Nint=071, Nfrac=051) +Fch=928.20 (Fvco=3712.80, Nint=071, Nfrac=052) +Fch=928.30 (Fvco=3713.20, Nint=071, Nfrac=053) +Fch=928.40 (Fvco=3713.60, Nint=071, Nfrac=054) +Fch=928.50 (Fvco=3714.00, Nint=071, Nfrac=055) +Fch=928.60 (Fvco=3714.40, Nint=071, Nfrac=056) +Fch=928.70 (Fvco=3714.80, Nint=071, Nfrac=057) +Fch=928.80 (Fvco=3715.20, Nint=071, Nfrac=058) +Fch=928.90 (Fvco=3715.60, Nint=071, Nfrac=059) +Fch=929.00 (Fvco=3716.00, Nint=071, Nfrac=060) +Fch=929.10 (Fvco=3716.40, Nint=071, Nfrac=061) +Fch=929.20 (Fvco=3716.80, Nint=071, Nfrac=062) +Fch=929.30 (Fvco=3717.20, Nint=071, Nfrac=063) +Fch=929.40 (Fvco=3717.60, Nint=071, Nfrac=064) +Fch=929.50 (Fvco=3718.00, Nint=071, Nfrac=065) +Fch=929.60 (Fvco=3718.40, Nint=071, Nfrac=066) +Fch=929.70 (Fvco=3718.80, Nint=071, Nfrac=067) +Fch=929.80 (Fvco=3719.20, Nint=071, Nfrac=068) +Fch=929.90 (Fvco=3719.60, Nint=071, Nfrac=069) +Fch=930.00 (Fvco=3720.00, Nint=071, Nfrac=070) +Fch=930.10 (Fvco=3720.40, Nint=071, Nfrac=071) +Fch=930.20 (Fvco=3720.80, Nint=071, Nfrac=072) +Fch=930.30 (Fvco=3721.20, Nint=071, Nfrac=073) +Fch=930.40 (Fvco=3721.60, Nint=071, Nfrac=074) +Fch=930.50 (Fvco=3722.00, Nint=071, Nfrac=075) +Fch=930.60 (Fvco=3722.40, Nint=071, Nfrac=076) +Fch=930.70 (Fvco=3722.80, Nint=071, Nfrac=077) +Fch=930.80 (Fvco=3723.20, Nint=071, Nfrac=078) +Fch=930.90 (Fvco=3723.60, Nint=071, Nfrac=079) +Fch=931.00 (Fvco=3724.00, Nint=071, Nfrac=080) +Fch=931.10 (Fvco=3724.40, Nint=071, Nfrac=081) +Fch=931.20 (Fvco=3724.80, Nint=071, Nfrac=082) +Fch=931.30 (Fvco=3725.20, Nint=071, Nfrac=083) +Fch=931.40 (Fvco=3725.60, Nint=071, Nfrac=084) +Fch=931.50 (Fvco=3726.00, Nint=071, Nfrac=085) +Fch=931.60 (Fvco=3726.40, Nint=071, Nfrac=086) +Fch=931.70 (Fvco=3726.80, Nint=071, Nfrac=087) +Fch=931.80 (Fvco=3727.20, Nint=071, Nfrac=088) +Fch=931.90 (Fvco=3727.60, Nint=071, Nfrac=089) +Fch=932.00 (Fvco=3728.00, Nint=071, Nfrac=090) +Fch=932.10 (Fvco=3728.40, Nint=071, Nfrac=091) +Fch=932.20 (Fvco=3728.80, Nint=071, Nfrac=092) +Fch=932.30 (Fvco=3729.20, Nint=071, Nfrac=093) +Fch=932.40 (Fvco=3729.60, Nint=071, Nfrac=094) +Fch=932.50 (Fvco=3730.00, Nint=071, Nfrac=095) +Fch=932.60 (Fvco=3730.40, Nint=071, Nfrac=096) +Fch=932.70 (Fvco=3730.80, Nint=071, Nfrac=097) +Fch=932.80 (Fvco=3731.20, Nint=071, Nfrac=098) +Fch=932.90 (Fvco=3731.60, Nint=071, Nfrac=099) +Fch=933.00 (Fvco=3732.00, Nint=071, Nfrac=100) +Fch=933.10 (Fvco=3732.40, Nint=071, Nfrac=101) +Fch=933.20 (Fvco=3732.80, Nint=071, Nfrac=102) +Fch=933.30 (Fvco=3733.20, Nint=071, Nfrac=103) +Fch=933.40 (Fvco=3733.60, Nint=071, Nfrac=104) +Fch=933.50 (Fvco=3734.00, Nint=071, Nfrac=105) +Fch=933.60 (Fvco=3734.40, Nint=071, Nfrac=106) +Fch=933.70 (Fvco=3734.80, Nint=071, Nfrac=107) +Fch=933.80 (Fvco=3735.20, Nint=071, Nfrac=108) +Fch=933.90 (Fvco=3735.60, Nint=071, Nfrac=109) +Fch=934.00 (Fvco=3736.00, Nint=071, Nfrac=110) +Fch=934.10 (Fvco=3736.40, Nint=071, Nfrac=111) +Fch=934.20 (Fvco=3736.80, Nint=071, Nfrac=112) +Fch=934.30 (Fvco=3737.20, Nint=071, Nfrac=113) +Fch=934.40 (Fvco=3737.60, Nint=071, Nfrac=114) +Fch=934.50 (Fvco=3738.00, Nint=071, Nfrac=115) +Fch=934.60 (Fvco=3738.40, Nint=071, Nfrac=116) +Fch=934.70 (Fvco=3738.80, Nint=071, Nfrac=117) +Fch=934.80 (Fvco=3739.20, Nint=071, Nfrac=118) +Fch=934.90 (Fvco=3739.60, Nint=071, Nfrac=119) +Fch=935.00 (Fvco=3740.00, Nint=071, Nfrac=120) +Fch=935.10 (Fvco=3740.40, Nint=071, Nfrac=121) +Fch=935.20 (Fvco=3740.80, Nint=071, Nfrac=122) +Fch=935.30 (Fvco=3741.20, Nint=071, Nfrac=123) +Fch=935.40 (Fvco=3741.60, Nint=071, Nfrac=124) +Fch=935.50 (Fvco=3742.00, Nint=071, Nfrac=125) +Fch=935.60 (Fvco=3742.40, Nint=071, Nfrac=126) +Fch=935.70 (Fvco=3742.80, Nint=071, Nfrac=127) +Fch=935.80 (Fvco=3743.20, Nint=071, Nfrac=128) +Fch=935.90 (Fvco=3743.60, Nint=071, Nfrac=129) +Fch=936.00 (Fvco=3744.00, Nint=071, Nfrac=130) +Fch=936.00 (Fvco=3744.00, Nint=072, Nfrac=000) +Fch=936.10 (Fvco=3744.40, Nint=072, Nfrac=001) +Fch=936.20 (Fvco=3744.80, Nint=072, Nfrac=002) +Fch=936.30 (Fvco=3745.20, Nint=072, Nfrac=003) +Fch=936.40 (Fvco=3745.60, Nint=072, Nfrac=004) +Fch=936.50 (Fvco=3746.00, Nint=072, Nfrac=005) +Fch=936.60 (Fvco=3746.40, Nint=072, Nfrac=006) +Fch=936.70 (Fvco=3746.80, Nint=072, Nfrac=007) +Fch=936.80 (Fvco=3747.20, Nint=072, Nfrac=008) +Fch=936.90 (Fvco=3747.60, Nint=072, Nfrac=009) +Fch=937.00 (Fvco=3748.00, Nint=072, Nfrac=010) +Fch=937.10 (Fvco=3748.40, Nint=072, Nfrac=011) +Fch=937.20 (Fvco=3748.80, Nint=072, Nfrac=012) +Fch=937.30 (Fvco=3749.20, Nint=072, Nfrac=013) +Fch=937.40 (Fvco=3749.60, Nint=072, Nfrac=014) +Fch=937.50 (Fvco=3750.00, Nint=072, Nfrac=015) +Fch=937.60 (Fvco=3750.40, Nint=072, Nfrac=016) +Fch=937.70 (Fvco=3750.80, Nint=072, Nfrac=017) +Fch=937.80 (Fvco=3751.20, Nint=072, Nfrac=018) +Fch=937.90 (Fvco=3751.60, Nint=072, Nfrac=019) +Fch=938.00 (Fvco=3752.00, Nint=072, Nfrac=020) +Fch=938.10 (Fvco=3752.40, Nint=072, Nfrac=021) +Fch=938.20 (Fvco=3752.80, Nint=072, Nfrac=022) +Fch=938.30 (Fvco=3753.20, Nint=072, Nfrac=023) +Fch=938.40 (Fvco=3753.60, Nint=072, Nfrac=024) +Fch=938.50 (Fvco=3754.00, Nint=072, Nfrac=025) +Fch=938.60 (Fvco=3754.40, Nint=072, Nfrac=026) +Fch=938.70 (Fvco=3754.80, Nint=072, Nfrac=027) +Fch=938.80 (Fvco=3755.20, Nint=072, Nfrac=028) +Fch=938.90 (Fvco=3755.60, Nint=072, Nfrac=029) +Fch=939.00 (Fvco=3756.00, Nint=072, Nfrac=030) +Fch=939.10 (Fvco=3756.40, Nint=072, Nfrac=031) +Fch=939.20 (Fvco=3756.80, Nint=072, Nfrac=032) +Fch=939.30 (Fvco=3757.20, Nint=072, Nfrac=033) +Fch=939.40 (Fvco=3757.60, Nint=072, Nfrac=034) +Fch=939.50 (Fvco=3758.00, Nint=072, Nfrac=035) +Fch=939.60 (Fvco=3758.40, Nint=072, Nfrac=036) +Fch=939.70 (Fvco=3758.80, Nint=072, Nfrac=037) +Fch=939.80 (Fvco=3759.20, Nint=072, Nfrac=038) +Fch=939.90 (Fvco=3759.60, Nint=072, Nfrac=039) +Fch=940.00 (Fvco=3760.00, Nint=072, Nfrac=040) +Fch=940.10 (Fvco=3760.40, Nint=072, Nfrac=041) +Fch=940.20 (Fvco=3760.80, Nint=072, Nfrac=042) +Fch=940.30 (Fvco=3761.20, Nint=072, Nfrac=043) +Fch=940.40 (Fvco=3761.60, Nint=072, Nfrac=044) +Fch=940.50 (Fvco=3762.00, Nint=072, Nfrac=045) +Fch=940.60 (Fvco=3762.40, Nint=072, Nfrac=046) +Fch=940.70 (Fvco=3762.80, Nint=072, Nfrac=047) +Fch=940.80 (Fvco=3763.20, Nint=072, Nfrac=048) +Fch=940.90 (Fvco=3763.60, Nint=072, Nfrac=049) +Fch=941.00 (Fvco=3764.00, Nint=072, Nfrac=050) +Fch=941.10 (Fvco=3764.40, Nint=072, Nfrac=051) +Fch=941.20 (Fvco=3764.80, Nint=072, Nfrac=052) +Fch=941.30 (Fvco=3765.20, Nint=072, Nfrac=053) +Fch=941.40 (Fvco=3765.60, Nint=072, Nfrac=054) +Fch=941.50 (Fvco=3766.00, Nint=072, Nfrac=055) +Fch=941.60 (Fvco=3766.40, Nint=072, Nfrac=056) +Fch=941.70 (Fvco=3766.80, Nint=072, Nfrac=057) +Fch=941.80 (Fvco=3767.20, Nint=072, Nfrac=058) +Fch=941.90 (Fvco=3767.60, Nint=072, Nfrac=059) +Fch=942.00 (Fvco=3768.00, Nint=072, Nfrac=060) +Fch=942.10 (Fvco=3768.40, Nint=072, Nfrac=061) +Fch=942.20 (Fvco=3768.80, Nint=072, Nfrac=062) +Fch=942.30 (Fvco=3769.20, Nint=072, Nfrac=063) +Fch=942.40 (Fvco=3769.60, Nint=072, Nfrac=064) +Fch=942.50 (Fvco=3770.00, Nint=072, Nfrac=065) +Fch=942.60 (Fvco=3770.40, Nint=072, Nfrac=066) +Fch=942.70 (Fvco=3770.80, Nint=072, Nfrac=067) +Fch=942.80 (Fvco=3771.20, Nint=072, Nfrac=068) +Fch=942.90 (Fvco=3771.60, Nint=072, Nfrac=069) +Fch=943.00 (Fvco=3772.00, Nint=072, Nfrac=070) +Fch=943.10 (Fvco=3772.40, Nint=072, Nfrac=071) +Fch=943.20 (Fvco=3772.80, Nint=072, Nfrac=072) +Fch=943.30 (Fvco=3773.20, Nint=072, Nfrac=073) +Fch=943.40 (Fvco=3773.60, Nint=072, Nfrac=074) +Fch=943.50 (Fvco=3774.00, Nint=072, Nfrac=075) +Fch=943.60 (Fvco=3774.40, Nint=072, Nfrac=076) +Fch=943.70 (Fvco=3774.80, Nint=072, Nfrac=077) +Fch=943.80 (Fvco=3775.20, Nint=072, Nfrac=078) +Fch=943.90 (Fvco=3775.60, Nint=072, Nfrac=079) +Fch=944.00 (Fvco=3776.00, Nint=072, Nfrac=080) +Fch=944.10 (Fvco=3776.40, Nint=072, Nfrac=081) +Fch=944.20 (Fvco=3776.80, Nint=072, Nfrac=082) +Fch=944.30 (Fvco=3777.20, Nint=072, Nfrac=083) +Fch=944.40 (Fvco=3777.60, Nint=072, Nfrac=084) +Fch=944.50 (Fvco=3778.00, Nint=072, Nfrac=085) +Fch=944.60 (Fvco=3778.40, Nint=072, Nfrac=086) +Fch=944.70 (Fvco=3778.80, Nint=072, Nfrac=087) +Fch=944.80 (Fvco=3779.20, Nint=072, Nfrac=088) +Fch=944.90 (Fvco=3779.60, Nint=072, Nfrac=089) +Fch=945.00 (Fvco=3780.00, Nint=072, Nfrac=090) +Fch=945.10 (Fvco=3780.40, Nint=072, Nfrac=091) +Fch=945.20 (Fvco=3780.80, Nint=072, Nfrac=092) +Fch=945.30 (Fvco=3781.20, Nint=072, Nfrac=093) +Fch=945.40 (Fvco=3781.60, Nint=072, Nfrac=094) +Fch=945.50 (Fvco=3782.00, Nint=072, Nfrac=095) +Fch=945.60 (Fvco=3782.40, Nint=072, Nfrac=096) +Fch=945.70 (Fvco=3782.80, Nint=072, Nfrac=097) +Fch=945.80 (Fvco=3783.20, Nint=072, Nfrac=098) +Fch=945.90 (Fvco=3783.60, Nint=072, Nfrac=099) +Fch=946.00 (Fvco=3784.00, Nint=072, Nfrac=100) +Fch=946.10 (Fvco=3784.40, Nint=072, Nfrac=101) +Fch=946.20 (Fvco=3784.80, Nint=072, Nfrac=102) +Fch=946.30 (Fvco=3785.20, Nint=072, Nfrac=103) +Fch=946.40 (Fvco=3785.60, Nint=072, Nfrac=104) +Fch=946.50 (Fvco=3786.00, Nint=072, Nfrac=105) +Fch=946.60 (Fvco=3786.40, Nint=072, Nfrac=106) +Fch=946.70 (Fvco=3786.80, Nint=072, Nfrac=107) +Fch=946.80 (Fvco=3787.20, Nint=072, Nfrac=108) +Fch=946.90 (Fvco=3787.60, Nint=072, Nfrac=109) +Fch=947.00 (Fvco=3788.00, Nint=072, Nfrac=110) +Fch=947.10 (Fvco=3788.40, Nint=072, Nfrac=111) +Fch=947.20 (Fvco=3788.80, Nint=072, Nfrac=112) +Fch=947.30 (Fvco=3789.20, Nint=072, Nfrac=113) +Fch=947.40 (Fvco=3789.60, Nint=072, Nfrac=114) +Fch=947.50 (Fvco=3790.00, Nint=072, Nfrac=115) +Fch=947.60 (Fvco=3790.40, Nint=072, Nfrac=116) +Fch=947.70 (Fvco=3790.80, Nint=072, Nfrac=117) +Fch=947.80 (Fvco=3791.20, Nint=072, Nfrac=118) +Fch=947.90 (Fvco=3791.60, Nint=072, Nfrac=119) +Fch=948.00 (Fvco=3792.00, Nint=072, Nfrac=120) +Fch=948.10 (Fvco=3792.40, Nint=072, Nfrac=121) +Fch=948.20 (Fvco=3792.80, Nint=072, Nfrac=122) +Fch=948.30 (Fvco=3793.20, Nint=072, Nfrac=123) +Fch=948.40 (Fvco=3793.60, Nint=072, Nfrac=124) +Fch=948.50 (Fvco=3794.00, Nint=072, Nfrac=125) +Fch=948.60 (Fvco=3794.40, Nint=072, Nfrac=126) +Fch=948.70 (Fvco=3794.80, Nint=072, Nfrac=127) +Fch=948.80 (Fvco=3795.20, Nint=072, Nfrac=128) +Fch=948.90 (Fvco=3795.60, Nint=072, Nfrac=129) +Fch=949.00 (Fvco=3796.00, Nint=072, Nfrac=130) +Fch=949.00 (Fvco=3796.00, Nint=073, Nfrac=000) +Fch=949.10 (Fvco=3796.40, Nint=073, Nfrac=001) +Fch=949.20 (Fvco=3796.80, Nint=073, Nfrac=002) +Fch=949.30 (Fvco=3797.20, Nint=073, Nfrac=003) +Fch=949.40 (Fvco=3797.60, Nint=073, Nfrac=004) +Fch=949.50 (Fvco=3798.00, Nint=073, Nfrac=005) +Fch=949.60 (Fvco=3798.40, Nint=073, Nfrac=006) +Fch=949.70 (Fvco=3798.80, Nint=073, Nfrac=007) +Fch=949.80 (Fvco=3799.20, Nint=073, Nfrac=008) +Fch=949.90 (Fvco=3799.60, Nint=073, Nfrac=009) +Fch=950.00 (Fvco=3800.00, Nint=073, Nfrac=010) +Fch=950.10 (Fvco=3800.40, Nint=073, Nfrac=011) +Fch=950.20 (Fvco=3800.80, Nint=073, Nfrac=012) +Fch=950.30 (Fvco=3801.20, Nint=073, Nfrac=013) +Fch=950.40 (Fvco=3801.60, Nint=073, Nfrac=014) +Fch=950.50 (Fvco=3802.00, Nint=073, Nfrac=015) +Fch=950.60 (Fvco=3802.40, Nint=073, Nfrac=016) +Fch=950.70 (Fvco=3802.80, Nint=073, Nfrac=017) +Fch=950.80 (Fvco=3803.20, Nint=073, Nfrac=018) +Fch=950.90 (Fvco=3803.60, Nint=073, Nfrac=019) +Fch=951.00 (Fvco=3804.00, Nint=073, Nfrac=020) +Fch=951.10 (Fvco=3804.40, Nint=073, Nfrac=021) +Fch=951.20 (Fvco=3804.80, Nint=073, Nfrac=022) +Fch=951.30 (Fvco=3805.20, Nint=073, Nfrac=023) +Fch=951.40 (Fvco=3805.60, Nint=073, Nfrac=024) +Fch=951.50 (Fvco=3806.00, Nint=073, Nfrac=025) +Fch=951.60 (Fvco=3806.40, Nint=073, Nfrac=026) +Fch=951.70 (Fvco=3806.80, Nint=073, Nfrac=027) +Fch=951.80 (Fvco=3807.20, Nint=073, Nfrac=028) +Fch=951.90 (Fvco=3807.60, Nint=073, Nfrac=029) +Fch=952.00 (Fvco=3808.00, Nint=073, Nfrac=030) +Fch=952.10 (Fvco=3808.40, Nint=073, Nfrac=031) +Fch=952.20 (Fvco=3808.80, Nint=073, Nfrac=032) +Fch=952.30 (Fvco=3809.20, Nint=073, Nfrac=033) +Fch=952.40 (Fvco=3809.60, Nint=073, Nfrac=034) +Fch=952.50 (Fvco=3810.00, Nint=073, Nfrac=035) +Fch=952.60 (Fvco=3810.40, Nint=073, Nfrac=036) +Fch=952.70 (Fvco=3810.80, Nint=073, Nfrac=037) +Fch=952.80 (Fvco=3811.20, Nint=073, Nfrac=038) +Fch=952.90 (Fvco=3811.60, Nint=073, Nfrac=039) +Fch=953.00 (Fvco=3812.00, Nint=073, Nfrac=040) +Fch=953.10 (Fvco=3812.40, Nint=073, Nfrac=041) +Fch=953.20 (Fvco=3812.80, Nint=073, Nfrac=042) +Fch=953.30 (Fvco=3813.20, Nint=073, Nfrac=043) +Fch=953.40 (Fvco=3813.60, Nint=073, Nfrac=044) +Fch=953.50 (Fvco=3814.00, Nint=073, Nfrac=045) +Fch=953.60 (Fvco=3814.40, Nint=073, Nfrac=046) +Fch=953.70 (Fvco=3814.80, Nint=073, Nfrac=047) +Fch=953.80 (Fvco=3815.20, Nint=073, Nfrac=048) +Fch=953.90 (Fvco=3815.60, Nint=073, Nfrac=049) +Fch=954.00 (Fvco=3816.00, Nint=073, Nfrac=050) +Fch=954.10 (Fvco=3816.40, Nint=073, Nfrac=051) +Fch=954.20 (Fvco=3816.80, Nint=073, Nfrac=052) +Fch=954.30 (Fvco=3817.20, Nint=073, Nfrac=053) +Fch=954.40 (Fvco=3817.60, Nint=073, Nfrac=054) +Fch=954.50 (Fvco=3818.00, Nint=073, Nfrac=055) +Fch=954.60 (Fvco=3818.40, Nint=073, Nfrac=056) +Fch=954.70 (Fvco=3818.80, Nint=073, Nfrac=057) +Fch=954.80 (Fvco=3819.20, Nint=073, Nfrac=058) +Fch=954.90 (Fvco=3819.60, Nint=073, Nfrac=059) +Fch=955.00 (Fvco=3820.00, Nint=073, Nfrac=060) +Fch=955.10 (Fvco=3820.40, Nint=073, Nfrac=061) +Fch=955.20 (Fvco=3820.80, Nint=073, Nfrac=062) +Fch=955.30 (Fvco=3821.20, Nint=073, Nfrac=063) +Fch=955.40 (Fvco=3821.60, Nint=073, Nfrac=064) +Fch=955.50 (Fvco=3822.00, Nint=073, Nfrac=065) +Fch=955.60 (Fvco=3822.40, Nint=073, Nfrac=066) +Fch=955.70 (Fvco=3822.80, Nint=073, Nfrac=067) +Fch=955.80 (Fvco=3823.20, Nint=073, Nfrac=068) +Fch=955.90 (Fvco=3823.60, Nint=073, Nfrac=069) +Fch=956.00 (Fvco=3824.00, Nint=073, Nfrac=070) +Fch=956.10 (Fvco=3824.40, Nint=073, Nfrac=071) +Fch=956.20 (Fvco=3824.80, Nint=073, Nfrac=072) +Fch=956.30 (Fvco=3825.20, Nint=073, Nfrac=073) +Fch=956.40 (Fvco=3825.60, Nint=073, Nfrac=074) +Fch=956.50 (Fvco=3826.00, Nint=073, Nfrac=075) +Fch=956.60 (Fvco=3826.40, Nint=073, Nfrac=076) +Fch=956.70 (Fvco=3826.80, Nint=073, Nfrac=077) +Fch=956.80 (Fvco=3827.20, Nint=073, Nfrac=078) +Fch=956.90 (Fvco=3827.60, Nint=073, Nfrac=079) +Fch=957.00 (Fvco=3828.00, Nint=073, Nfrac=080) +Fch=957.10 (Fvco=3828.40, Nint=073, Nfrac=081) +Fch=957.20 (Fvco=3828.80, Nint=073, Nfrac=082) +Fch=957.30 (Fvco=3829.20, Nint=073, Nfrac=083) +Fch=957.40 (Fvco=3829.60, Nint=073, Nfrac=084) +Fch=957.50 (Fvco=3830.00, Nint=073, Nfrac=085) +Fch=957.60 (Fvco=3830.40, Nint=073, Nfrac=086) +Fch=957.70 (Fvco=3830.80, Nint=073, Nfrac=087) +Fch=957.80 (Fvco=3831.20, Nint=073, Nfrac=088) +Fch=957.90 (Fvco=3831.60, Nint=073, Nfrac=089) +Fch=958.00 (Fvco=3832.00, Nint=073, Nfrac=090) +Fch=958.10 (Fvco=3832.40, Nint=073, Nfrac=091) +Fch=958.20 (Fvco=3832.80, Nint=073, Nfrac=092) +Fch=958.30 (Fvco=3833.20, Nint=073, Nfrac=093) +Fch=958.40 (Fvco=3833.60, Nint=073, Nfrac=094) +Fch=958.50 (Fvco=3834.00, Nint=073, Nfrac=095) +Fch=958.60 (Fvco=3834.40, Nint=073, Nfrac=096) +Fch=958.70 (Fvco=3834.80, Nint=073, Nfrac=097) +Fch=958.80 (Fvco=3835.20, Nint=073, Nfrac=098) +Fch=958.90 (Fvco=3835.60, Nint=073, Nfrac=099) +Fch=959.00 (Fvco=3836.00, Nint=073, Nfrac=100) +Fch=959.10 (Fvco=3836.40, Nint=073, Nfrac=101) +Fch=959.20 (Fvco=3836.80, Nint=073, Nfrac=102) +Fch=959.30 (Fvco=3837.20, Nint=073, Nfrac=103) +Fch=959.40 (Fvco=3837.60, Nint=073, Nfrac=104) +Fch=959.50 (Fvco=3838.00, Nint=073, Nfrac=105) +Fch=959.60 (Fvco=3838.40, Nint=073, Nfrac=106) +Fch=959.70 (Fvco=3838.80, Nint=073, Nfrac=107) +Fch=959.80 (Fvco=3839.20, Nint=073, Nfrac=108) +Fch=959.90 (Fvco=3839.60, Nint=073, Nfrac=109) +Fch=960.00 (Fvco=3840.00, Nint=073, Nfrac=110) +Fch=960.10 (Fvco=3840.40, Nint=073, Nfrac=111) +Fch=960.20 (Fvco=3840.80, Nint=073, Nfrac=112) +Fch=960.30 (Fvco=3841.20, Nint=073, Nfrac=113) +Fch=960.40 (Fvco=3841.60, Nint=073, Nfrac=114) +Fch=960.50 (Fvco=3842.00, Nint=073, Nfrac=115) +Fch=960.60 (Fvco=3842.40, Nint=073, Nfrac=116) +Fch=960.70 (Fvco=3842.80, Nint=073, Nfrac=117) +Fch=960.80 (Fvco=3843.20, Nint=073, Nfrac=118) +Fch=960.90 (Fvco=3843.60, Nint=073, Nfrac=119) +Fch=961.00 (Fvco=3844.00, Nint=073, Nfrac=120) +Fch=961.10 (Fvco=3844.40, Nint=073, Nfrac=121) +Fch=961.20 (Fvco=3844.80, Nint=073, Nfrac=122) +Fch=961.30 (Fvco=3845.20, Nint=073, Nfrac=123) +Fch=961.40 (Fvco=3845.60, Nint=073, Nfrac=124) +Fch=961.50 (Fvco=3846.00, Nint=073, Nfrac=125) +Fch=961.60 (Fvco=3846.40, Nint=073, Nfrac=126) +Fch=961.70 (Fvco=3846.80, Nint=073, Nfrac=127) +Fch=961.80 (Fvco=3847.20, Nint=073, Nfrac=128) +Fch=961.90 (Fvco=3847.60, Nint=073, Nfrac=129) +Fch=962.00 (Fvco=3848.00, Nint=073, Nfrac=130) +====================================================================== +PLL Rx High Band: +Fch=1794.00 (Fvco=3588.00, Nint=069, Nfrac=000) +Fch=1794.20 (Fvco=3588.40, Nint=069, Nfrac=001) +Fch=1794.40 (Fvco=3588.80, Nint=069, Nfrac=002) +Fch=1794.60 (Fvco=3589.20, Nint=069, Nfrac=003) +Fch=1794.80 (Fvco=3589.60, Nint=069, Nfrac=004) +Fch=1795.00 (Fvco=3590.00, Nint=069, Nfrac=005) +Fch=1795.20 (Fvco=3590.40, Nint=069, Nfrac=006) +Fch=1795.40 (Fvco=3590.80, Nint=069, Nfrac=007) +Fch=1795.60 (Fvco=3591.20, Nint=069, Nfrac=008) +Fch=1795.80 (Fvco=3591.60, Nint=069, Nfrac=009) +Fch=1796.00 (Fvco=3592.00, Nint=069, Nfrac=010) +Fch=1796.20 (Fvco=3592.40, Nint=069, Nfrac=011) +Fch=1796.40 (Fvco=3592.80, Nint=069, Nfrac=012) +Fch=1796.60 (Fvco=3593.20, Nint=069, Nfrac=013) +Fch=1796.80 (Fvco=3593.60, Nint=069, Nfrac=014) +Fch=1797.00 (Fvco=3594.00, Nint=069, Nfrac=015) +Fch=1797.20 (Fvco=3594.40, Nint=069, Nfrac=016) +Fch=1797.40 (Fvco=3594.80, Nint=069, Nfrac=017) +Fch=1797.60 (Fvco=3595.20, Nint=069, Nfrac=018) +Fch=1797.80 (Fvco=3595.60, Nint=069, Nfrac=019) +Fch=1798.00 (Fvco=3596.00, Nint=069, Nfrac=020) +Fch=1798.20 (Fvco=3596.40, Nint=069, Nfrac=021) +Fch=1798.40 (Fvco=3596.80, Nint=069, Nfrac=022) +Fch=1798.60 (Fvco=3597.20, Nint=069, Nfrac=023) +Fch=1798.80 (Fvco=3597.60, Nint=069, Nfrac=024) +Fch=1799.00 (Fvco=3598.00, Nint=069, Nfrac=025) +Fch=1799.20 (Fvco=3598.40, Nint=069, Nfrac=026) +Fch=1799.40 (Fvco=3598.80, Nint=069, Nfrac=027) +Fch=1799.60 (Fvco=3599.20, Nint=069, Nfrac=028) +Fch=1799.80 (Fvco=3599.60, Nint=069, Nfrac=029) +Fch=1800.00 (Fvco=3600.00, Nint=069, Nfrac=030) +Fch=1800.20 (Fvco=3600.40, Nint=069, Nfrac=031) +Fch=1800.40 (Fvco=3600.80, Nint=069, Nfrac=032) +Fch=1800.60 (Fvco=3601.20, Nint=069, Nfrac=033) +Fch=1800.80 (Fvco=3601.60, Nint=069, Nfrac=034) +Fch=1801.00 (Fvco=3602.00, Nint=069, Nfrac=035) +Fch=1801.20 (Fvco=3602.40, Nint=069, Nfrac=036) +Fch=1801.40 (Fvco=3602.80, Nint=069, Nfrac=037) +Fch=1801.60 (Fvco=3603.20, Nint=069, Nfrac=038) +Fch=1801.80 (Fvco=3603.60, Nint=069, Nfrac=039) +Fch=1802.00 (Fvco=3604.00, Nint=069, Nfrac=040) +Fch=1802.20 (Fvco=3604.40, Nint=069, Nfrac=041) +Fch=1802.40 (Fvco=3604.80, Nint=069, Nfrac=042) +Fch=1802.60 (Fvco=3605.20, Nint=069, Nfrac=043) +Fch=1802.80 (Fvco=3605.60, Nint=069, Nfrac=044) +Fch=1803.00 (Fvco=3606.00, Nint=069, Nfrac=045) +Fch=1803.20 (Fvco=3606.40, Nint=069, Nfrac=046) +Fch=1803.40 (Fvco=3606.80, Nint=069, Nfrac=047) +Fch=1803.60 (Fvco=3607.20, Nint=069, Nfrac=048) +Fch=1803.80 (Fvco=3607.60, Nint=069, Nfrac=049) +Fch=1804.00 (Fvco=3608.00, Nint=069, Nfrac=050) +Fch=1804.20 (Fvco=3608.40, Nint=069, Nfrac=051) +Fch=1804.40 (Fvco=3608.80, Nint=069, Nfrac=052) +Fch=1804.60 (Fvco=3609.20, Nint=069, Nfrac=053) +Fch=1804.80 (Fvco=3609.60, Nint=069, Nfrac=054) +Fch=1805.00 (Fvco=3610.00, Nint=069, Nfrac=055) +Fch=1805.20 (Fvco=3610.40, Nint=069, Nfrac=056) +Fch=1805.40 (Fvco=3610.80, Nint=069, Nfrac=057) +Fch=1805.60 (Fvco=3611.20, Nint=069, Nfrac=058) +Fch=1805.80 (Fvco=3611.60, Nint=069, Nfrac=059) +Fch=1806.00 (Fvco=3612.00, Nint=069, Nfrac=060) +Fch=1806.20 (Fvco=3612.40, Nint=069, Nfrac=061) +Fch=1806.40 (Fvco=3612.80, Nint=069, Nfrac=062) +Fch=1806.60 (Fvco=3613.20, Nint=069, Nfrac=063) +Fch=1806.80 (Fvco=3613.60, Nint=069, Nfrac=064) +Fch=1807.00 (Fvco=3614.00, Nint=069, Nfrac=065) +Fch=1807.20 (Fvco=3614.40, Nint=069, Nfrac=066) +Fch=1807.40 (Fvco=3614.80, Nint=069, Nfrac=067) +Fch=1807.60 (Fvco=3615.20, Nint=069, Nfrac=068) +Fch=1807.80 (Fvco=3615.60, Nint=069, Nfrac=069) +Fch=1808.00 (Fvco=3616.00, Nint=069, Nfrac=070) +Fch=1808.20 (Fvco=3616.40, Nint=069, Nfrac=071) +Fch=1808.40 (Fvco=3616.80, Nint=069, Nfrac=072) +Fch=1808.60 (Fvco=3617.20, Nint=069, Nfrac=073) +Fch=1808.80 (Fvco=3617.60, Nint=069, Nfrac=074) +Fch=1809.00 (Fvco=3618.00, Nint=069, Nfrac=075) +Fch=1809.20 (Fvco=3618.40, Nint=069, Nfrac=076) +Fch=1809.40 (Fvco=3618.80, Nint=069, Nfrac=077) +Fch=1809.60 (Fvco=3619.20, Nint=069, Nfrac=078) +Fch=1809.80 (Fvco=3619.60, Nint=069, Nfrac=079) +Fch=1810.00 (Fvco=3620.00, Nint=069, Nfrac=080) +Fch=1810.20 (Fvco=3620.40, Nint=069, Nfrac=081) +Fch=1810.40 (Fvco=3620.80, Nint=069, Nfrac=082) +Fch=1810.60 (Fvco=3621.20, Nint=069, Nfrac=083) +Fch=1810.80 (Fvco=3621.60, Nint=069, Nfrac=084) +Fch=1811.00 (Fvco=3622.00, Nint=069, Nfrac=085) +Fch=1811.20 (Fvco=3622.40, Nint=069, Nfrac=086) +Fch=1811.40 (Fvco=3622.80, Nint=069, Nfrac=087) +Fch=1811.60 (Fvco=3623.20, Nint=069, Nfrac=088) +Fch=1811.80 (Fvco=3623.60, Nint=069, Nfrac=089) +Fch=1812.00 (Fvco=3624.00, Nint=069, Nfrac=090) +Fch=1812.20 (Fvco=3624.40, Nint=069, Nfrac=091) +Fch=1812.40 (Fvco=3624.80, Nint=069, Nfrac=092) +Fch=1812.60 (Fvco=3625.20, Nint=069, Nfrac=093) +Fch=1812.80 (Fvco=3625.60, Nint=069, Nfrac=094) +Fch=1813.00 (Fvco=3626.00, Nint=069, Nfrac=095) +Fch=1813.20 (Fvco=3626.40, Nint=069, Nfrac=096) +Fch=1813.40 (Fvco=3626.80, Nint=069, Nfrac=097) +Fch=1813.60 (Fvco=3627.20, Nint=069, Nfrac=098) +Fch=1813.80 (Fvco=3627.60, Nint=069, Nfrac=099) +Fch=1814.00 (Fvco=3628.00, Nint=069, Nfrac=100) +Fch=1814.20 (Fvco=3628.40, Nint=069, Nfrac=101) +Fch=1814.40 (Fvco=3628.80, Nint=069, Nfrac=102) +Fch=1814.60 (Fvco=3629.20, Nint=069, Nfrac=103) +Fch=1814.80 (Fvco=3629.60, Nint=069, Nfrac=104) +Fch=1815.00 (Fvco=3630.00, Nint=069, Nfrac=105) +Fch=1815.20 (Fvco=3630.40, Nint=069, Nfrac=106) +Fch=1815.40 (Fvco=3630.80, Nint=069, Nfrac=107) +Fch=1815.60 (Fvco=3631.20, Nint=069, Nfrac=108) +Fch=1815.80 (Fvco=3631.60, Nint=069, Nfrac=109) +Fch=1816.00 (Fvco=3632.00, Nint=069, Nfrac=110) +Fch=1816.20 (Fvco=3632.40, Nint=069, Nfrac=111) +Fch=1816.40 (Fvco=3632.80, Nint=069, Nfrac=112) +Fch=1816.60 (Fvco=3633.20, Nint=069, Nfrac=113) +Fch=1816.80 (Fvco=3633.60, Nint=069, Nfrac=114) +Fch=1817.00 (Fvco=3634.00, Nint=069, Nfrac=115) +Fch=1817.20 (Fvco=3634.40, Nint=069, Nfrac=116) +Fch=1817.40 (Fvco=3634.80, Nint=069, Nfrac=117) +Fch=1817.60 (Fvco=3635.20, Nint=069, Nfrac=118) +Fch=1817.80 (Fvco=3635.60, Nint=069, Nfrac=119) +Fch=1818.00 (Fvco=3636.00, Nint=069, Nfrac=120) +Fch=1818.20 (Fvco=3636.40, Nint=069, Nfrac=121) +Fch=1818.40 (Fvco=3636.80, Nint=069, Nfrac=122) +Fch=1818.60 (Fvco=3637.20, Nint=069, Nfrac=123) +Fch=1818.80 (Fvco=3637.60, Nint=069, Nfrac=124) +Fch=1819.00 (Fvco=3638.00, Nint=069, Nfrac=125) +Fch=1819.20 (Fvco=3638.40, Nint=069, Nfrac=126) +Fch=1819.40 (Fvco=3638.80, Nint=069, Nfrac=127) +Fch=1819.60 (Fvco=3639.20, Nint=069, Nfrac=128) +Fch=1819.80 (Fvco=3639.60, Nint=069, Nfrac=129) +Fch=1820.00 (Fvco=3640.00, Nint=069, Nfrac=130) +Fch=1820.00 (Fvco=3640.00, Nint=070, Nfrac=000) +Fch=1820.20 (Fvco=3640.40, Nint=070, Nfrac=001) +Fch=1820.40 (Fvco=3640.80, Nint=070, Nfrac=002) +Fch=1820.60 (Fvco=3641.20, Nint=070, Nfrac=003) +Fch=1820.80 (Fvco=3641.60, Nint=070, Nfrac=004) +Fch=1821.00 (Fvco=3642.00, Nint=070, Nfrac=005) +Fch=1821.20 (Fvco=3642.40, Nint=070, Nfrac=006) +Fch=1821.40 (Fvco=3642.80, Nint=070, Nfrac=007) +Fch=1821.60 (Fvco=3643.20, Nint=070, Nfrac=008) +Fch=1821.80 (Fvco=3643.60, Nint=070, Nfrac=009) +Fch=1822.00 (Fvco=3644.00, Nint=070, Nfrac=010) +Fch=1822.20 (Fvco=3644.40, Nint=070, Nfrac=011) +Fch=1822.40 (Fvco=3644.80, Nint=070, Nfrac=012) +Fch=1822.60 (Fvco=3645.20, Nint=070, Nfrac=013) +Fch=1822.80 (Fvco=3645.60, Nint=070, Nfrac=014) +Fch=1823.00 (Fvco=3646.00, Nint=070, Nfrac=015) +Fch=1823.20 (Fvco=3646.40, Nint=070, Nfrac=016) +Fch=1823.40 (Fvco=3646.80, Nint=070, Nfrac=017) +Fch=1823.60 (Fvco=3647.20, Nint=070, Nfrac=018) +Fch=1823.80 (Fvco=3647.60, Nint=070, Nfrac=019) +Fch=1824.00 (Fvco=3648.00, Nint=070, Nfrac=020) +Fch=1824.20 (Fvco=3648.40, Nint=070, Nfrac=021) +Fch=1824.40 (Fvco=3648.80, Nint=070, Nfrac=022) +Fch=1824.60 (Fvco=3649.20, Nint=070, Nfrac=023) +Fch=1824.80 (Fvco=3649.60, Nint=070, Nfrac=024) +Fch=1825.00 (Fvco=3650.00, Nint=070, Nfrac=025) +Fch=1825.20 (Fvco=3650.40, Nint=070, Nfrac=026) +Fch=1825.40 (Fvco=3650.80, Nint=070, Nfrac=027) +Fch=1825.60 (Fvco=3651.20, Nint=070, Nfrac=028) +Fch=1825.80 (Fvco=3651.60, Nint=070, Nfrac=029) +Fch=1826.00 (Fvco=3652.00, Nint=070, Nfrac=030) +Fch=1826.20 (Fvco=3652.40, Nint=070, Nfrac=031) +Fch=1826.40 (Fvco=3652.80, Nint=070, Nfrac=032) +Fch=1826.60 (Fvco=3653.20, Nint=070, Nfrac=033) +Fch=1826.80 (Fvco=3653.60, Nint=070, Nfrac=034) +Fch=1827.00 (Fvco=3654.00, Nint=070, Nfrac=035) +Fch=1827.20 (Fvco=3654.40, Nint=070, Nfrac=036) +Fch=1827.40 (Fvco=3654.80, Nint=070, Nfrac=037) +Fch=1827.60 (Fvco=3655.20, Nint=070, Nfrac=038) +Fch=1827.80 (Fvco=3655.60, Nint=070, Nfrac=039) +Fch=1828.00 (Fvco=3656.00, Nint=070, Nfrac=040) +Fch=1828.20 (Fvco=3656.40, Nint=070, Nfrac=041) +Fch=1828.40 (Fvco=3656.80, Nint=070, Nfrac=042) +Fch=1828.60 (Fvco=3657.20, Nint=070, Nfrac=043) +Fch=1828.80 (Fvco=3657.60, Nint=070, Nfrac=044) +Fch=1829.00 (Fvco=3658.00, Nint=070, Nfrac=045) +Fch=1829.20 (Fvco=3658.40, Nint=070, Nfrac=046) +Fch=1829.40 (Fvco=3658.80, Nint=070, Nfrac=047) +Fch=1829.60 (Fvco=3659.20, Nint=070, Nfrac=048) +Fch=1829.80 (Fvco=3659.60, Nint=070, Nfrac=049) +Fch=1830.00 (Fvco=3660.00, Nint=070, Nfrac=050) +Fch=1830.20 (Fvco=3660.40, Nint=070, Nfrac=051) +Fch=1830.40 (Fvco=3660.80, Nint=070, Nfrac=052) +Fch=1830.60 (Fvco=3661.20, Nint=070, Nfrac=053) +Fch=1830.80 (Fvco=3661.60, Nint=070, Nfrac=054) +Fch=1831.00 (Fvco=3662.00, Nint=070, Nfrac=055) +Fch=1831.20 (Fvco=3662.40, Nint=070, Nfrac=056) +Fch=1831.40 (Fvco=3662.80, Nint=070, Nfrac=057) +Fch=1831.60 (Fvco=3663.20, Nint=070, Nfrac=058) +Fch=1831.80 (Fvco=3663.60, Nint=070, Nfrac=059) +Fch=1832.00 (Fvco=3664.00, Nint=070, Nfrac=060) +Fch=1832.20 (Fvco=3664.40, Nint=070, Nfrac=061) +Fch=1832.40 (Fvco=3664.80, Nint=070, Nfrac=062) +Fch=1832.60 (Fvco=3665.20, Nint=070, Nfrac=063) +Fch=1832.80 (Fvco=3665.60, Nint=070, Nfrac=064) +Fch=1833.00 (Fvco=3666.00, Nint=070, Nfrac=065) +Fch=1833.20 (Fvco=3666.40, Nint=070, Nfrac=066) +Fch=1833.40 (Fvco=3666.80, Nint=070, Nfrac=067) +Fch=1833.60 (Fvco=3667.20, Nint=070, Nfrac=068) +Fch=1833.80 (Fvco=3667.60, Nint=070, Nfrac=069) +Fch=1834.00 (Fvco=3668.00, Nint=070, Nfrac=070) +Fch=1834.20 (Fvco=3668.40, Nint=070, Nfrac=071) +Fch=1834.40 (Fvco=3668.80, Nint=070, Nfrac=072) +Fch=1834.60 (Fvco=3669.20, Nint=070, Nfrac=073) +Fch=1834.80 (Fvco=3669.60, Nint=070, Nfrac=074) +Fch=1835.00 (Fvco=3670.00, Nint=070, Nfrac=075) +Fch=1835.20 (Fvco=3670.40, Nint=070, Nfrac=076) +Fch=1835.40 (Fvco=3670.80, Nint=070, Nfrac=077) +Fch=1835.60 (Fvco=3671.20, Nint=070, Nfrac=078) +Fch=1835.80 (Fvco=3671.60, Nint=070, Nfrac=079) +Fch=1836.00 (Fvco=3672.00, Nint=070, Nfrac=080) +Fch=1836.20 (Fvco=3672.40, Nint=070, Nfrac=081) +Fch=1836.40 (Fvco=3672.80, Nint=070, Nfrac=082) +Fch=1836.60 (Fvco=3673.20, Nint=070, Nfrac=083) +Fch=1836.80 (Fvco=3673.60, Nint=070, Nfrac=084) +Fch=1837.00 (Fvco=3674.00, Nint=070, Nfrac=085) +Fch=1837.20 (Fvco=3674.40, Nint=070, Nfrac=086) +Fch=1837.40 (Fvco=3674.80, Nint=070, Nfrac=087) +Fch=1837.60 (Fvco=3675.20, Nint=070, Nfrac=088) +Fch=1837.80 (Fvco=3675.60, Nint=070, Nfrac=089) +Fch=1838.00 (Fvco=3676.00, Nint=070, Nfrac=090) +Fch=1838.20 (Fvco=3676.40, Nint=070, Nfrac=091) +Fch=1838.40 (Fvco=3676.80, Nint=070, Nfrac=092) +Fch=1838.60 (Fvco=3677.20, Nint=070, Nfrac=093) +Fch=1838.80 (Fvco=3677.60, Nint=070, Nfrac=094) +Fch=1839.00 (Fvco=3678.00, Nint=070, Nfrac=095) +Fch=1839.20 (Fvco=3678.40, Nint=070, Nfrac=096) +Fch=1839.40 (Fvco=3678.80, Nint=070, Nfrac=097) +Fch=1839.60 (Fvco=3679.20, Nint=070, Nfrac=098) +Fch=1839.80 (Fvco=3679.60, Nint=070, Nfrac=099) +Fch=1840.00 (Fvco=3680.00, Nint=070, Nfrac=100) +Fch=1840.20 (Fvco=3680.40, Nint=070, Nfrac=101) +Fch=1840.40 (Fvco=3680.80, Nint=070, Nfrac=102) +Fch=1840.60 (Fvco=3681.20, Nint=070, Nfrac=103) +Fch=1840.80 (Fvco=3681.60, Nint=070, Nfrac=104) +Fch=1841.00 (Fvco=3682.00, Nint=070, Nfrac=105) +Fch=1841.20 (Fvco=3682.40, Nint=070, Nfrac=106) +Fch=1841.40 (Fvco=3682.80, Nint=070, Nfrac=107) +Fch=1841.60 (Fvco=3683.20, Nint=070, Nfrac=108) +Fch=1841.80 (Fvco=3683.60, Nint=070, Nfrac=109) +Fch=1842.00 (Fvco=3684.00, Nint=070, Nfrac=110) +Fch=1842.20 (Fvco=3684.40, Nint=070, Nfrac=111) +Fch=1842.40 (Fvco=3684.80, Nint=070, Nfrac=112) +Fch=1842.60 (Fvco=3685.20, Nint=070, Nfrac=113) +Fch=1842.80 (Fvco=3685.60, Nint=070, Nfrac=114) +Fch=1843.00 (Fvco=3686.00, Nint=070, Nfrac=115) +Fch=1843.20 (Fvco=3686.40, Nint=070, Nfrac=116) +Fch=1843.40 (Fvco=3686.80, Nint=070, Nfrac=117) +Fch=1843.60 (Fvco=3687.20, Nint=070, Nfrac=118) +Fch=1843.80 (Fvco=3687.60, Nint=070, Nfrac=119) +Fch=1844.00 (Fvco=3688.00, Nint=070, Nfrac=120) +Fch=1844.20 (Fvco=3688.40, Nint=070, Nfrac=121) +Fch=1844.40 (Fvco=3688.80, Nint=070, Nfrac=122) +Fch=1844.60 (Fvco=3689.20, Nint=070, Nfrac=123) +Fch=1844.80 (Fvco=3689.60, Nint=070, Nfrac=124) +Fch=1845.00 (Fvco=3690.00, Nint=070, Nfrac=125) +Fch=1845.20 (Fvco=3690.40, Nint=070, Nfrac=126) +Fch=1845.40 (Fvco=3690.80, Nint=070, Nfrac=127) +Fch=1845.60 (Fvco=3691.20, Nint=070, Nfrac=128) +Fch=1845.80 (Fvco=3691.60, Nint=070, Nfrac=129) +Fch=1846.00 (Fvco=3692.00, Nint=070, Nfrac=130) +Fch=1846.00 (Fvco=3692.00, Nint=071, Nfrac=000) +Fch=1846.20 (Fvco=3692.40, Nint=071, Nfrac=001) +Fch=1846.40 (Fvco=3692.80, Nint=071, Nfrac=002) +Fch=1846.60 (Fvco=3693.20, Nint=071, Nfrac=003) +Fch=1846.80 (Fvco=3693.60, Nint=071, Nfrac=004) +Fch=1847.00 (Fvco=3694.00, Nint=071, Nfrac=005) +Fch=1847.20 (Fvco=3694.40, Nint=071, Nfrac=006) +Fch=1847.40 (Fvco=3694.80, Nint=071, Nfrac=007) +Fch=1847.60 (Fvco=3695.20, Nint=071, Nfrac=008) +Fch=1847.80 (Fvco=3695.60, Nint=071, Nfrac=009) +Fch=1848.00 (Fvco=3696.00, Nint=071, Nfrac=010) +Fch=1848.20 (Fvco=3696.40, Nint=071, Nfrac=011) +Fch=1848.40 (Fvco=3696.80, Nint=071, Nfrac=012) +Fch=1848.60 (Fvco=3697.20, Nint=071, Nfrac=013) +Fch=1848.80 (Fvco=3697.60, Nint=071, Nfrac=014) +Fch=1849.00 (Fvco=3698.00, Nint=071, Nfrac=015) +Fch=1849.20 (Fvco=3698.40, Nint=071, Nfrac=016) +Fch=1849.40 (Fvco=3698.80, Nint=071, Nfrac=017) +Fch=1849.60 (Fvco=3699.20, Nint=071, Nfrac=018) +Fch=1849.80 (Fvco=3699.60, Nint=071, Nfrac=019) +Fch=1850.00 (Fvco=3700.00, Nint=071, Nfrac=020) +Fch=1850.20 (Fvco=3700.40, Nint=071, Nfrac=021) +Fch=1850.40 (Fvco=3700.80, Nint=071, Nfrac=022) +Fch=1850.60 (Fvco=3701.20, Nint=071, Nfrac=023) +Fch=1850.80 (Fvco=3701.60, Nint=071, Nfrac=024) +Fch=1851.00 (Fvco=3702.00, Nint=071, Nfrac=025) +Fch=1851.20 (Fvco=3702.40, Nint=071, Nfrac=026) +Fch=1851.40 (Fvco=3702.80, Nint=071, Nfrac=027) +Fch=1851.60 (Fvco=3703.20, Nint=071, Nfrac=028) +Fch=1851.80 (Fvco=3703.60, Nint=071, Nfrac=029) +Fch=1852.00 (Fvco=3704.00, Nint=071, Nfrac=030) +Fch=1852.20 (Fvco=3704.40, Nint=071, Nfrac=031) +Fch=1852.40 (Fvco=3704.80, Nint=071, Nfrac=032) +Fch=1852.60 (Fvco=3705.20, Nint=071, Nfrac=033) +Fch=1852.80 (Fvco=3705.60, Nint=071, Nfrac=034) +Fch=1853.00 (Fvco=3706.00, Nint=071, Nfrac=035) +Fch=1853.20 (Fvco=3706.40, Nint=071, Nfrac=036) +Fch=1853.40 (Fvco=3706.80, Nint=071, Nfrac=037) +Fch=1853.60 (Fvco=3707.20, Nint=071, Nfrac=038) +Fch=1853.80 (Fvco=3707.60, Nint=071, Nfrac=039) +Fch=1854.00 (Fvco=3708.00, Nint=071, Nfrac=040) +Fch=1854.20 (Fvco=3708.40, Nint=071, Nfrac=041) +Fch=1854.40 (Fvco=3708.80, Nint=071, Nfrac=042) +Fch=1854.60 (Fvco=3709.20, Nint=071, Nfrac=043) +Fch=1854.80 (Fvco=3709.60, Nint=071, Nfrac=044) +Fch=1855.00 (Fvco=3710.00, Nint=071, Nfrac=045) +Fch=1855.20 (Fvco=3710.40, Nint=071, Nfrac=046) +Fch=1855.40 (Fvco=3710.80, Nint=071, Nfrac=047) +Fch=1855.60 (Fvco=3711.20, Nint=071, Nfrac=048) +Fch=1855.80 (Fvco=3711.60, Nint=071, Nfrac=049) +Fch=1856.00 (Fvco=3712.00, Nint=071, Nfrac=050) +Fch=1856.20 (Fvco=3712.40, Nint=071, Nfrac=051) +Fch=1856.40 (Fvco=3712.80, Nint=071, Nfrac=052) +Fch=1856.60 (Fvco=3713.20, Nint=071, Nfrac=053) +Fch=1856.80 (Fvco=3713.60, Nint=071, Nfrac=054) +Fch=1857.00 (Fvco=3714.00, Nint=071, Nfrac=055) +Fch=1857.20 (Fvco=3714.40, Nint=071, Nfrac=056) +Fch=1857.40 (Fvco=3714.80, Nint=071, Nfrac=057) +Fch=1857.60 (Fvco=3715.20, Nint=071, Nfrac=058) +Fch=1857.80 (Fvco=3715.60, Nint=071, Nfrac=059) +Fch=1858.00 (Fvco=3716.00, Nint=071, Nfrac=060) +Fch=1858.20 (Fvco=3716.40, Nint=071, Nfrac=061) +Fch=1858.40 (Fvco=3716.80, Nint=071, Nfrac=062) +Fch=1858.60 (Fvco=3717.20, Nint=071, Nfrac=063) +Fch=1858.80 (Fvco=3717.60, Nint=071, Nfrac=064) +Fch=1859.00 (Fvco=3718.00, Nint=071, Nfrac=065) +Fch=1859.20 (Fvco=3718.40, Nint=071, Nfrac=066) +Fch=1859.40 (Fvco=3718.80, Nint=071, Nfrac=067) +Fch=1859.60 (Fvco=3719.20, Nint=071, Nfrac=068) +Fch=1859.80 (Fvco=3719.60, Nint=071, Nfrac=069) +Fch=1860.00 (Fvco=3720.00, Nint=071, Nfrac=070) +Fch=1860.20 (Fvco=3720.40, Nint=071, Nfrac=071) +Fch=1860.40 (Fvco=3720.80, Nint=071, Nfrac=072) +Fch=1860.60 (Fvco=3721.20, Nint=071, Nfrac=073) +Fch=1860.80 (Fvco=3721.60, Nint=071, Nfrac=074) +Fch=1861.00 (Fvco=3722.00, Nint=071, Nfrac=075) +Fch=1861.20 (Fvco=3722.40, Nint=071, Nfrac=076) +Fch=1861.40 (Fvco=3722.80, Nint=071, Nfrac=077) +Fch=1861.60 (Fvco=3723.20, Nint=071, Nfrac=078) +Fch=1861.80 (Fvco=3723.60, Nint=071, Nfrac=079) +Fch=1862.00 (Fvco=3724.00, Nint=071, Nfrac=080) +Fch=1862.20 (Fvco=3724.40, Nint=071, Nfrac=081) +Fch=1862.40 (Fvco=3724.80, Nint=071, Nfrac=082) +Fch=1862.60 (Fvco=3725.20, Nint=071, Nfrac=083) +Fch=1862.80 (Fvco=3725.60, Nint=071, Nfrac=084) +Fch=1863.00 (Fvco=3726.00, Nint=071, Nfrac=085) +Fch=1863.20 (Fvco=3726.40, Nint=071, Nfrac=086) +Fch=1863.40 (Fvco=3726.80, Nint=071, Nfrac=087) +Fch=1863.60 (Fvco=3727.20, Nint=071, Nfrac=088) +Fch=1863.80 (Fvco=3727.60, Nint=071, Nfrac=089) +Fch=1864.00 (Fvco=3728.00, Nint=071, Nfrac=090) +Fch=1864.20 (Fvco=3728.40, Nint=071, Nfrac=091) +Fch=1864.40 (Fvco=3728.80, Nint=071, Nfrac=092) +Fch=1864.60 (Fvco=3729.20, Nint=071, Nfrac=093) +Fch=1864.80 (Fvco=3729.60, Nint=071, Nfrac=094) +Fch=1865.00 (Fvco=3730.00, Nint=071, Nfrac=095) +Fch=1865.20 (Fvco=3730.40, Nint=071, Nfrac=096) +Fch=1865.40 (Fvco=3730.80, Nint=071, Nfrac=097) +Fch=1865.60 (Fvco=3731.20, Nint=071, Nfrac=098) +Fch=1865.80 (Fvco=3731.60, Nint=071, Nfrac=099) +Fch=1866.00 (Fvco=3732.00, Nint=071, Nfrac=100) +Fch=1866.20 (Fvco=3732.40, Nint=071, Nfrac=101) +Fch=1866.40 (Fvco=3732.80, Nint=071, Nfrac=102) +Fch=1866.60 (Fvco=3733.20, Nint=071, Nfrac=103) +Fch=1866.80 (Fvco=3733.60, Nint=071, Nfrac=104) +Fch=1867.00 (Fvco=3734.00, Nint=071, Nfrac=105) +Fch=1867.20 (Fvco=3734.40, Nint=071, Nfrac=106) +Fch=1867.40 (Fvco=3734.80, Nint=071, Nfrac=107) +Fch=1867.60 (Fvco=3735.20, Nint=071, Nfrac=108) +Fch=1867.80 (Fvco=3735.60, Nint=071, Nfrac=109) +Fch=1868.00 (Fvco=3736.00, Nint=071, Nfrac=110) +Fch=1868.20 (Fvco=3736.40, Nint=071, Nfrac=111) +Fch=1868.40 (Fvco=3736.80, Nint=071, Nfrac=112) +Fch=1868.60 (Fvco=3737.20, Nint=071, Nfrac=113) +Fch=1868.80 (Fvco=3737.60, Nint=071, Nfrac=114) +Fch=1869.00 (Fvco=3738.00, Nint=071, Nfrac=115) +Fch=1869.20 (Fvco=3738.40, Nint=071, Nfrac=116) +Fch=1869.40 (Fvco=3738.80, Nint=071, Nfrac=117) +Fch=1869.60 (Fvco=3739.20, Nint=071, Nfrac=118) +Fch=1869.80 (Fvco=3739.60, Nint=071, Nfrac=119) +Fch=1870.00 (Fvco=3740.00, Nint=071, Nfrac=120) +Fch=1870.20 (Fvco=3740.40, Nint=071, Nfrac=121) +Fch=1870.40 (Fvco=3740.80, Nint=071, Nfrac=122) +Fch=1870.60 (Fvco=3741.20, Nint=071, Nfrac=123) +Fch=1870.80 (Fvco=3741.60, Nint=071, Nfrac=124) +Fch=1871.00 (Fvco=3742.00, Nint=071, Nfrac=125) +Fch=1871.20 (Fvco=3742.40, Nint=071, Nfrac=126) +Fch=1871.40 (Fvco=3742.80, Nint=071, Nfrac=127) +Fch=1871.60 (Fvco=3743.20, Nint=071, Nfrac=128) +Fch=1871.80 (Fvco=3743.60, Nint=071, Nfrac=129) +Fch=1872.00 (Fvco=3744.00, Nint=071, Nfrac=130) +Fch=1872.00 (Fvco=3744.00, Nint=072, Nfrac=000) +Fch=1872.20 (Fvco=3744.40, Nint=072, Nfrac=001) +Fch=1872.40 (Fvco=3744.80, Nint=072, Nfrac=002) +Fch=1872.60 (Fvco=3745.20, Nint=072, Nfrac=003) +Fch=1872.80 (Fvco=3745.60, Nint=072, Nfrac=004) +Fch=1873.00 (Fvco=3746.00, Nint=072, Nfrac=005) +Fch=1873.20 (Fvco=3746.40, Nint=072, Nfrac=006) +Fch=1873.40 (Fvco=3746.80, Nint=072, Nfrac=007) +Fch=1873.60 (Fvco=3747.20, Nint=072, Nfrac=008) +Fch=1873.80 (Fvco=3747.60, Nint=072, Nfrac=009) +Fch=1874.00 (Fvco=3748.00, Nint=072, Nfrac=010) +Fch=1874.20 (Fvco=3748.40, Nint=072, Nfrac=011) +Fch=1874.40 (Fvco=3748.80, Nint=072, Nfrac=012) +Fch=1874.60 (Fvco=3749.20, Nint=072, Nfrac=013) +Fch=1874.80 (Fvco=3749.60, Nint=072, Nfrac=014) +Fch=1875.00 (Fvco=3750.00, Nint=072, Nfrac=015) +Fch=1875.20 (Fvco=3750.40, Nint=072, Nfrac=016) +Fch=1875.40 (Fvco=3750.80, Nint=072, Nfrac=017) +Fch=1875.60 (Fvco=3751.20, Nint=072, Nfrac=018) +Fch=1875.80 (Fvco=3751.60, Nint=072, Nfrac=019) +Fch=1876.00 (Fvco=3752.00, Nint=072, Nfrac=020) +Fch=1876.20 (Fvco=3752.40, Nint=072, Nfrac=021) +Fch=1876.40 (Fvco=3752.80, Nint=072, Nfrac=022) +Fch=1876.60 (Fvco=3753.20, Nint=072, Nfrac=023) +Fch=1876.80 (Fvco=3753.60, Nint=072, Nfrac=024) +Fch=1877.00 (Fvco=3754.00, Nint=072, Nfrac=025) +Fch=1877.20 (Fvco=3754.40, Nint=072, Nfrac=026) +Fch=1877.40 (Fvco=3754.80, Nint=072, Nfrac=027) +Fch=1877.60 (Fvco=3755.20, Nint=072, Nfrac=028) +Fch=1877.80 (Fvco=3755.60, Nint=072, Nfrac=029) +Fch=1878.00 (Fvco=3756.00, Nint=072, Nfrac=030) +Fch=1878.20 (Fvco=3756.40, Nint=072, Nfrac=031) +Fch=1878.40 (Fvco=3756.80, Nint=072, Nfrac=032) +Fch=1878.60 (Fvco=3757.20, Nint=072, Nfrac=033) +Fch=1878.80 (Fvco=3757.60, Nint=072, Nfrac=034) +Fch=1879.00 (Fvco=3758.00, Nint=072, Nfrac=035) +Fch=1879.20 (Fvco=3758.40, Nint=072, Nfrac=036) +Fch=1879.40 (Fvco=3758.80, Nint=072, Nfrac=037) +Fch=1879.60 (Fvco=3759.20, Nint=072, Nfrac=038) +Fch=1879.80 (Fvco=3759.60, Nint=072, Nfrac=039) +Fch=1880.00 (Fvco=3760.00, Nint=072, Nfrac=040) +Fch=1880.20 (Fvco=3760.40, Nint=072, Nfrac=041) +Fch=1880.40 (Fvco=3760.80, Nint=072, Nfrac=042) +Fch=1880.60 (Fvco=3761.20, Nint=072, Nfrac=043) +Fch=1880.80 (Fvco=3761.60, Nint=072, Nfrac=044) +Fch=1881.00 (Fvco=3762.00, Nint=072, Nfrac=045) +Fch=1881.20 (Fvco=3762.40, Nint=072, Nfrac=046) +Fch=1881.40 (Fvco=3762.80, Nint=072, Nfrac=047) +Fch=1881.60 (Fvco=3763.20, Nint=072, Nfrac=048) +Fch=1881.80 (Fvco=3763.60, Nint=072, Nfrac=049) +Fch=1882.00 (Fvco=3764.00, Nint=072, Nfrac=050) +Fch=1882.20 (Fvco=3764.40, Nint=072, Nfrac=051) +Fch=1882.40 (Fvco=3764.80, Nint=072, Nfrac=052) +Fch=1882.60 (Fvco=3765.20, Nint=072, Nfrac=053) +Fch=1882.80 (Fvco=3765.60, Nint=072, Nfrac=054) +Fch=1883.00 (Fvco=3766.00, Nint=072, Nfrac=055) +Fch=1883.20 (Fvco=3766.40, Nint=072, Nfrac=056) +Fch=1883.40 (Fvco=3766.80, Nint=072, Nfrac=057) +Fch=1883.60 (Fvco=3767.20, Nint=072, Nfrac=058) +Fch=1883.80 (Fvco=3767.60, Nint=072, Nfrac=059) +Fch=1884.00 (Fvco=3768.00, Nint=072, Nfrac=060) +Fch=1884.20 (Fvco=3768.40, Nint=072, Nfrac=061) +Fch=1884.40 (Fvco=3768.80, Nint=072, Nfrac=062) +Fch=1884.60 (Fvco=3769.20, Nint=072, Nfrac=063) +Fch=1884.80 (Fvco=3769.60, Nint=072, Nfrac=064) +Fch=1885.00 (Fvco=3770.00, Nint=072, Nfrac=065) +Fch=1885.20 (Fvco=3770.40, Nint=072, Nfrac=066) +Fch=1885.40 (Fvco=3770.80, Nint=072, Nfrac=067) +Fch=1885.60 (Fvco=3771.20, Nint=072, Nfrac=068) +Fch=1885.80 (Fvco=3771.60, Nint=072, Nfrac=069) +Fch=1886.00 (Fvco=3772.00, Nint=072, Nfrac=070) +Fch=1886.20 (Fvco=3772.40, Nint=072, Nfrac=071) +Fch=1886.40 (Fvco=3772.80, Nint=072, Nfrac=072) +Fch=1886.60 (Fvco=3773.20, Nint=072, Nfrac=073) +Fch=1886.80 (Fvco=3773.60, Nint=072, Nfrac=074) +Fch=1887.00 (Fvco=3774.00, Nint=072, Nfrac=075) +Fch=1887.20 (Fvco=3774.40, Nint=072, Nfrac=076) +Fch=1887.40 (Fvco=3774.80, Nint=072, Nfrac=077) +Fch=1887.60 (Fvco=3775.20, Nint=072, Nfrac=078) +Fch=1887.80 (Fvco=3775.60, Nint=072, Nfrac=079) +Fch=1888.00 (Fvco=3776.00, Nint=072, Nfrac=080) +Fch=1888.20 (Fvco=3776.40, Nint=072, Nfrac=081) +Fch=1888.40 (Fvco=3776.80, Nint=072, Nfrac=082) +Fch=1888.60 (Fvco=3777.20, Nint=072, Nfrac=083) +Fch=1888.80 (Fvco=3777.60, Nint=072, Nfrac=084) +Fch=1889.00 (Fvco=3778.00, Nint=072, Nfrac=085) +Fch=1889.20 (Fvco=3778.40, Nint=072, Nfrac=086) +Fch=1889.40 (Fvco=3778.80, Nint=072, Nfrac=087) +Fch=1889.60 (Fvco=3779.20, Nint=072, Nfrac=088) +Fch=1889.80 (Fvco=3779.60, Nint=072, Nfrac=089) +Fch=1890.00 (Fvco=3780.00, Nint=072, Nfrac=090) +Fch=1890.20 (Fvco=3780.40, Nint=072, Nfrac=091) +Fch=1890.40 (Fvco=3780.80, Nint=072, Nfrac=092) +Fch=1890.60 (Fvco=3781.20, Nint=072, Nfrac=093) +Fch=1890.80 (Fvco=3781.60, Nint=072, Nfrac=094) +Fch=1891.00 (Fvco=3782.00, Nint=072, Nfrac=095) +Fch=1891.20 (Fvco=3782.40, Nint=072, Nfrac=096) +Fch=1891.40 (Fvco=3782.80, Nint=072, Nfrac=097) +Fch=1891.60 (Fvco=3783.20, Nint=072, Nfrac=098) +Fch=1891.80 (Fvco=3783.60, Nint=072, Nfrac=099) +Fch=1892.00 (Fvco=3784.00, Nint=072, Nfrac=100) +Fch=1892.20 (Fvco=3784.40, Nint=072, Nfrac=101) +Fch=1892.40 (Fvco=3784.80, Nint=072, Nfrac=102) +Fch=1892.60 (Fvco=3785.20, Nint=072, Nfrac=103) +Fch=1892.80 (Fvco=3785.60, Nint=072, Nfrac=104) +Fch=1893.00 (Fvco=3786.00, Nint=072, Nfrac=105) +Fch=1893.20 (Fvco=3786.40, Nint=072, Nfrac=106) +Fch=1893.40 (Fvco=3786.80, Nint=072, Nfrac=107) +Fch=1893.60 (Fvco=3787.20, Nint=072, Nfrac=108) +Fch=1893.80 (Fvco=3787.60, Nint=072, Nfrac=109) +Fch=1894.00 (Fvco=3788.00, Nint=072, Nfrac=110) +Fch=1894.20 (Fvco=3788.40, Nint=072, Nfrac=111) +Fch=1894.40 (Fvco=3788.80, Nint=072, Nfrac=112) +Fch=1894.60 (Fvco=3789.20, Nint=072, Nfrac=113) +Fch=1894.80 (Fvco=3789.60, Nint=072, Nfrac=114) +Fch=1895.00 (Fvco=3790.00, Nint=072, Nfrac=115) +Fch=1895.20 (Fvco=3790.40, Nint=072, Nfrac=116) +Fch=1895.40 (Fvco=3790.80, Nint=072, Nfrac=117) +Fch=1895.60 (Fvco=3791.20, Nint=072, Nfrac=118) +Fch=1895.80 (Fvco=3791.60, Nint=072, Nfrac=119) +Fch=1896.00 (Fvco=3792.00, Nint=072, Nfrac=120) +Fch=1896.20 (Fvco=3792.40, Nint=072, Nfrac=121) +Fch=1896.40 (Fvco=3792.80, Nint=072, Nfrac=122) +Fch=1896.60 (Fvco=3793.20, Nint=072, Nfrac=123) +Fch=1896.80 (Fvco=3793.60, Nint=072, Nfrac=124) +Fch=1897.00 (Fvco=3794.00, Nint=072, Nfrac=125) +Fch=1897.20 (Fvco=3794.40, Nint=072, Nfrac=126) +Fch=1897.40 (Fvco=3794.80, Nint=072, Nfrac=127) +Fch=1897.60 (Fvco=3795.20, Nint=072, Nfrac=128) +Fch=1897.80 (Fvco=3795.60, Nint=072, Nfrac=129) +Fch=1898.00 (Fvco=3796.00, Nint=072, Nfrac=130) +Fch=1898.00 (Fvco=3796.00, Nint=073, Nfrac=000) +Fch=1898.20 (Fvco=3796.40, Nint=073, Nfrac=001) +Fch=1898.40 (Fvco=3796.80, Nint=073, Nfrac=002) +Fch=1898.60 (Fvco=3797.20, Nint=073, Nfrac=003) +Fch=1898.80 (Fvco=3797.60, Nint=073, Nfrac=004) +Fch=1899.00 (Fvco=3798.00, Nint=073, Nfrac=005) +Fch=1899.20 (Fvco=3798.40, Nint=073, Nfrac=006) +Fch=1899.40 (Fvco=3798.80, Nint=073, Nfrac=007) +Fch=1899.60 (Fvco=3799.20, Nint=073, Nfrac=008) +Fch=1899.80 (Fvco=3799.60, Nint=073, Nfrac=009) +Fch=1900.00 (Fvco=3800.00, Nint=073, Nfrac=010) +Fch=1900.20 (Fvco=3800.40, Nint=073, Nfrac=011) +Fch=1900.40 (Fvco=3800.80, Nint=073, Nfrac=012) +Fch=1900.60 (Fvco=3801.20, Nint=073, Nfrac=013) +Fch=1900.80 (Fvco=3801.60, Nint=073, Nfrac=014) +Fch=1901.00 (Fvco=3802.00, Nint=073, Nfrac=015) +Fch=1901.20 (Fvco=3802.40, Nint=073, Nfrac=016) +Fch=1901.40 (Fvco=3802.80, Nint=073, Nfrac=017) +Fch=1901.60 (Fvco=3803.20, Nint=073, Nfrac=018) +Fch=1901.80 (Fvco=3803.60, Nint=073, Nfrac=019) +Fch=1902.00 (Fvco=3804.00, Nint=073, Nfrac=020) +Fch=1902.20 (Fvco=3804.40, Nint=073, Nfrac=021) +Fch=1902.40 (Fvco=3804.80, Nint=073, Nfrac=022) +Fch=1902.60 (Fvco=3805.20, Nint=073, Nfrac=023) +Fch=1902.80 (Fvco=3805.60, Nint=073, Nfrac=024) +Fch=1903.00 (Fvco=3806.00, Nint=073, Nfrac=025) +Fch=1903.20 (Fvco=3806.40, Nint=073, Nfrac=026) +Fch=1903.40 (Fvco=3806.80, Nint=073, Nfrac=027) +Fch=1903.60 (Fvco=3807.20, Nint=073, Nfrac=028) +Fch=1903.80 (Fvco=3807.60, Nint=073, Nfrac=029) +Fch=1904.00 (Fvco=3808.00, Nint=073, Nfrac=030) +Fch=1904.20 (Fvco=3808.40, Nint=073, Nfrac=031) +Fch=1904.40 (Fvco=3808.80, Nint=073, Nfrac=032) +Fch=1904.60 (Fvco=3809.20, Nint=073, Nfrac=033) +Fch=1904.80 (Fvco=3809.60, Nint=073, Nfrac=034) +Fch=1905.00 (Fvco=3810.00, Nint=073, Nfrac=035) +Fch=1905.20 (Fvco=3810.40, Nint=073, Nfrac=036) +Fch=1905.40 (Fvco=3810.80, Nint=073, Nfrac=037) +Fch=1905.60 (Fvco=3811.20, Nint=073, Nfrac=038) +Fch=1905.80 (Fvco=3811.60, Nint=073, Nfrac=039) +Fch=1906.00 (Fvco=3812.00, Nint=073, Nfrac=040) +Fch=1906.20 (Fvco=3812.40, Nint=073, Nfrac=041) +Fch=1906.40 (Fvco=3812.80, Nint=073, Nfrac=042) +Fch=1906.60 (Fvco=3813.20, Nint=073, Nfrac=043) +Fch=1906.80 (Fvco=3813.60, Nint=073, Nfrac=044) +Fch=1907.00 (Fvco=3814.00, Nint=073, Nfrac=045) +Fch=1907.20 (Fvco=3814.40, Nint=073, Nfrac=046) +Fch=1907.40 (Fvco=3814.80, Nint=073, Nfrac=047) +Fch=1907.60 (Fvco=3815.20, Nint=073, Nfrac=048) +Fch=1907.80 (Fvco=3815.60, Nint=073, Nfrac=049) +Fch=1908.00 (Fvco=3816.00, Nint=073, Nfrac=050) +Fch=1908.20 (Fvco=3816.40, Nint=073, Nfrac=051) +Fch=1908.40 (Fvco=3816.80, Nint=073, Nfrac=052) +Fch=1908.60 (Fvco=3817.20, Nint=073, Nfrac=053) +Fch=1908.80 (Fvco=3817.60, Nint=073, Nfrac=054) +Fch=1909.00 (Fvco=3818.00, Nint=073, Nfrac=055) +Fch=1909.20 (Fvco=3818.40, Nint=073, Nfrac=056) +Fch=1909.40 (Fvco=3818.80, Nint=073, Nfrac=057) +Fch=1909.60 (Fvco=3819.20, Nint=073, Nfrac=058) +Fch=1909.80 (Fvco=3819.60, Nint=073, Nfrac=059) +Fch=1910.00 (Fvco=3820.00, Nint=073, Nfrac=060) +Fch=1910.20 (Fvco=3820.40, Nint=073, Nfrac=061) +Fch=1910.40 (Fvco=3820.80, Nint=073, Nfrac=062) +Fch=1910.60 (Fvco=3821.20, Nint=073, Nfrac=063) +Fch=1910.80 (Fvco=3821.60, Nint=073, Nfrac=064) +Fch=1911.00 (Fvco=3822.00, Nint=073, Nfrac=065) +Fch=1911.20 (Fvco=3822.40, Nint=073, Nfrac=066) +Fch=1911.40 (Fvco=3822.80, Nint=073, Nfrac=067) +Fch=1911.60 (Fvco=3823.20, Nint=073, Nfrac=068) +Fch=1911.80 (Fvco=3823.60, Nint=073, Nfrac=069) +Fch=1912.00 (Fvco=3824.00, Nint=073, Nfrac=070) +Fch=1912.20 (Fvco=3824.40, Nint=073, Nfrac=071) +Fch=1912.40 (Fvco=3824.80, Nint=073, Nfrac=072) +Fch=1912.60 (Fvco=3825.20, Nint=073, Nfrac=073) +Fch=1912.80 (Fvco=3825.60, Nint=073, Nfrac=074) +Fch=1913.00 (Fvco=3826.00, Nint=073, Nfrac=075) +Fch=1913.20 (Fvco=3826.40, Nint=073, Nfrac=076) +Fch=1913.40 (Fvco=3826.80, Nint=073, Nfrac=077) +Fch=1913.60 (Fvco=3827.20, Nint=073, Nfrac=078) +Fch=1913.80 (Fvco=3827.60, Nint=073, Nfrac=079) +Fch=1914.00 (Fvco=3828.00, Nint=073, Nfrac=080) +Fch=1914.20 (Fvco=3828.40, Nint=073, Nfrac=081) +Fch=1914.40 (Fvco=3828.80, Nint=073, Nfrac=082) +Fch=1914.60 (Fvco=3829.20, Nint=073, Nfrac=083) +Fch=1914.80 (Fvco=3829.60, Nint=073, Nfrac=084) +Fch=1915.00 (Fvco=3830.00, Nint=073, Nfrac=085) +Fch=1915.20 (Fvco=3830.40, Nint=073, Nfrac=086) +Fch=1915.40 (Fvco=3830.80, Nint=073, Nfrac=087) +Fch=1915.60 (Fvco=3831.20, Nint=073, Nfrac=088) +Fch=1915.80 (Fvco=3831.60, Nint=073, Nfrac=089) +Fch=1916.00 (Fvco=3832.00, Nint=073, Nfrac=090) +Fch=1916.20 (Fvco=3832.40, Nint=073, Nfrac=091) +Fch=1916.40 (Fvco=3832.80, Nint=073, Nfrac=092) +Fch=1916.60 (Fvco=3833.20, Nint=073, Nfrac=093) +Fch=1916.80 (Fvco=3833.60, Nint=073, Nfrac=094) +Fch=1917.00 (Fvco=3834.00, Nint=073, Nfrac=095) +Fch=1917.20 (Fvco=3834.40, Nint=073, Nfrac=096) +Fch=1917.40 (Fvco=3834.80, Nint=073, Nfrac=097) +Fch=1917.60 (Fvco=3835.20, Nint=073, Nfrac=098) +Fch=1917.80 (Fvco=3835.60, Nint=073, Nfrac=099) +Fch=1918.00 (Fvco=3836.00, Nint=073, Nfrac=100) +Fch=1918.20 (Fvco=3836.40, Nint=073, Nfrac=101) +Fch=1918.40 (Fvco=3836.80, Nint=073, Nfrac=102) +Fch=1918.60 (Fvco=3837.20, Nint=073, Nfrac=103) +Fch=1918.80 (Fvco=3837.60, Nint=073, Nfrac=104) +Fch=1919.00 (Fvco=3838.00, Nint=073, Nfrac=105) +Fch=1919.20 (Fvco=3838.40, Nint=073, Nfrac=106) +Fch=1919.40 (Fvco=3838.80, Nint=073, Nfrac=107) +Fch=1919.60 (Fvco=3839.20, Nint=073, Nfrac=108) +Fch=1919.80 (Fvco=3839.60, Nint=073, Nfrac=109) +Fch=1920.00 (Fvco=3840.00, Nint=073, Nfrac=110) +Fch=1920.20 (Fvco=3840.40, Nint=073, Nfrac=111) +Fch=1920.40 (Fvco=3840.80, Nint=073, Nfrac=112) +Fch=1920.60 (Fvco=3841.20, Nint=073, Nfrac=113) +Fch=1920.80 (Fvco=3841.60, Nint=073, Nfrac=114) +Fch=1921.00 (Fvco=3842.00, Nint=073, Nfrac=115) +Fch=1921.20 (Fvco=3842.40, Nint=073, Nfrac=116) +Fch=1921.40 (Fvco=3842.80, Nint=073, Nfrac=117) +Fch=1921.60 (Fvco=3843.20, Nint=073, Nfrac=118) +Fch=1921.80 (Fvco=3843.60, Nint=073, Nfrac=119) +Fch=1922.00 (Fvco=3844.00, Nint=073, Nfrac=120) +Fch=1922.20 (Fvco=3844.40, Nint=073, Nfrac=121) +Fch=1922.40 (Fvco=3844.80, Nint=073, Nfrac=122) +Fch=1922.60 (Fvco=3845.20, Nint=073, Nfrac=123) +Fch=1922.80 (Fvco=3845.60, Nint=073, Nfrac=124) +Fch=1923.00 (Fvco=3846.00, Nint=073, Nfrac=125) +Fch=1923.20 (Fvco=3846.40, Nint=073, Nfrac=126) +Fch=1923.40 (Fvco=3846.80, Nint=073, Nfrac=127) +Fch=1923.60 (Fvco=3847.20, Nint=073, Nfrac=128) +Fch=1923.80 (Fvco=3847.60, Nint=073, Nfrac=129) +Fch=1924.00 (Fvco=3848.00, Nint=073, Nfrac=130) +Fch=1924.00 (Fvco=3848.00, Nint=074, Nfrac=000) +Fch=1924.20 (Fvco=3848.40, Nint=074, Nfrac=001) +Fch=1924.40 (Fvco=3848.80, Nint=074, Nfrac=002) +Fch=1924.60 (Fvco=3849.20, Nint=074, Nfrac=003) +Fch=1924.80 (Fvco=3849.60, Nint=074, Nfrac=004) +Fch=1925.00 (Fvco=3850.00, Nint=074, Nfrac=005) +Fch=1925.20 (Fvco=3850.40, Nint=074, Nfrac=006) +Fch=1925.40 (Fvco=3850.80, Nint=074, Nfrac=007) +Fch=1925.60 (Fvco=3851.20, Nint=074, Nfrac=008) +Fch=1925.80 (Fvco=3851.60, Nint=074, Nfrac=009) +Fch=1926.00 (Fvco=3852.00, Nint=074, Nfrac=010) +Fch=1926.20 (Fvco=3852.40, Nint=074, Nfrac=011) +Fch=1926.40 (Fvco=3852.80, Nint=074, Nfrac=012) +Fch=1926.60 (Fvco=3853.20, Nint=074, Nfrac=013) +Fch=1926.80 (Fvco=3853.60, Nint=074, Nfrac=014) +Fch=1927.00 (Fvco=3854.00, Nint=074, Nfrac=015) +Fch=1927.20 (Fvco=3854.40, Nint=074, Nfrac=016) +Fch=1927.40 (Fvco=3854.80, Nint=074, Nfrac=017) +Fch=1927.60 (Fvco=3855.20, Nint=074, Nfrac=018) +Fch=1927.80 (Fvco=3855.60, Nint=074, Nfrac=019) +Fch=1928.00 (Fvco=3856.00, Nint=074, Nfrac=020) +Fch=1928.20 (Fvco=3856.40, Nint=074, Nfrac=021) +Fch=1928.40 (Fvco=3856.80, Nint=074, Nfrac=022) +Fch=1928.60 (Fvco=3857.20, Nint=074, Nfrac=023) +Fch=1928.80 (Fvco=3857.60, Nint=074, Nfrac=024) +Fch=1929.00 (Fvco=3858.00, Nint=074, Nfrac=025) +Fch=1929.20 (Fvco=3858.40, Nint=074, Nfrac=026) +Fch=1929.40 (Fvco=3858.80, Nint=074, Nfrac=027) +Fch=1929.60 (Fvco=3859.20, Nint=074, Nfrac=028) +Fch=1929.80 (Fvco=3859.60, Nint=074, Nfrac=029) +Fch=1930.00 (Fvco=3860.00, Nint=074, Nfrac=030) +Fch=1930.20 (Fvco=3860.40, Nint=074, Nfrac=031) +Fch=1930.40 (Fvco=3860.80, Nint=074, Nfrac=032) +Fch=1930.60 (Fvco=3861.20, Nint=074, Nfrac=033) +Fch=1930.80 (Fvco=3861.60, Nint=074, Nfrac=034) +Fch=1931.00 (Fvco=3862.00, Nint=074, Nfrac=035) +Fch=1931.20 (Fvco=3862.40, Nint=074, Nfrac=036) +Fch=1931.40 (Fvco=3862.80, Nint=074, Nfrac=037) +Fch=1931.60 (Fvco=3863.20, Nint=074, Nfrac=038) +Fch=1931.80 (Fvco=3863.60, Nint=074, Nfrac=039) +Fch=1932.00 (Fvco=3864.00, Nint=074, Nfrac=040) +Fch=1932.20 (Fvco=3864.40, Nint=074, Nfrac=041) +Fch=1932.40 (Fvco=3864.80, Nint=074, Nfrac=042) +Fch=1932.60 (Fvco=3865.20, Nint=074, Nfrac=043) +Fch=1932.80 (Fvco=3865.60, Nint=074, Nfrac=044) +Fch=1933.00 (Fvco=3866.00, Nint=074, Nfrac=045) +Fch=1933.20 (Fvco=3866.40, Nint=074, Nfrac=046) +Fch=1933.40 (Fvco=3866.80, Nint=074, Nfrac=047) +Fch=1933.60 (Fvco=3867.20, Nint=074, Nfrac=048) +Fch=1933.80 (Fvco=3867.60, Nint=074, Nfrac=049) +Fch=1934.00 (Fvco=3868.00, Nint=074, Nfrac=050) +Fch=1934.20 (Fvco=3868.40, Nint=074, Nfrac=051) +Fch=1934.40 (Fvco=3868.80, Nint=074, Nfrac=052) +Fch=1934.60 (Fvco=3869.20, Nint=074, Nfrac=053) +Fch=1934.80 (Fvco=3869.60, Nint=074, Nfrac=054) +Fch=1935.00 (Fvco=3870.00, Nint=074, Nfrac=055) +Fch=1935.20 (Fvco=3870.40, Nint=074, Nfrac=056) +Fch=1935.40 (Fvco=3870.80, Nint=074, Nfrac=057) +Fch=1935.60 (Fvco=3871.20, Nint=074, Nfrac=058) +Fch=1935.80 (Fvco=3871.60, Nint=074, Nfrac=059) +Fch=1936.00 (Fvco=3872.00, Nint=074, Nfrac=060) +Fch=1936.20 (Fvco=3872.40, Nint=074, Nfrac=061) +Fch=1936.40 (Fvco=3872.80, Nint=074, Nfrac=062) +Fch=1936.60 (Fvco=3873.20, Nint=074, Nfrac=063) +Fch=1936.80 (Fvco=3873.60, Nint=074, Nfrac=064) +Fch=1937.00 (Fvco=3874.00, Nint=074, Nfrac=065) +Fch=1937.20 (Fvco=3874.40, Nint=074, Nfrac=066) +Fch=1937.40 (Fvco=3874.80, Nint=074, Nfrac=067) +Fch=1937.60 (Fvco=3875.20, Nint=074, Nfrac=068) +Fch=1937.80 (Fvco=3875.60, Nint=074, Nfrac=069) +Fch=1938.00 (Fvco=3876.00, Nint=074, Nfrac=070) +Fch=1938.20 (Fvco=3876.40, Nint=074, Nfrac=071) +Fch=1938.40 (Fvco=3876.80, Nint=074, Nfrac=072) +Fch=1938.60 (Fvco=3877.20, Nint=074, Nfrac=073) +Fch=1938.80 (Fvco=3877.60, Nint=074, Nfrac=074) +Fch=1939.00 (Fvco=3878.00, Nint=074, Nfrac=075) +Fch=1939.20 (Fvco=3878.40, Nint=074, Nfrac=076) +Fch=1939.40 (Fvco=3878.80, Nint=074, Nfrac=077) +Fch=1939.60 (Fvco=3879.20, Nint=074, Nfrac=078) +Fch=1939.80 (Fvco=3879.60, Nint=074, Nfrac=079) +Fch=1940.00 (Fvco=3880.00, Nint=074, Nfrac=080) +Fch=1940.20 (Fvco=3880.40, Nint=074, Nfrac=081) +Fch=1940.40 (Fvco=3880.80, Nint=074, Nfrac=082) +Fch=1940.60 (Fvco=3881.20, Nint=074, Nfrac=083) +Fch=1940.80 (Fvco=3881.60, Nint=074, Nfrac=084) +Fch=1941.00 (Fvco=3882.00, Nint=074, Nfrac=085) +Fch=1941.20 (Fvco=3882.40, Nint=074, Nfrac=086) +Fch=1941.40 (Fvco=3882.80, Nint=074, Nfrac=087) +Fch=1941.60 (Fvco=3883.20, Nint=074, Nfrac=088) +Fch=1941.80 (Fvco=3883.60, Nint=074, Nfrac=089) +Fch=1942.00 (Fvco=3884.00, Nint=074, Nfrac=090) +Fch=1942.20 (Fvco=3884.40, Nint=074, Nfrac=091) +Fch=1942.40 (Fvco=3884.80, Nint=074, Nfrac=092) +Fch=1942.60 (Fvco=3885.20, Nint=074, Nfrac=093) +Fch=1942.80 (Fvco=3885.60, Nint=074, Nfrac=094) +Fch=1943.00 (Fvco=3886.00, Nint=074, Nfrac=095) +Fch=1943.20 (Fvco=3886.40, Nint=074, Nfrac=096) +Fch=1943.40 (Fvco=3886.80, Nint=074, Nfrac=097) +Fch=1943.60 (Fvco=3887.20, Nint=074, Nfrac=098) +Fch=1943.80 (Fvco=3887.60, Nint=074, Nfrac=099) +Fch=1944.00 (Fvco=3888.00, Nint=074, Nfrac=100) +Fch=1944.20 (Fvco=3888.40, Nint=074, Nfrac=101) +Fch=1944.40 (Fvco=3888.80, Nint=074, Nfrac=102) +Fch=1944.60 (Fvco=3889.20, Nint=074, Nfrac=103) +Fch=1944.80 (Fvco=3889.60, Nint=074, Nfrac=104) +Fch=1945.00 (Fvco=3890.00, Nint=074, Nfrac=105) +Fch=1945.20 (Fvco=3890.40, Nint=074, Nfrac=106) +Fch=1945.40 (Fvco=3890.80, Nint=074, Nfrac=107) +Fch=1945.60 (Fvco=3891.20, Nint=074, Nfrac=108) +Fch=1945.80 (Fvco=3891.60, Nint=074, Nfrac=109) +Fch=1946.00 (Fvco=3892.00, Nint=074, Nfrac=110) +Fch=1946.20 (Fvco=3892.40, Nint=074, Nfrac=111) +Fch=1946.40 (Fvco=3892.80, Nint=074, Nfrac=112) +Fch=1946.60 (Fvco=3893.20, Nint=074, Nfrac=113) +Fch=1946.80 (Fvco=3893.60, Nint=074, Nfrac=114) +Fch=1947.00 (Fvco=3894.00, Nint=074, Nfrac=115) +Fch=1947.20 (Fvco=3894.40, Nint=074, Nfrac=116) +Fch=1947.40 (Fvco=3894.80, Nint=074, Nfrac=117) +Fch=1947.60 (Fvco=3895.20, Nint=074, Nfrac=118) +Fch=1947.80 (Fvco=3895.60, Nint=074, Nfrac=119) +Fch=1948.00 (Fvco=3896.00, Nint=074, Nfrac=120) +Fch=1948.20 (Fvco=3896.40, Nint=074, Nfrac=121) +Fch=1948.40 (Fvco=3896.80, Nint=074, Nfrac=122) +Fch=1948.60 (Fvco=3897.20, Nint=074, Nfrac=123) +Fch=1948.80 (Fvco=3897.60, Nint=074, Nfrac=124) +Fch=1949.00 (Fvco=3898.00, Nint=074, Nfrac=125) +Fch=1949.20 (Fvco=3898.40, Nint=074, Nfrac=126) +Fch=1949.40 (Fvco=3898.80, Nint=074, Nfrac=127) +Fch=1949.60 (Fvco=3899.20, Nint=074, Nfrac=128) +Fch=1949.80 (Fvco=3899.60, Nint=074, Nfrac=129) +Fch=1950.00 (Fvco=3900.00, Nint=074, Nfrac=130) +Fch=1950.00 (Fvco=3900.00, Nint=075, Nfrac=000) +Fch=1950.20 (Fvco=3900.40, Nint=075, Nfrac=001) +Fch=1950.40 (Fvco=3900.80, Nint=075, Nfrac=002) +Fch=1950.60 (Fvco=3901.20, Nint=075, Nfrac=003) +Fch=1950.80 (Fvco=3901.60, Nint=075, Nfrac=004) +Fch=1951.00 (Fvco=3902.00, Nint=075, Nfrac=005) +Fch=1951.20 (Fvco=3902.40, Nint=075, Nfrac=006) +Fch=1951.40 (Fvco=3902.80, Nint=075, Nfrac=007) +Fch=1951.60 (Fvco=3903.20, Nint=075, Nfrac=008) +Fch=1951.80 (Fvco=3903.60, Nint=075, Nfrac=009) +Fch=1952.00 (Fvco=3904.00, Nint=075, Nfrac=010) +Fch=1952.20 (Fvco=3904.40, Nint=075, Nfrac=011) +Fch=1952.40 (Fvco=3904.80, Nint=075, Nfrac=012) +Fch=1952.60 (Fvco=3905.20, Nint=075, Nfrac=013) +Fch=1952.80 (Fvco=3905.60, Nint=075, Nfrac=014) +Fch=1953.00 (Fvco=3906.00, Nint=075, Nfrac=015) +Fch=1953.20 (Fvco=3906.40, Nint=075, Nfrac=016) +Fch=1953.40 (Fvco=3906.80, Nint=075, Nfrac=017) +Fch=1953.60 (Fvco=3907.20, Nint=075, Nfrac=018) +Fch=1953.80 (Fvco=3907.60, Nint=075, Nfrac=019) +Fch=1954.00 (Fvco=3908.00, Nint=075, Nfrac=020) +Fch=1954.20 (Fvco=3908.40, Nint=075, Nfrac=021) +Fch=1954.40 (Fvco=3908.80, Nint=075, Nfrac=022) +Fch=1954.60 (Fvco=3909.20, Nint=075, Nfrac=023) +Fch=1954.80 (Fvco=3909.60, Nint=075, Nfrac=024) +Fch=1955.00 (Fvco=3910.00, Nint=075, Nfrac=025) +Fch=1955.20 (Fvco=3910.40, Nint=075, Nfrac=026) +Fch=1955.40 (Fvco=3910.80, Nint=075, Nfrac=027) +Fch=1955.60 (Fvco=3911.20, Nint=075, Nfrac=028) +Fch=1955.80 (Fvco=3911.60, Nint=075, Nfrac=029) +Fch=1956.00 (Fvco=3912.00, Nint=075, Nfrac=030) +Fch=1956.20 (Fvco=3912.40, Nint=075, Nfrac=031) +Fch=1956.40 (Fvco=3912.80, Nint=075, Nfrac=032) +Fch=1956.60 (Fvco=3913.20, Nint=075, Nfrac=033) +Fch=1956.80 (Fvco=3913.60, Nint=075, Nfrac=034) +Fch=1957.00 (Fvco=3914.00, Nint=075, Nfrac=035) +Fch=1957.20 (Fvco=3914.40, Nint=075, Nfrac=036) +Fch=1957.40 (Fvco=3914.80, Nint=075, Nfrac=037) +Fch=1957.60 (Fvco=3915.20, Nint=075, Nfrac=038) +Fch=1957.80 (Fvco=3915.60, Nint=075, Nfrac=039) +Fch=1958.00 (Fvco=3916.00, Nint=075, Nfrac=040) +Fch=1958.20 (Fvco=3916.40, Nint=075, Nfrac=041) +Fch=1958.40 (Fvco=3916.80, Nint=075, Nfrac=042) +Fch=1958.60 (Fvco=3917.20, Nint=075, Nfrac=043) +Fch=1958.80 (Fvco=3917.60, Nint=075, Nfrac=044) +Fch=1959.00 (Fvco=3918.00, Nint=075, Nfrac=045) +Fch=1959.20 (Fvco=3918.40, Nint=075, Nfrac=046) +Fch=1959.40 (Fvco=3918.80, Nint=075, Nfrac=047) +Fch=1959.60 (Fvco=3919.20, Nint=075, Nfrac=048) +Fch=1959.80 (Fvco=3919.60, Nint=075, Nfrac=049) +Fch=1960.00 (Fvco=3920.00, Nint=075, Nfrac=050) +Fch=1960.20 (Fvco=3920.40, Nint=075, Nfrac=051) +Fch=1960.40 (Fvco=3920.80, Nint=075, Nfrac=052) +Fch=1960.60 (Fvco=3921.20, Nint=075, Nfrac=053) +Fch=1960.80 (Fvco=3921.60, Nint=075, Nfrac=054) +Fch=1961.00 (Fvco=3922.00, Nint=075, Nfrac=055) +Fch=1961.20 (Fvco=3922.40, Nint=075, Nfrac=056) +Fch=1961.40 (Fvco=3922.80, Nint=075, Nfrac=057) +Fch=1961.60 (Fvco=3923.20, Nint=075, Nfrac=058) +Fch=1961.80 (Fvco=3923.60, Nint=075, Nfrac=059) +Fch=1962.00 (Fvco=3924.00, Nint=075, Nfrac=060) +Fch=1962.20 (Fvco=3924.40, Nint=075, Nfrac=061) +Fch=1962.40 (Fvco=3924.80, Nint=075, Nfrac=062) +Fch=1962.60 (Fvco=3925.20, Nint=075, Nfrac=063) +Fch=1962.80 (Fvco=3925.60, Nint=075, Nfrac=064) +Fch=1963.00 (Fvco=3926.00, Nint=075, Nfrac=065) +Fch=1963.20 (Fvco=3926.40, Nint=075, Nfrac=066) +Fch=1963.40 (Fvco=3926.80, Nint=075, Nfrac=067) +Fch=1963.60 (Fvco=3927.20, Nint=075, Nfrac=068) +Fch=1963.80 (Fvco=3927.60, Nint=075, Nfrac=069) +Fch=1964.00 (Fvco=3928.00, Nint=075, Nfrac=070) +Fch=1964.20 (Fvco=3928.40, Nint=075, Nfrac=071) +Fch=1964.40 (Fvco=3928.80, Nint=075, Nfrac=072) +Fch=1964.60 (Fvco=3929.20, Nint=075, Nfrac=073) +Fch=1964.80 (Fvco=3929.60, Nint=075, Nfrac=074) +Fch=1965.00 (Fvco=3930.00, Nint=075, Nfrac=075) +Fch=1965.20 (Fvco=3930.40, Nint=075, Nfrac=076) +Fch=1965.40 (Fvco=3930.80, Nint=075, Nfrac=077) +Fch=1965.60 (Fvco=3931.20, Nint=075, Nfrac=078) +Fch=1965.80 (Fvco=3931.60, Nint=075, Nfrac=079) +Fch=1966.00 (Fvco=3932.00, Nint=075, Nfrac=080) +Fch=1966.20 (Fvco=3932.40, Nint=075, Nfrac=081) +Fch=1966.40 (Fvco=3932.80, Nint=075, Nfrac=082) +Fch=1966.60 (Fvco=3933.20, Nint=075, Nfrac=083) +Fch=1966.80 (Fvco=3933.60, Nint=075, Nfrac=084) +Fch=1967.00 (Fvco=3934.00, Nint=075, Nfrac=085) +Fch=1967.20 (Fvco=3934.40, Nint=075, Nfrac=086) +Fch=1967.40 (Fvco=3934.80, Nint=075, Nfrac=087) +Fch=1967.60 (Fvco=3935.20, Nint=075, Nfrac=088) +Fch=1967.80 (Fvco=3935.60, Nint=075, Nfrac=089) +Fch=1968.00 (Fvco=3936.00, Nint=075, Nfrac=090) +Fch=1968.20 (Fvco=3936.40, Nint=075, Nfrac=091) +Fch=1968.40 (Fvco=3936.80, Nint=075, Nfrac=092) +Fch=1968.60 (Fvco=3937.20, Nint=075, Nfrac=093) +Fch=1968.80 (Fvco=3937.60, Nint=075, Nfrac=094) +Fch=1969.00 (Fvco=3938.00, Nint=075, Nfrac=095) +Fch=1969.20 (Fvco=3938.40, Nint=075, Nfrac=096) +Fch=1969.40 (Fvco=3938.80, Nint=075, Nfrac=097) +Fch=1969.60 (Fvco=3939.20, Nint=075, Nfrac=098) +Fch=1969.80 (Fvco=3939.60, Nint=075, Nfrac=099) +Fch=1970.00 (Fvco=3940.00, Nint=075, Nfrac=100) +Fch=1970.20 (Fvco=3940.40, Nint=075, Nfrac=101) +Fch=1970.40 (Fvco=3940.80, Nint=075, Nfrac=102) +Fch=1970.60 (Fvco=3941.20, Nint=075, Nfrac=103) +Fch=1970.80 (Fvco=3941.60, Nint=075, Nfrac=104) +Fch=1971.00 (Fvco=3942.00, Nint=075, Nfrac=105) +Fch=1971.20 (Fvco=3942.40, Nint=075, Nfrac=106) +Fch=1971.40 (Fvco=3942.80, Nint=075, Nfrac=107) +Fch=1971.60 (Fvco=3943.20, Nint=075, Nfrac=108) +Fch=1971.80 (Fvco=3943.60, Nint=075, Nfrac=109) +Fch=1972.00 (Fvco=3944.00, Nint=075, Nfrac=110) +Fch=1972.20 (Fvco=3944.40, Nint=075, Nfrac=111) +Fch=1972.40 (Fvco=3944.80, Nint=075, Nfrac=112) +Fch=1972.60 (Fvco=3945.20, Nint=075, Nfrac=113) +Fch=1972.80 (Fvco=3945.60, Nint=075, Nfrac=114) +Fch=1973.00 (Fvco=3946.00, Nint=075, Nfrac=115) +Fch=1973.20 (Fvco=3946.40, Nint=075, Nfrac=116) +Fch=1973.40 (Fvco=3946.80, Nint=075, Nfrac=117) +Fch=1973.60 (Fvco=3947.20, Nint=075, Nfrac=118) +Fch=1973.80 (Fvco=3947.60, Nint=075, Nfrac=119) +Fch=1974.00 (Fvco=3948.00, Nint=075, Nfrac=120) +Fch=1974.20 (Fvco=3948.40, Nint=075, Nfrac=121) +Fch=1974.40 (Fvco=3948.80, Nint=075, Nfrac=122) +Fch=1974.60 (Fvco=3949.20, Nint=075, Nfrac=123) +Fch=1974.80 (Fvco=3949.60, Nint=075, Nfrac=124) +Fch=1975.00 (Fvco=3950.00, Nint=075, Nfrac=125) +Fch=1975.20 (Fvco=3950.40, Nint=075, Nfrac=126) +Fch=1975.40 (Fvco=3950.80, Nint=075, Nfrac=127) +Fch=1975.60 (Fvco=3951.20, Nint=075, Nfrac=128) +Fch=1975.80 (Fvco=3951.60, Nint=075, Nfrac=129) +Fch=1976.00 (Fvco=3952.00, Nint=075, Nfrac=130) +Fch=1976.00 (Fvco=3952.00, Nint=076, Nfrac=000) +Fch=1976.20 (Fvco=3952.40, Nint=076, Nfrac=001) +Fch=1976.40 (Fvco=3952.80, Nint=076, Nfrac=002) +Fch=1976.60 (Fvco=3953.20, Nint=076, Nfrac=003) +Fch=1976.80 (Fvco=3953.60, Nint=076, Nfrac=004) +Fch=1977.00 (Fvco=3954.00, Nint=076, Nfrac=005) +Fch=1977.20 (Fvco=3954.40, Nint=076, Nfrac=006) +Fch=1977.40 (Fvco=3954.80, Nint=076, Nfrac=007) +Fch=1977.60 (Fvco=3955.20, Nint=076, Nfrac=008) +Fch=1977.80 (Fvco=3955.60, Nint=076, Nfrac=009) +Fch=1978.00 (Fvco=3956.00, Nint=076, Nfrac=010) +Fch=1978.20 (Fvco=3956.40, Nint=076, Nfrac=011) +Fch=1978.40 (Fvco=3956.80, Nint=076, Nfrac=012) +Fch=1978.60 (Fvco=3957.20, Nint=076, Nfrac=013) +Fch=1978.80 (Fvco=3957.60, Nint=076, Nfrac=014) +Fch=1979.00 (Fvco=3958.00, Nint=076, Nfrac=015) +Fch=1979.20 (Fvco=3958.40, Nint=076, Nfrac=016) +Fch=1979.40 (Fvco=3958.80, Nint=076, Nfrac=017) +Fch=1979.60 (Fvco=3959.20, Nint=076, Nfrac=018) +Fch=1979.80 (Fvco=3959.60, Nint=076, Nfrac=019) +Fch=1980.00 (Fvco=3960.00, Nint=076, Nfrac=020) +Fch=1980.20 (Fvco=3960.40, Nint=076, Nfrac=021) +Fch=1980.40 (Fvco=3960.80, Nint=076, Nfrac=022) +Fch=1980.60 (Fvco=3961.20, Nint=076, Nfrac=023) +Fch=1980.80 (Fvco=3961.60, Nint=076, Nfrac=024) +Fch=1981.00 (Fvco=3962.00, Nint=076, Nfrac=025) +Fch=1981.20 (Fvco=3962.40, Nint=076, Nfrac=026) +Fch=1981.40 (Fvco=3962.80, Nint=076, Nfrac=027) +Fch=1981.60 (Fvco=3963.20, Nint=076, Nfrac=028) +Fch=1981.80 (Fvco=3963.60, Nint=076, Nfrac=029) +Fch=1982.00 (Fvco=3964.00, Nint=076, Nfrac=030) +Fch=1982.20 (Fvco=3964.40, Nint=076, Nfrac=031) +Fch=1982.40 (Fvco=3964.80, Nint=076, Nfrac=032) +Fch=1982.60 (Fvco=3965.20, Nint=076, Nfrac=033) +Fch=1982.80 (Fvco=3965.60, Nint=076, Nfrac=034) +Fch=1983.00 (Fvco=3966.00, Nint=076, Nfrac=035) +Fch=1983.20 (Fvco=3966.40, Nint=076, Nfrac=036) +Fch=1983.40 (Fvco=3966.80, Nint=076, Nfrac=037) +Fch=1983.60 (Fvco=3967.20, Nint=076, Nfrac=038) +Fch=1983.80 (Fvco=3967.60, Nint=076, Nfrac=039) +Fch=1984.00 (Fvco=3968.00, Nint=076, Nfrac=040) +Fch=1984.20 (Fvco=3968.40, Nint=076, Nfrac=041) +Fch=1984.40 (Fvco=3968.80, Nint=076, Nfrac=042) +Fch=1984.60 (Fvco=3969.20, Nint=076, Nfrac=043) +Fch=1984.80 (Fvco=3969.60, Nint=076, Nfrac=044) +Fch=1985.00 (Fvco=3970.00, Nint=076, Nfrac=045) +Fch=1985.20 (Fvco=3970.40, Nint=076, Nfrac=046) +Fch=1985.40 (Fvco=3970.80, Nint=076, Nfrac=047) +Fch=1985.60 (Fvco=3971.20, Nint=076, Nfrac=048) +Fch=1985.80 (Fvco=3971.60, Nint=076, Nfrac=049) +Fch=1986.00 (Fvco=3972.00, Nint=076, Nfrac=050) +Fch=1986.20 (Fvco=3972.40, Nint=076, Nfrac=051) +Fch=1986.40 (Fvco=3972.80, Nint=076, Nfrac=052) +Fch=1986.60 (Fvco=3973.20, Nint=076, Nfrac=053) +Fch=1986.80 (Fvco=3973.60, Nint=076, Nfrac=054) +Fch=1987.00 (Fvco=3974.00, Nint=076, Nfrac=055) +Fch=1987.20 (Fvco=3974.40, Nint=076, Nfrac=056) +Fch=1987.40 (Fvco=3974.80, Nint=076, Nfrac=057) +Fch=1987.60 (Fvco=3975.20, Nint=076, Nfrac=058) +Fch=1987.80 (Fvco=3975.60, Nint=076, Nfrac=059) +Fch=1988.00 (Fvco=3976.00, Nint=076, Nfrac=060) +Fch=1988.20 (Fvco=3976.40, Nint=076, Nfrac=061) +Fch=1988.40 (Fvco=3976.80, Nint=076, Nfrac=062) +Fch=1988.60 (Fvco=3977.20, Nint=076, Nfrac=063) +Fch=1988.80 (Fvco=3977.60, Nint=076, Nfrac=064) +Fch=1989.00 (Fvco=3978.00, Nint=076, Nfrac=065) +Fch=1989.20 (Fvco=3978.40, Nint=076, Nfrac=066) +Fch=1989.40 (Fvco=3978.80, Nint=076, Nfrac=067) +Fch=1989.60 (Fvco=3979.20, Nint=076, Nfrac=068) +Fch=1989.80 (Fvco=3979.60, Nint=076, Nfrac=069) +Fch=1990.00 (Fvco=3980.00, Nint=076, Nfrac=070) +Fch=1990.20 (Fvco=3980.40, Nint=076, Nfrac=071) +Fch=1990.40 (Fvco=3980.80, Nint=076, Nfrac=072) +Fch=1990.60 (Fvco=3981.20, Nint=076, Nfrac=073) +Fch=1990.80 (Fvco=3981.60, Nint=076, Nfrac=074) +Fch=1991.00 (Fvco=3982.00, Nint=076, Nfrac=075) +Fch=1991.20 (Fvco=3982.40, Nint=076, Nfrac=076) +Fch=1991.40 (Fvco=3982.80, Nint=076, Nfrac=077) +Fch=1991.60 (Fvco=3983.20, Nint=076, Nfrac=078) +Fch=1991.80 (Fvco=3983.60, Nint=076, Nfrac=079) +Fch=1992.00 (Fvco=3984.00, Nint=076, Nfrac=080) +Fch=1992.20 (Fvco=3984.40, Nint=076, Nfrac=081) +Fch=1992.40 (Fvco=3984.80, Nint=076, Nfrac=082) +Fch=1992.60 (Fvco=3985.20, Nint=076, Nfrac=083) +Fch=1992.80 (Fvco=3985.60, Nint=076, Nfrac=084) +Fch=1993.00 (Fvco=3986.00, Nint=076, Nfrac=085) +Fch=1993.20 (Fvco=3986.40, Nint=076, Nfrac=086) +Fch=1993.40 (Fvco=3986.80, Nint=076, Nfrac=087) +Fch=1993.60 (Fvco=3987.20, Nint=076, Nfrac=088) +Fch=1993.80 (Fvco=3987.60, Nint=076, Nfrac=089) +Fch=1994.00 (Fvco=3988.00, Nint=076, Nfrac=090) +Fch=1994.20 (Fvco=3988.40, Nint=076, Nfrac=091) +Fch=1994.40 (Fvco=3988.80, Nint=076, Nfrac=092) +Fch=1994.60 (Fvco=3989.20, Nint=076, Nfrac=093) +Fch=1994.80 (Fvco=3989.60, Nint=076, Nfrac=094) +Fch=1995.00 (Fvco=3990.00, Nint=076, Nfrac=095) +Fch=1995.20 (Fvco=3990.40, Nint=076, Nfrac=096) +Fch=1995.40 (Fvco=3990.80, Nint=076, Nfrac=097) +Fch=1995.60 (Fvco=3991.20, Nint=076, Nfrac=098) +Fch=1995.80 (Fvco=3991.60, Nint=076, Nfrac=099) +Fch=1996.00 (Fvco=3992.00, Nint=076, Nfrac=100) +Fch=1996.20 (Fvco=3992.40, Nint=076, Nfrac=101) +Fch=1996.40 (Fvco=3992.80, Nint=076, Nfrac=102) +Fch=1996.60 (Fvco=3993.20, Nint=076, Nfrac=103) +Fch=1996.80 (Fvco=3993.60, Nint=076, Nfrac=104) +Fch=1997.00 (Fvco=3994.00, Nint=076, Nfrac=105) +Fch=1997.20 (Fvco=3994.40, Nint=076, Nfrac=106) +Fch=1997.40 (Fvco=3994.80, Nint=076, Nfrac=107) +Fch=1997.60 (Fvco=3995.20, Nint=076, Nfrac=108) +Fch=1997.80 (Fvco=3995.60, Nint=076, Nfrac=109) +Fch=1998.00 (Fvco=3996.00, Nint=076, Nfrac=110) +Fch=1998.20 (Fvco=3996.40, Nint=076, Nfrac=111) +Fch=1998.40 (Fvco=3996.80, Nint=076, Nfrac=112) +Fch=1998.60 (Fvco=3997.20, Nint=076, Nfrac=113) +Fch=1998.80 (Fvco=3997.60, Nint=076, Nfrac=114) +Fch=1999.00 (Fvco=3998.00, Nint=076, Nfrac=115) +Fch=1999.20 (Fvco=3998.40, Nint=076, Nfrac=116) +Fch=1999.40 (Fvco=3998.80, Nint=076, Nfrac=117) +Fch=1999.60 (Fvco=3999.20, Nint=076, Nfrac=118) +Fch=1999.80 (Fvco=3999.60, Nint=076, Nfrac=119) +Fch=2000.00 (Fvco=4000.00, Nint=076, Nfrac=120) +Fch=2000.20 (Fvco=4000.40, Nint=076, Nfrac=121) +Fch=2000.40 (Fvco=4000.80, Nint=076, Nfrac=122) +Fch=2000.60 (Fvco=4001.20, Nint=076, Nfrac=123) +Fch=2000.80 (Fvco=4001.60, Nint=076, Nfrac=124) +Fch=2001.00 (Fvco=4002.00, Nint=076, Nfrac=125) +Fch=2001.20 (Fvco=4002.40, Nint=076, Nfrac=126) +Fch=2001.40 (Fvco=4002.80, Nint=076, Nfrac=127) +Fch=2001.60 (Fvco=4003.20, Nint=076, Nfrac=128) +Fch=2001.80 (Fvco=4003.60, Nint=076, Nfrac=129) +Fch=2002.00 (Fvco=4004.00, Nint=076, Nfrac=130) +Fch=2002.00 (Fvco=4004.00, Nint=077, Nfrac=000) +Fch=2002.20 (Fvco=4004.40, Nint=077, Nfrac=001) +Fch=2002.40 (Fvco=4004.80, Nint=077, Nfrac=002) +Fch=2002.60 (Fvco=4005.20, Nint=077, Nfrac=003) +Fch=2002.80 (Fvco=4005.60, Nint=077, Nfrac=004) +Fch=2003.00 (Fvco=4006.00, Nint=077, Nfrac=005) +Fch=2003.20 (Fvco=4006.40, Nint=077, Nfrac=006) +Fch=2003.40 (Fvco=4006.80, Nint=077, Nfrac=007) +Fch=2003.60 (Fvco=4007.20, Nint=077, Nfrac=008) +Fch=2003.80 (Fvco=4007.60, Nint=077, Nfrac=009) +Fch=2004.00 (Fvco=4008.00, Nint=077, Nfrac=010) +Fch=2004.20 (Fvco=4008.40, Nint=077, Nfrac=011) +Fch=2004.40 (Fvco=4008.80, Nint=077, Nfrac=012) +Fch=2004.60 (Fvco=4009.20, Nint=077, Nfrac=013) +Fch=2004.80 (Fvco=4009.60, Nint=077, Nfrac=014) +Fch=2005.00 (Fvco=4010.00, Nint=077, Nfrac=015) +Fch=2005.20 (Fvco=4010.40, Nint=077, Nfrac=016) +Fch=2005.40 (Fvco=4010.80, Nint=077, Nfrac=017) +Fch=2005.60 (Fvco=4011.20, Nint=077, Nfrac=018) +Fch=2005.80 (Fvco=4011.60, Nint=077, Nfrac=019) +Fch=2006.00 (Fvco=4012.00, Nint=077, Nfrac=020) +Fch=2006.20 (Fvco=4012.40, Nint=077, Nfrac=021) +Fch=2006.40 (Fvco=4012.80, Nint=077, Nfrac=022) +Fch=2006.60 (Fvco=4013.20, Nint=077, Nfrac=023) +Fch=2006.80 (Fvco=4013.60, Nint=077, Nfrac=024) +Fch=2007.00 (Fvco=4014.00, Nint=077, Nfrac=025) +Fch=2007.20 (Fvco=4014.40, Nint=077, Nfrac=026) +Fch=2007.40 (Fvco=4014.80, Nint=077, Nfrac=027) +Fch=2007.60 (Fvco=4015.20, Nint=077, Nfrac=028) +Fch=2007.80 (Fvco=4015.60, Nint=077, Nfrac=029) +Fch=2008.00 (Fvco=4016.00, Nint=077, Nfrac=030) +Fch=2008.20 (Fvco=4016.40, Nint=077, Nfrac=031) +Fch=2008.40 (Fvco=4016.80, Nint=077, Nfrac=032) +Fch=2008.60 (Fvco=4017.20, Nint=077, Nfrac=033) +Fch=2008.80 (Fvco=4017.60, Nint=077, Nfrac=034) +Fch=2009.00 (Fvco=4018.00, Nint=077, Nfrac=035) +Fch=2009.20 (Fvco=4018.40, Nint=077, Nfrac=036) +Fch=2009.40 (Fvco=4018.80, Nint=077, Nfrac=037) +Fch=2009.60 (Fvco=4019.20, Nint=077, Nfrac=038) +Fch=2009.80 (Fvco=4019.60, Nint=077, Nfrac=039) +Fch=2010.00 (Fvco=4020.00, Nint=077, Nfrac=040) +Fch=2010.20 (Fvco=4020.40, Nint=077, Nfrac=041) +Fch=2010.40 (Fvco=4020.80, Nint=077, Nfrac=042) +Fch=2010.60 (Fvco=4021.20, Nint=077, Nfrac=043) +Fch=2010.80 (Fvco=4021.60, Nint=077, Nfrac=044) +Fch=2011.00 (Fvco=4022.00, Nint=077, Nfrac=045) +Fch=2011.20 (Fvco=4022.40, Nint=077, Nfrac=046) +Fch=2011.40 (Fvco=4022.80, Nint=077, Nfrac=047) +Fch=2011.60 (Fvco=4023.20, Nint=077, Nfrac=048) +Fch=2011.80 (Fvco=4023.60, Nint=077, Nfrac=049) +Fch=2012.00 (Fvco=4024.00, Nint=077, Nfrac=050) +Fch=2012.20 (Fvco=4024.40, Nint=077, Nfrac=051) +Fch=2012.40 (Fvco=4024.80, Nint=077, Nfrac=052) +Fch=2012.60 (Fvco=4025.20, Nint=077, Nfrac=053) +Fch=2012.80 (Fvco=4025.60, Nint=077, Nfrac=054) +Fch=2013.00 (Fvco=4026.00, Nint=077, Nfrac=055) +Fch=2013.20 (Fvco=4026.40, Nint=077, Nfrac=056) +Fch=2013.40 (Fvco=4026.80, Nint=077, Nfrac=057) +Fch=2013.60 (Fvco=4027.20, Nint=077, Nfrac=058) +Fch=2013.80 (Fvco=4027.60, Nint=077, Nfrac=059) +Fch=2014.00 (Fvco=4028.00, Nint=077, Nfrac=060) +Fch=2014.20 (Fvco=4028.40, Nint=077, Nfrac=061) +Fch=2014.40 (Fvco=4028.80, Nint=077, Nfrac=062) +Fch=2014.60 (Fvco=4029.20, Nint=077, Nfrac=063) +Fch=2014.80 (Fvco=4029.60, Nint=077, Nfrac=064) +Fch=2015.00 (Fvco=4030.00, Nint=077, Nfrac=065) +Fch=2015.20 (Fvco=4030.40, Nint=077, Nfrac=066) +Fch=2015.40 (Fvco=4030.80, Nint=077, Nfrac=067) +Fch=2015.60 (Fvco=4031.20, Nint=077, Nfrac=068) +Fch=2015.80 (Fvco=4031.60, Nint=077, Nfrac=069) +Fch=2016.00 (Fvco=4032.00, Nint=077, Nfrac=070) +Fch=2016.20 (Fvco=4032.40, Nint=077, Nfrac=071) +Fch=2016.40 (Fvco=4032.80, Nint=077, Nfrac=072) +Fch=2016.60 (Fvco=4033.20, Nint=077, Nfrac=073) +Fch=2016.80 (Fvco=4033.60, Nint=077, Nfrac=074) +Fch=2017.00 (Fvco=4034.00, Nint=077, Nfrac=075) +Fch=2017.20 (Fvco=4034.40, Nint=077, Nfrac=076) +Fch=2017.40 (Fvco=4034.80, Nint=077, Nfrac=077) +Fch=2017.60 (Fvco=4035.20, Nint=077, Nfrac=078) +Fch=2017.80 (Fvco=4035.60, Nint=077, Nfrac=079) +Fch=2018.00 (Fvco=4036.00, Nint=077, Nfrac=080) +Fch=2018.20 (Fvco=4036.40, Nint=077, Nfrac=081) +Fch=2018.40 (Fvco=4036.80, Nint=077, Nfrac=082) +Fch=2018.60 (Fvco=4037.20, Nint=077, Nfrac=083) +Fch=2018.80 (Fvco=4037.60, Nint=077, Nfrac=084) +Fch=2019.00 (Fvco=4038.00, Nint=077, Nfrac=085) +Fch=2019.20 (Fvco=4038.40, Nint=077, Nfrac=086) +Fch=2019.40 (Fvco=4038.80, Nint=077, Nfrac=087) +Fch=2019.60 (Fvco=4039.20, Nint=077, Nfrac=088) +Fch=2019.80 (Fvco=4039.60, Nint=077, Nfrac=089) +Fch=2020.00 (Fvco=4040.00, Nint=077, Nfrac=090) +Fch=2020.20 (Fvco=4040.40, Nint=077, Nfrac=091) +Fch=2020.40 (Fvco=4040.80, Nint=077, Nfrac=092) +Fch=2020.60 (Fvco=4041.20, Nint=077, Nfrac=093) +Fch=2020.80 (Fvco=4041.60, Nint=077, Nfrac=094) +Fch=2021.00 (Fvco=4042.00, Nint=077, Nfrac=095) +Fch=2021.20 (Fvco=4042.40, Nint=077, Nfrac=096) +Fch=2021.40 (Fvco=4042.80, Nint=077, Nfrac=097) +Fch=2021.60 (Fvco=4043.20, Nint=077, Nfrac=098) +Fch=2021.80 (Fvco=4043.60, Nint=077, Nfrac=099) +Fch=2022.00 (Fvco=4044.00, Nint=077, Nfrac=100) +Fch=2022.20 (Fvco=4044.40, Nint=077, Nfrac=101) +Fch=2022.40 (Fvco=4044.80, Nint=077, Nfrac=102) +Fch=2022.60 (Fvco=4045.20, Nint=077, Nfrac=103) +Fch=2022.80 (Fvco=4045.60, Nint=077, Nfrac=104) +Fch=2023.00 (Fvco=4046.00, Nint=077, Nfrac=105) +Fch=2023.20 (Fvco=4046.40, Nint=077, Nfrac=106) +Fch=2023.40 (Fvco=4046.80, Nint=077, Nfrac=107) +Fch=2023.60 (Fvco=4047.20, Nint=077, Nfrac=108) +Fch=2023.80 (Fvco=4047.60, Nint=077, Nfrac=109) +Fch=2024.00 (Fvco=4048.00, Nint=077, Nfrac=110) +Fch=2024.20 (Fvco=4048.40, Nint=077, Nfrac=111) +Fch=2024.40 (Fvco=4048.80, Nint=077, Nfrac=112) +Fch=2024.60 (Fvco=4049.20, Nint=077, Nfrac=113) +Fch=2024.80 (Fvco=4049.60, Nint=077, Nfrac=114) +Fch=2025.00 (Fvco=4050.00, Nint=077, Nfrac=115) +Fch=2025.20 (Fvco=4050.40, Nint=077, Nfrac=116) +Fch=2025.40 (Fvco=4050.80, Nint=077, Nfrac=117) +Fch=2025.60 (Fvco=4051.20, Nint=077, Nfrac=118) +Fch=2025.80 (Fvco=4051.60, Nint=077, Nfrac=119) +Fch=2026.00 (Fvco=4052.00, Nint=077, Nfrac=120) +Fch=2026.20 (Fvco=4052.40, Nint=077, Nfrac=121) +Fch=2026.40 (Fvco=4052.80, Nint=077, Nfrac=122) +Fch=2026.60 (Fvco=4053.20, Nint=077, Nfrac=123) +Fch=2026.80 (Fvco=4053.60, Nint=077, Nfrac=124) +Fch=2027.00 (Fvco=4054.00, Nint=077, Nfrac=125) +Fch=2027.20 (Fvco=4054.40, Nint=077, Nfrac=126) +Fch=2027.40 (Fvco=4054.80, Nint=077, Nfrac=127) +Fch=2027.60 (Fvco=4055.20, Nint=077, Nfrac=128) +Fch=2027.80 (Fvco=4055.60, Nint=077, Nfrac=129) +Fch=2028.00 (Fvco=4056.00, Nint=077, Nfrac=130) +Fch=2028.00 (Fvco=4056.00, Nint=078, Nfrac=000) +Fch=2028.20 (Fvco=4056.40, Nint=078, Nfrac=001) +Fch=2028.40 (Fvco=4056.80, Nint=078, Nfrac=002) +Fch=2028.60 (Fvco=4057.20, Nint=078, Nfrac=003) +Fch=2028.80 (Fvco=4057.60, Nint=078, Nfrac=004) +Fch=2029.00 (Fvco=4058.00, Nint=078, Nfrac=005) +Fch=2029.20 (Fvco=4058.40, Nint=078, Nfrac=006) +Fch=2029.40 (Fvco=4058.80, Nint=078, Nfrac=007) +Fch=2029.60 (Fvco=4059.20, Nint=078, Nfrac=008) +Fch=2029.80 (Fvco=4059.60, Nint=078, Nfrac=009) +Fch=2030.00 (Fvco=4060.00, Nint=078, Nfrac=010) +Fch=2030.20 (Fvco=4060.40, Nint=078, Nfrac=011) +Fch=2030.40 (Fvco=4060.80, Nint=078, Nfrac=012) +Fch=2030.60 (Fvco=4061.20, Nint=078, Nfrac=013) +Fch=2030.80 (Fvco=4061.60, Nint=078, Nfrac=014) +Fch=2031.00 (Fvco=4062.00, Nint=078, Nfrac=015) +Fch=2031.20 (Fvco=4062.40, Nint=078, Nfrac=016) +Fch=2031.40 (Fvco=4062.80, Nint=078, Nfrac=017) +Fch=2031.60 (Fvco=4063.20, Nint=078, Nfrac=018) +Fch=2031.80 (Fvco=4063.60, Nint=078, Nfrac=019) +Fch=2032.00 (Fvco=4064.00, Nint=078, Nfrac=020) +Fch=2032.20 (Fvco=4064.40, Nint=078, Nfrac=021) +Fch=2032.40 (Fvco=4064.80, Nint=078, Nfrac=022) +Fch=2032.60 (Fvco=4065.20, Nint=078, Nfrac=023) +Fch=2032.80 (Fvco=4065.60, Nint=078, Nfrac=024) +Fch=2033.00 (Fvco=4066.00, Nint=078, Nfrac=025) +Fch=2033.20 (Fvco=4066.40, Nint=078, Nfrac=026) +Fch=2033.40 (Fvco=4066.80, Nint=078, Nfrac=027) +Fch=2033.60 (Fvco=4067.20, Nint=078, Nfrac=028) +Fch=2033.80 (Fvco=4067.60, Nint=078, Nfrac=029) +Fch=2034.00 (Fvco=4068.00, Nint=078, Nfrac=030) +Fch=2034.20 (Fvco=4068.40, Nint=078, Nfrac=031) +Fch=2034.40 (Fvco=4068.80, Nint=078, Nfrac=032) +Fch=2034.60 (Fvco=4069.20, Nint=078, Nfrac=033) +Fch=2034.80 (Fvco=4069.60, Nint=078, Nfrac=034) +Fch=2035.00 (Fvco=4070.00, Nint=078, Nfrac=035) +Fch=2035.20 (Fvco=4070.40, Nint=078, Nfrac=036) +Fch=2035.40 (Fvco=4070.80, Nint=078, Nfrac=037) +Fch=2035.60 (Fvco=4071.20, Nint=078, Nfrac=038) +Fch=2035.80 (Fvco=4071.60, Nint=078, Nfrac=039) +Fch=2036.00 (Fvco=4072.00, Nint=078, Nfrac=040) +Fch=2036.20 (Fvco=4072.40, Nint=078, Nfrac=041) +Fch=2036.40 (Fvco=4072.80, Nint=078, Nfrac=042) +Fch=2036.60 (Fvco=4073.20, Nint=078, Nfrac=043) +Fch=2036.80 (Fvco=4073.60, Nint=078, Nfrac=044) +Fch=2037.00 (Fvco=4074.00, Nint=078, Nfrac=045) +Fch=2037.20 (Fvco=4074.40, Nint=078, Nfrac=046) +Fch=2037.40 (Fvco=4074.80, Nint=078, Nfrac=047) +Fch=2037.60 (Fvco=4075.20, Nint=078, Nfrac=048) +Fch=2037.80 (Fvco=4075.60, Nint=078, Nfrac=049) +Fch=2038.00 (Fvco=4076.00, Nint=078, Nfrac=050) +Fch=2038.20 (Fvco=4076.40, Nint=078, Nfrac=051) +Fch=2038.40 (Fvco=4076.80, Nint=078, Nfrac=052) +Fch=2038.60 (Fvco=4077.20, Nint=078, Nfrac=053) +Fch=2038.80 (Fvco=4077.60, Nint=078, Nfrac=054) +Fch=2039.00 (Fvco=4078.00, Nint=078, Nfrac=055) +Fch=2039.20 (Fvco=4078.40, Nint=078, Nfrac=056) +Fch=2039.40 (Fvco=4078.80, Nint=078, Nfrac=057) +Fch=2039.60 (Fvco=4079.20, Nint=078, Nfrac=058) +Fch=2039.80 (Fvco=4079.60, Nint=078, Nfrac=059) +Fch=2040.00 (Fvco=4080.00, Nint=078, Nfrac=060) +Fch=2040.20 (Fvco=4080.40, Nint=078, Nfrac=061) +Fch=2040.40 (Fvco=4080.80, Nint=078, Nfrac=062) +Fch=2040.60 (Fvco=4081.20, Nint=078, Nfrac=063) +Fch=2040.80 (Fvco=4081.60, Nint=078, Nfrac=064) +Fch=2041.00 (Fvco=4082.00, Nint=078, Nfrac=065) +Fch=2041.20 (Fvco=4082.40, Nint=078, Nfrac=066) +Fch=2041.40 (Fvco=4082.80, Nint=078, Nfrac=067) +Fch=2041.60 (Fvco=4083.20, Nint=078, Nfrac=068) +Fch=2041.80 (Fvco=4083.60, Nint=078, Nfrac=069) +Fch=2042.00 (Fvco=4084.00, Nint=078, Nfrac=070) +Fch=2042.20 (Fvco=4084.40, Nint=078, Nfrac=071) +Fch=2042.40 (Fvco=4084.80, Nint=078, Nfrac=072) +Fch=2042.60 (Fvco=4085.20, Nint=078, Nfrac=073) +Fch=2042.80 (Fvco=4085.60, Nint=078, Nfrac=074) +Fch=2043.00 (Fvco=4086.00, Nint=078, Nfrac=075) +Fch=2043.20 (Fvco=4086.40, Nint=078, Nfrac=076) +Fch=2043.40 (Fvco=4086.80, Nint=078, Nfrac=077) +Fch=2043.60 (Fvco=4087.20, Nint=078, Nfrac=078) +Fch=2043.80 (Fvco=4087.60, Nint=078, Nfrac=079) +Fch=2044.00 (Fvco=4088.00, Nint=078, Nfrac=080) +Fch=2044.20 (Fvco=4088.40, Nint=078, Nfrac=081) +Fch=2044.40 (Fvco=4088.80, Nint=078, Nfrac=082) +Fch=2044.60 (Fvco=4089.20, Nint=078, Nfrac=083) +Fch=2044.80 (Fvco=4089.60, Nint=078, Nfrac=084) +Fch=2045.00 (Fvco=4090.00, Nint=078, Nfrac=085) +Fch=2045.20 (Fvco=4090.40, Nint=078, Nfrac=086) +Fch=2045.40 (Fvco=4090.80, Nint=078, Nfrac=087) +Fch=2045.60 (Fvco=4091.20, Nint=078, Nfrac=088) +Fch=2045.80 (Fvco=4091.60, Nint=078, Nfrac=089) +Fch=2046.00 (Fvco=4092.00, Nint=078, Nfrac=090) +Fch=2046.20 (Fvco=4092.40, Nint=078, Nfrac=091) +Fch=2046.40 (Fvco=4092.80, Nint=078, Nfrac=092) +Fch=2046.60 (Fvco=4093.20, Nint=078, Nfrac=093) +Fch=2046.80 (Fvco=4093.60, Nint=078, Nfrac=094) +Fch=2047.00 (Fvco=4094.00, Nint=078, Nfrac=095) +Fch=2047.20 (Fvco=4094.40, Nint=078, Nfrac=096) +Fch=2047.40 (Fvco=4094.80, Nint=078, Nfrac=097) +Fch=2047.60 (Fvco=4095.20, Nint=078, Nfrac=098) +Fch=2047.80 (Fvco=4095.60, Nint=078, Nfrac=099) +Fch=2048.00 (Fvco=4096.00, Nint=078, Nfrac=100) +Fch=2048.20 (Fvco=4096.40, Nint=078, Nfrac=101) +Fch=2048.40 (Fvco=4096.80, Nint=078, Nfrac=102) +Fch=2048.60 (Fvco=4097.20, Nint=078, Nfrac=103) +Fch=2048.80 (Fvco=4097.60, Nint=078, Nfrac=104) +Fch=2049.00 (Fvco=4098.00, Nint=078, Nfrac=105) +Fch=2049.20 (Fvco=4098.40, Nint=078, Nfrac=106) +Fch=2049.40 (Fvco=4098.80, Nint=078, Nfrac=107) +Fch=2049.60 (Fvco=4099.20, Nint=078, Nfrac=108) +Fch=2049.80 (Fvco=4099.60, Nint=078, Nfrac=109) +Fch=2050.00 (Fvco=4100.00, Nint=078, Nfrac=110) +Fch=2050.20 (Fvco=4100.40, Nint=078, Nfrac=111) +Fch=2050.40 (Fvco=4100.80, Nint=078, Nfrac=112) +Fch=2050.60 (Fvco=4101.20, Nint=078, Nfrac=113) +Fch=2050.80 (Fvco=4101.60, Nint=078, Nfrac=114) +Fch=2051.00 (Fvco=4102.00, Nint=078, Nfrac=115) +Fch=2051.20 (Fvco=4102.40, Nint=078, Nfrac=116) +Fch=2051.40 (Fvco=4102.80, Nint=078, Nfrac=117) +Fch=2051.60 (Fvco=4103.20, Nint=078, Nfrac=118) +Fch=2051.80 (Fvco=4103.60, Nint=078, Nfrac=119) +Fch=2052.00 (Fvco=4104.00, Nint=078, Nfrac=120) +Fch=2052.20 (Fvco=4104.40, Nint=078, Nfrac=121) +Fch=2052.40 (Fvco=4104.80, Nint=078, Nfrac=122) +Fch=2052.60 (Fvco=4105.20, Nint=078, Nfrac=123) +Fch=2052.80 (Fvco=4105.60, Nint=078, Nfrac=124) +Fch=2053.00 (Fvco=4106.00, Nint=078, Nfrac=125) +Fch=2053.20 (Fvco=4106.40, Nint=078, Nfrac=126) +Fch=2053.40 (Fvco=4106.80, Nint=078, Nfrac=127) +Fch=2053.60 (Fvco=4107.20, Nint=078, Nfrac=128) +Fch=2053.80 (Fvco=4107.60, Nint=078, Nfrac=129) +Fch=2054.00 (Fvco=4108.00, Nint=078, Nfrac=130) +Fch=2054.00 (Fvco=4108.00, Nint=079, Nfrac=000) +Fch=2054.20 (Fvco=4108.40, Nint=079, Nfrac=001) +Fch=2054.40 (Fvco=4108.80, Nint=079, Nfrac=002) +Fch=2054.60 (Fvco=4109.20, Nint=079, Nfrac=003) +Fch=2054.80 (Fvco=4109.60, Nint=079, Nfrac=004) +Fch=2055.00 (Fvco=4110.00, Nint=079, Nfrac=005) +Fch=2055.20 (Fvco=4110.40, Nint=079, Nfrac=006) +Fch=2055.40 (Fvco=4110.80, Nint=079, Nfrac=007) +Fch=2055.60 (Fvco=4111.20, Nint=079, Nfrac=008) +Fch=2055.80 (Fvco=4111.60, Nint=079, Nfrac=009) +Fch=2056.00 (Fvco=4112.00, Nint=079, Nfrac=010) +Fch=2056.20 (Fvco=4112.40, Nint=079, Nfrac=011) +Fch=2056.40 (Fvco=4112.80, Nint=079, Nfrac=012) +Fch=2056.60 (Fvco=4113.20, Nint=079, Nfrac=013) +Fch=2056.80 (Fvco=4113.60, Nint=079, Nfrac=014) +Fch=2057.00 (Fvco=4114.00, Nint=079, Nfrac=015) +Fch=2057.20 (Fvco=4114.40, Nint=079, Nfrac=016) +Fch=2057.40 (Fvco=4114.80, Nint=079, Nfrac=017) +Fch=2057.60 (Fvco=4115.20, Nint=079, Nfrac=018) +Fch=2057.80 (Fvco=4115.60, Nint=079, Nfrac=019) +Fch=2058.00 (Fvco=4116.00, Nint=079, Nfrac=020) +Fch=2058.20 (Fvco=4116.40, Nint=079, Nfrac=021) +Fch=2058.40 (Fvco=4116.80, Nint=079, Nfrac=022) +Fch=2058.60 (Fvco=4117.20, Nint=079, Nfrac=023) +Fch=2058.80 (Fvco=4117.60, Nint=079, Nfrac=024) +Fch=2059.00 (Fvco=4118.00, Nint=079, Nfrac=025) +Fch=2059.20 (Fvco=4118.40, Nint=079, Nfrac=026) +Fch=2059.40 (Fvco=4118.80, Nint=079, Nfrac=027) +Fch=2059.60 (Fvco=4119.20, Nint=079, Nfrac=028) +Fch=2059.80 (Fvco=4119.60, Nint=079, Nfrac=029) +Fch=2060.00 (Fvco=4120.00, Nint=079, Nfrac=030) +Fch=2060.20 (Fvco=4120.40, Nint=079, Nfrac=031) +Fch=2060.40 (Fvco=4120.80, Nint=079, Nfrac=032) +Fch=2060.60 (Fvco=4121.20, Nint=079, Nfrac=033) +Fch=2060.80 (Fvco=4121.60, Nint=079, Nfrac=034) +Fch=2061.00 (Fvco=4122.00, Nint=079, Nfrac=035) +Fch=2061.20 (Fvco=4122.40, Nint=079, Nfrac=036) +Fch=2061.40 (Fvco=4122.80, Nint=079, Nfrac=037) +Fch=2061.60 (Fvco=4123.20, Nint=079, Nfrac=038) +Fch=2061.80 (Fvco=4123.60, Nint=079, Nfrac=039) +Fch=2062.00 (Fvco=4124.00, Nint=079, Nfrac=040) +Fch=2062.20 (Fvco=4124.40, Nint=079, Nfrac=041) +Fch=2062.40 (Fvco=4124.80, Nint=079, Nfrac=042) +Fch=2062.60 (Fvco=4125.20, Nint=079, Nfrac=043) +Fch=2062.80 (Fvco=4125.60, Nint=079, Nfrac=044) +Fch=2063.00 (Fvco=4126.00, Nint=079, Nfrac=045) +Fch=2063.20 (Fvco=4126.40, Nint=079, Nfrac=046) +Fch=2063.40 (Fvco=4126.80, Nint=079, Nfrac=047) +Fch=2063.60 (Fvco=4127.20, Nint=079, Nfrac=048) +Fch=2063.80 (Fvco=4127.60, Nint=079, Nfrac=049) +Fch=2064.00 (Fvco=4128.00, Nint=079, Nfrac=050) +Fch=2064.20 (Fvco=4128.40, Nint=079, Nfrac=051) +Fch=2064.40 (Fvco=4128.80, Nint=079, Nfrac=052) +Fch=2064.60 (Fvco=4129.20, Nint=079, Nfrac=053) +Fch=2064.80 (Fvco=4129.60, Nint=079, Nfrac=054) +Fch=2065.00 (Fvco=4130.00, Nint=079, Nfrac=055) +Fch=2065.20 (Fvco=4130.40, Nint=079, Nfrac=056) +Fch=2065.40 (Fvco=4130.80, Nint=079, Nfrac=057) +Fch=2065.60 (Fvco=4131.20, Nint=079, Nfrac=058) +Fch=2065.80 (Fvco=4131.60, Nint=079, Nfrac=059) +Fch=2066.00 (Fvco=4132.00, Nint=079, Nfrac=060) +Fch=2066.20 (Fvco=4132.40, Nint=079, Nfrac=061) +Fch=2066.40 (Fvco=4132.80, Nint=079, Nfrac=062) +Fch=2066.60 (Fvco=4133.20, Nint=079, Nfrac=063) +Fch=2066.80 (Fvco=4133.60, Nint=079, Nfrac=064) +Fch=2067.00 (Fvco=4134.00, Nint=079, Nfrac=065) +Fch=2067.20 (Fvco=4134.40, Nint=079, Nfrac=066) +Fch=2067.40 (Fvco=4134.80, Nint=079, Nfrac=067) +Fch=2067.60 (Fvco=4135.20, Nint=079, Nfrac=068) +Fch=2067.80 (Fvco=4135.60, Nint=079, Nfrac=069) +Fch=2068.00 (Fvco=4136.00, Nint=079, Nfrac=070) +Fch=2068.20 (Fvco=4136.40, Nint=079, Nfrac=071) +Fch=2068.40 (Fvco=4136.80, Nint=079, Nfrac=072) +Fch=2068.60 (Fvco=4137.20, Nint=079, Nfrac=073) +Fch=2068.80 (Fvco=4137.60, Nint=079, Nfrac=074) +Fch=2069.00 (Fvco=4138.00, Nint=079, Nfrac=075) +Fch=2069.20 (Fvco=4138.40, Nint=079, Nfrac=076) +Fch=2069.40 (Fvco=4138.80, Nint=079, Nfrac=077) +Fch=2069.60 (Fvco=4139.20, Nint=079, Nfrac=078) +Fch=2069.80 (Fvco=4139.60, Nint=079, Nfrac=079) +Fch=2070.00 (Fvco=4140.00, Nint=079, Nfrac=080) +Fch=2070.20 (Fvco=4140.40, Nint=079, Nfrac=081) +Fch=2070.40 (Fvco=4140.80, Nint=079, Nfrac=082) +Fch=2070.60 (Fvco=4141.20, Nint=079, Nfrac=083) +Fch=2070.80 (Fvco=4141.60, Nint=079, Nfrac=084) +Fch=2071.00 (Fvco=4142.00, Nint=079, Nfrac=085) +Fch=2071.20 (Fvco=4142.40, Nint=079, Nfrac=086) +Fch=2071.40 (Fvco=4142.80, Nint=079, Nfrac=087) +Fch=2071.60 (Fvco=4143.20, Nint=079, Nfrac=088) +Fch=2071.80 (Fvco=4143.60, Nint=079, Nfrac=089) +Fch=2072.00 (Fvco=4144.00, Nint=079, Nfrac=090) +Fch=2072.20 (Fvco=4144.40, Nint=079, Nfrac=091) +Fch=2072.40 (Fvco=4144.80, Nint=079, Nfrac=092) +Fch=2072.60 (Fvco=4145.20, Nint=079, Nfrac=093) +Fch=2072.80 (Fvco=4145.60, Nint=079, Nfrac=094) +Fch=2073.00 (Fvco=4146.00, Nint=079, Nfrac=095) +Fch=2073.20 (Fvco=4146.40, Nint=079, Nfrac=096) +Fch=2073.40 (Fvco=4146.80, Nint=079, Nfrac=097) +Fch=2073.60 (Fvco=4147.20, Nint=079, Nfrac=098) +Fch=2073.80 (Fvco=4147.60, Nint=079, Nfrac=099) +Fch=2074.00 (Fvco=4148.00, Nint=079, Nfrac=100) +Fch=2074.20 (Fvco=4148.40, Nint=079, Nfrac=101) +Fch=2074.40 (Fvco=4148.80, Nint=079, Nfrac=102) +Fch=2074.60 (Fvco=4149.20, Nint=079, Nfrac=103) +Fch=2074.80 (Fvco=4149.60, Nint=079, Nfrac=104) +Fch=2075.00 (Fvco=4150.00, Nint=079, Nfrac=105) +Fch=2075.20 (Fvco=4150.40, Nint=079, Nfrac=106) +Fch=2075.40 (Fvco=4150.80, Nint=079, Nfrac=107) +Fch=2075.60 (Fvco=4151.20, Nint=079, Nfrac=108) +Fch=2075.80 (Fvco=4151.60, Nint=079, Nfrac=109) +Fch=2076.00 (Fvco=4152.00, Nint=079, Nfrac=110) +Fch=2076.20 (Fvco=4152.40, Nint=079, Nfrac=111) +Fch=2076.40 (Fvco=4152.80, Nint=079, Nfrac=112) +Fch=2076.60 (Fvco=4153.20, Nint=079, Nfrac=113) +Fch=2076.80 (Fvco=4153.60, Nint=079, Nfrac=114) +Fch=2077.00 (Fvco=4154.00, Nint=079, Nfrac=115) +Fch=2077.20 (Fvco=4154.40, Nint=079, Nfrac=116) +Fch=2077.40 (Fvco=4154.80, Nint=079, Nfrac=117) +Fch=2077.60 (Fvco=4155.20, Nint=079, Nfrac=118) +Fch=2077.80 (Fvco=4155.60, Nint=079, Nfrac=119) +Fch=2078.00 (Fvco=4156.00, Nint=079, Nfrac=120) +Fch=2078.20 (Fvco=4156.40, Nint=079, Nfrac=121) +Fch=2078.40 (Fvco=4156.80, Nint=079, Nfrac=122) +Fch=2078.60 (Fvco=4157.20, Nint=079, Nfrac=123) +Fch=2078.80 (Fvco=4157.60, Nint=079, Nfrac=124) +Fch=2079.00 (Fvco=4158.00, Nint=079, Nfrac=125) +Fch=2079.20 (Fvco=4158.40, Nint=079, Nfrac=126) +Fch=2079.40 (Fvco=4158.80, Nint=079, Nfrac=127) +Fch=2079.60 (Fvco=4159.20, Nint=079, Nfrac=128) +Fch=2079.80 (Fvco=4159.60, Nint=079, Nfrac=129) +Fch=2080.00 (Fvco=4160.00, Nint=079, Nfrac=130) +====================================================================== +PLL Tx Low Band: +Fch=819.00 (Fvco=3276.00, Nint=063, Nfrac=000) +Fch=819.10 (Fvco=3276.40, Nint=063, Nfrac=001) +Fch=819.20 (Fvco=3276.80, Nint=063, Nfrac=002) +Fch=819.30 (Fvco=3277.20, Nint=063, Nfrac=003) +Fch=819.40 (Fvco=3277.60, Nint=063, Nfrac=004) +Fch=819.50 (Fvco=3278.00, Nint=063, Nfrac=005) +Fch=819.60 (Fvco=3278.40, Nint=063, Nfrac=006) +Fch=819.70 (Fvco=3278.80, Nint=063, Nfrac=007) +Fch=819.80 (Fvco=3279.20, Nint=063, Nfrac=008) +Fch=819.90 (Fvco=3279.60, Nint=063, Nfrac=009) +Fch=820.00 (Fvco=3280.00, Nint=063, Nfrac=010) +Fch=820.10 (Fvco=3280.40, Nint=063, Nfrac=011) +Fch=820.20 (Fvco=3280.80, Nint=063, Nfrac=012) +Fch=820.30 (Fvco=3281.20, Nint=063, Nfrac=013) +Fch=820.40 (Fvco=3281.60, Nint=063, Nfrac=014) +Fch=820.50 (Fvco=3282.00, Nint=063, Nfrac=015) +Fch=820.60 (Fvco=3282.40, Nint=063, Nfrac=016) +Fch=820.70 (Fvco=3282.80, Nint=063, Nfrac=017) +Fch=820.80 (Fvco=3283.20, Nint=063, Nfrac=018) +Fch=820.90 (Fvco=3283.60, Nint=063, Nfrac=019) +Fch=821.00 (Fvco=3284.00, Nint=063, Nfrac=020) +Fch=821.10 (Fvco=3284.40, Nint=063, Nfrac=021) +Fch=821.20 (Fvco=3284.80, Nint=063, Nfrac=022) +Fch=821.30 (Fvco=3285.20, Nint=063, Nfrac=023) +Fch=821.40 (Fvco=3285.60, Nint=063, Nfrac=024) +Fch=821.50 (Fvco=3286.00, Nint=063, Nfrac=025) +Fch=821.60 (Fvco=3286.40, Nint=063, Nfrac=026) +Fch=821.70 (Fvco=3286.80, Nint=063, Nfrac=027) +Fch=821.80 (Fvco=3287.20, Nint=063, Nfrac=028) +Fch=821.90 (Fvco=3287.60, Nint=063, Nfrac=029) +Fch=822.00 (Fvco=3288.00, Nint=063, Nfrac=030) +Fch=822.10 (Fvco=3288.40, Nint=063, Nfrac=031) +Fch=822.20 (Fvco=3288.80, Nint=063, Nfrac=032) +Fch=822.30 (Fvco=3289.20, Nint=063, Nfrac=033) +Fch=822.40 (Fvco=3289.60, Nint=063, Nfrac=034) +Fch=822.50 (Fvco=3290.00, Nint=063, Nfrac=035) +Fch=822.60 (Fvco=3290.40, Nint=063, Nfrac=036) +Fch=822.70 (Fvco=3290.80, Nint=063, Nfrac=037) +Fch=822.80 (Fvco=3291.20, Nint=063, Nfrac=038) +Fch=822.90 (Fvco=3291.60, Nint=063, Nfrac=039) +Fch=823.00 (Fvco=3292.00, Nint=063, Nfrac=040) +Fch=823.10 (Fvco=3292.40, Nint=063, Nfrac=041) +Fch=823.20 (Fvco=3292.80, Nint=063, Nfrac=042) +Fch=823.30 (Fvco=3293.20, Nint=063, Nfrac=043) +Fch=823.40 (Fvco=3293.60, Nint=063, Nfrac=044) +Fch=823.50 (Fvco=3294.00, Nint=063, Nfrac=045) +Fch=823.60 (Fvco=3294.40, Nint=063, Nfrac=046) +Fch=823.70 (Fvco=3294.80, Nint=063, Nfrac=047) +Fch=823.80 (Fvco=3295.20, Nint=063, Nfrac=048) +Fch=823.90 (Fvco=3295.60, Nint=063, Nfrac=049) +Fch=824.00 (Fvco=3296.00, Nint=063, Nfrac=050) +Fch=824.10 (Fvco=3296.40, Nint=063, Nfrac=051) +Fch=824.20 (Fvco=3296.80, Nint=063, Nfrac=052) +Fch=824.30 (Fvco=3297.20, Nint=063, Nfrac=053) +Fch=824.40 (Fvco=3297.60, Nint=063, Nfrac=054) +Fch=824.50 (Fvco=3298.00, Nint=063, Nfrac=055) +Fch=824.60 (Fvco=3298.40, Nint=063, Nfrac=056) +Fch=824.70 (Fvco=3298.80, Nint=063, Nfrac=057) +Fch=824.80 (Fvco=3299.20, Nint=063, Nfrac=058) +Fch=824.90 (Fvco=3299.60, Nint=063, Nfrac=059) +Fch=825.00 (Fvco=3300.00, Nint=063, Nfrac=060) +Fch=825.10 (Fvco=3300.40, Nint=063, Nfrac=061) +Fch=825.20 (Fvco=3300.80, Nint=063, Nfrac=062) +Fch=825.30 (Fvco=3301.20, Nint=063, Nfrac=063) +Fch=825.40 (Fvco=3301.60, Nint=063, Nfrac=064) +Fch=825.50 (Fvco=3302.00, Nint=063, Nfrac=065) +Fch=825.60 (Fvco=3302.40, Nint=063, Nfrac=066) +Fch=825.70 (Fvco=3302.80, Nint=063, Nfrac=067) +Fch=825.80 (Fvco=3303.20, Nint=063, Nfrac=068) +Fch=825.90 (Fvco=3303.60, Nint=063, Nfrac=069) +Fch=826.00 (Fvco=3304.00, Nint=063, Nfrac=070) +Fch=826.10 (Fvco=3304.40, Nint=063, Nfrac=071) +Fch=826.20 (Fvco=3304.80, Nint=063, Nfrac=072) +Fch=826.30 (Fvco=3305.20, Nint=063, Nfrac=073) +Fch=826.40 (Fvco=3305.60, Nint=063, Nfrac=074) +Fch=826.50 (Fvco=3306.00, Nint=063, Nfrac=075) +Fch=826.60 (Fvco=3306.40, Nint=063, Nfrac=076) +Fch=826.70 (Fvco=3306.80, Nint=063, Nfrac=077) +Fch=826.80 (Fvco=3307.20, Nint=063, Nfrac=078) +Fch=826.90 (Fvco=3307.60, Nint=063, Nfrac=079) +Fch=827.00 (Fvco=3308.00, Nint=063, Nfrac=080) +Fch=827.10 (Fvco=3308.40, Nint=063, Nfrac=081) +Fch=827.20 (Fvco=3308.80, Nint=063, Nfrac=082) +Fch=827.30 (Fvco=3309.20, Nint=063, Nfrac=083) +Fch=827.40 (Fvco=3309.60, Nint=063, Nfrac=084) +Fch=827.50 (Fvco=3310.00, Nint=063, Nfrac=085) +Fch=827.60 (Fvco=3310.40, Nint=063, Nfrac=086) +Fch=827.70 (Fvco=3310.80, Nint=063, Nfrac=087) +Fch=827.80 (Fvco=3311.20, Nint=063, Nfrac=088) +Fch=827.90 (Fvco=3311.60, Nint=063, Nfrac=089) +Fch=828.00 (Fvco=3312.00, Nint=063, Nfrac=090) +Fch=828.10 (Fvco=3312.40, Nint=063, Nfrac=091) +Fch=828.20 (Fvco=3312.80, Nint=063, Nfrac=092) +Fch=828.30 (Fvco=3313.20, Nint=063, Nfrac=093) +Fch=828.40 (Fvco=3313.60, Nint=063, Nfrac=094) +Fch=828.50 (Fvco=3314.00, Nint=063, Nfrac=095) +Fch=828.60 (Fvco=3314.40, Nint=063, Nfrac=096) +Fch=828.70 (Fvco=3314.80, Nint=063, Nfrac=097) +Fch=828.80 (Fvco=3315.20, Nint=063, Nfrac=098) +Fch=828.90 (Fvco=3315.60, Nint=063, Nfrac=099) +Fch=829.00 (Fvco=3316.00, Nint=063, Nfrac=100) +Fch=829.10 (Fvco=3316.40, Nint=063, Nfrac=101) +Fch=829.20 (Fvco=3316.80, Nint=063, Nfrac=102) +Fch=829.30 (Fvco=3317.20, Nint=063, Nfrac=103) +Fch=829.40 (Fvco=3317.60, Nint=063, Nfrac=104) +Fch=829.50 (Fvco=3318.00, Nint=063, Nfrac=105) +Fch=829.60 (Fvco=3318.40, Nint=063, Nfrac=106) +Fch=829.70 (Fvco=3318.80, Nint=063, Nfrac=107) +Fch=829.80 (Fvco=3319.20, Nint=063, Nfrac=108) +Fch=829.90 (Fvco=3319.60, Nint=063, Nfrac=109) +Fch=830.00 (Fvco=3320.00, Nint=063, Nfrac=110) +Fch=830.10 (Fvco=3320.40, Nint=063, Nfrac=111) +Fch=830.20 (Fvco=3320.80, Nint=063, Nfrac=112) +Fch=830.30 (Fvco=3321.20, Nint=063, Nfrac=113) +Fch=830.40 (Fvco=3321.60, Nint=063, Nfrac=114) +Fch=830.50 (Fvco=3322.00, Nint=063, Nfrac=115) +Fch=830.60 (Fvco=3322.40, Nint=063, Nfrac=116) +Fch=830.70 (Fvco=3322.80, Nint=063, Nfrac=117) +Fch=830.80 (Fvco=3323.20, Nint=063, Nfrac=118) +Fch=830.90 (Fvco=3323.60, Nint=063, Nfrac=119) +Fch=831.00 (Fvco=3324.00, Nint=063, Nfrac=120) +Fch=831.10 (Fvco=3324.40, Nint=063, Nfrac=121) +Fch=831.20 (Fvco=3324.80, Nint=063, Nfrac=122) +Fch=831.30 (Fvco=3325.20, Nint=063, Nfrac=123) +Fch=831.40 (Fvco=3325.60, Nint=063, Nfrac=124) +Fch=831.50 (Fvco=3326.00, Nint=063, Nfrac=125) +Fch=831.60 (Fvco=3326.40, Nint=063, Nfrac=126) +Fch=831.70 (Fvco=3326.80, Nint=063, Nfrac=127) +Fch=831.80 (Fvco=3327.20, Nint=063, Nfrac=128) +Fch=831.90 (Fvco=3327.60, Nint=063, Nfrac=129) +Fch=832.00 (Fvco=3328.00, Nint=063, Nfrac=130) +Fch=832.00 (Fvco=3328.00, Nint=064, Nfrac=000) +Fch=832.10 (Fvco=3328.40, Nint=064, Nfrac=001) +Fch=832.20 (Fvco=3328.80, Nint=064, Nfrac=002) +Fch=832.30 (Fvco=3329.20, Nint=064, Nfrac=003) +Fch=832.40 (Fvco=3329.60, Nint=064, Nfrac=004) +Fch=832.50 (Fvco=3330.00, Nint=064, Nfrac=005) +Fch=832.60 (Fvco=3330.40, Nint=064, Nfrac=006) +Fch=832.70 (Fvco=3330.80, Nint=064, Nfrac=007) +Fch=832.80 (Fvco=3331.20, Nint=064, Nfrac=008) +Fch=832.90 (Fvco=3331.60, Nint=064, Nfrac=009) +Fch=833.00 (Fvco=3332.00, Nint=064, Nfrac=010) +Fch=833.10 (Fvco=3332.40, Nint=064, Nfrac=011) +Fch=833.20 (Fvco=3332.80, Nint=064, Nfrac=012) +Fch=833.30 (Fvco=3333.20, Nint=064, Nfrac=013) +Fch=833.40 (Fvco=3333.60, Nint=064, Nfrac=014) +Fch=833.50 (Fvco=3334.00, Nint=064, Nfrac=015) +Fch=833.60 (Fvco=3334.40, Nint=064, Nfrac=016) +Fch=833.70 (Fvco=3334.80, Nint=064, Nfrac=017) +Fch=833.80 (Fvco=3335.20, Nint=064, Nfrac=018) +Fch=833.90 (Fvco=3335.60, Nint=064, Nfrac=019) +Fch=834.00 (Fvco=3336.00, Nint=064, Nfrac=020) +Fch=834.10 (Fvco=3336.40, Nint=064, Nfrac=021) +Fch=834.20 (Fvco=3336.80, Nint=064, Nfrac=022) +Fch=834.30 (Fvco=3337.20, Nint=064, Nfrac=023) +Fch=834.40 (Fvco=3337.60, Nint=064, Nfrac=024) +Fch=834.50 (Fvco=3338.00, Nint=064, Nfrac=025) +Fch=834.60 (Fvco=3338.40, Nint=064, Nfrac=026) +Fch=834.70 (Fvco=3338.80, Nint=064, Nfrac=027) +Fch=834.80 (Fvco=3339.20, Nint=064, Nfrac=028) +Fch=834.90 (Fvco=3339.60, Nint=064, Nfrac=029) +Fch=835.00 (Fvco=3340.00, Nint=064, Nfrac=030) +Fch=835.10 (Fvco=3340.40, Nint=064, Nfrac=031) +Fch=835.20 (Fvco=3340.80, Nint=064, Nfrac=032) +Fch=835.30 (Fvco=3341.20, Nint=064, Nfrac=033) +Fch=835.40 (Fvco=3341.60, Nint=064, Nfrac=034) +Fch=835.50 (Fvco=3342.00, Nint=064, Nfrac=035) +Fch=835.60 (Fvco=3342.40, Nint=064, Nfrac=036) +Fch=835.70 (Fvco=3342.80, Nint=064, Nfrac=037) +Fch=835.80 (Fvco=3343.20, Nint=064, Nfrac=038) +Fch=835.90 (Fvco=3343.60, Nint=064, Nfrac=039) +Fch=836.00 (Fvco=3344.00, Nint=064, Nfrac=040) +Fch=836.10 (Fvco=3344.40, Nint=064, Nfrac=041) +Fch=836.20 (Fvco=3344.80, Nint=064, Nfrac=042) +Fch=836.30 (Fvco=3345.20, Nint=064, Nfrac=043) +Fch=836.40 (Fvco=3345.60, Nint=064, Nfrac=044) +Fch=836.50 (Fvco=3346.00, Nint=064, Nfrac=045) +Fch=836.60 (Fvco=3346.40, Nint=064, Nfrac=046) +Fch=836.70 (Fvco=3346.80, Nint=064, Nfrac=047) +Fch=836.80 (Fvco=3347.20, Nint=064, Nfrac=048) +Fch=836.90 (Fvco=3347.60, Nint=064, Nfrac=049) +Fch=837.00 (Fvco=3348.00, Nint=064, Nfrac=050) +Fch=837.10 (Fvco=3348.40, Nint=064, Nfrac=051) +Fch=837.20 (Fvco=3348.80, Nint=064, Nfrac=052) +Fch=837.30 (Fvco=3349.20, Nint=064, Nfrac=053) +Fch=837.40 (Fvco=3349.60, Nint=064, Nfrac=054) +Fch=837.50 (Fvco=3350.00, Nint=064, Nfrac=055) +Fch=837.60 (Fvco=3350.40, Nint=064, Nfrac=056) +Fch=837.70 (Fvco=3350.80, Nint=064, Nfrac=057) +Fch=837.80 (Fvco=3351.20, Nint=064, Nfrac=058) +Fch=837.90 (Fvco=3351.60, Nint=064, Nfrac=059) +Fch=838.00 (Fvco=3352.00, Nint=064, Nfrac=060) +Fch=838.10 (Fvco=3352.40, Nint=064, Nfrac=061) +Fch=838.20 (Fvco=3352.80, Nint=064, Nfrac=062) +Fch=838.30 (Fvco=3353.20, Nint=064, Nfrac=063) +Fch=838.40 (Fvco=3353.60, Nint=064, Nfrac=064) +Fch=838.50 (Fvco=3354.00, Nint=064, Nfrac=065) +Fch=838.60 (Fvco=3354.40, Nint=064, Nfrac=066) +Fch=838.70 (Fvco=3354.80, Nint=064, Nfrac=067) +Fch=838.80 (Fvco=3355.20, Nint=064, Nfrac=068) +Fch=838.90 (Fvco=3355.60, Nint=064, Nfrac=069) +Fch=839.00 (Fvco=3356.00, Nint=064, Nfrac=070) +Fch=839.10 (Fvco=3356.40, Nint=064, Nfrac=071) +Fch=839.20 (Fvco=3356.80, Nint=064, Nfrac=072) +Fch=839.30 (Fvco=3357.20, Nint=064, Nfrac=073) +Fch=839.40 (Fvco=3357.60, Nint=064, Nfrac=074) +Fch=839.50 (Fvco=3358.00, Nint=064, Nfrac=075) +Fch=839.60 (Fvco=3358.40, Nint=064, Nfrac=076) +Fch=839.70 (Fvco=3358.80, Nint=064, Nfrac=077) +Fch=839.80 (Fvco=3359.20, Nint=064, Nfrac=078) +Fch=839.90 (Fvco=3359.60, Nint=064, Nfrac=079) +Fch=840.00 (Fvco=3360.00, Nint=064, Nfrac=080) +Fch=840.10 (Fvco=3360.40, Nint=064, Nfrac=081) +Fch=840.20 (Fvco=3360.80, Nint=064, Nfrac=082) +Fch=840.30 (Fvco=3361.20, Nint=064, Nfrac=083) +Fch=840.40 (Fvco=3361.60, Nint=064, Nfrac=084) +Fch=840.50 (Fvco=3362.00, Nint=064, Nfrac=085) +Fch=840.60 (Fvco=3362.40, Nint=064, Nfrac=086) +Fch=840.70 (Fvco=3362.80, Nint=064, Nfrac=087) +Fch=840.80 (Fvco=3363.20, Nint=064, Nfrac=088) +Fch=840.90 (Fvco=3363.60, Nint=064, Nfrac=089) +Fch=841.00 (Fvco=3364.00, Nint=064, Nfrac=090) +Fch=841.10 (Fvco=3364.40, Nint=064, Nfrac=091) +Fch=841.20 (Fvco=3364.80, Nint=064, Nfrac=092) +Fch=841.30 (Fvco=3365.20, Nint=064, Nfrac=093) +Fch=841.40 (Fvco=3365.60, Nint=064, Nfrac=094) +Fch=841.50 (Fvco=3366.00, Nint=064, Nfrac=095) +Fch=841.60 (Fvco=3366.40, Nint=064, Nfrac=096) +Fch=841.70 (Fvco=3366.80, Nint=064, Nfrac=097) +Fch=841.80 (Fvco=3367.20, Nint=064, Nfrac=098) +Fch=841.90 (Fvco=3367.60, Nint=064, Nfrac=099) +Fch=842.00 (Fvco=3368.00, Nint=064, Nfrac=100) +Fch=842.10 (Fvco=3368.40, Nint=064, Nfrac=101) +Fch=842.20 (Fvco=3368.80, Nint=064, Nfrac=102) +Fch=842.30 (Fvco=3369.20, Nint=064, Nfrac=103) +Fch=842.40 (Fvco=3369.60, Nint=064, Nfrac=104) +Fch=842.50 (Fvco=3370.00, Nint=064, Nfrac=105) +Fch=842.60 (Fvco=3370.40, Nint=064, Nfrac=106) +Fch=842.70 (Fvco=3370.80, Nint=064, Nfrac=107) +Fch=842.80 (Fvco=3371.20, Nint=064, Nfrac=108) +Fch=842.90 (Fvco=3371.60, Nint=064, Nfrac=109) +Fch=843.00 (Fvco=3372.00, Nint=064, Nfrac=110) +Fch=843.10 (Fvco=3372.40, Nint=064, Nfrac=111) +Fch=843.20 (Fvco=3372.80, Nint=064, Nfrac=112) +Fch=843.30 (Fvco=3373.20, Nint=064, Nfrac=113) +Fch=843.40 (Fvco=3373.60, Nint=064, Nfrac=114) +Fch=843.50 (Fvco=3374.00, Nint=064, Nfrac=115) +Fch=843.60 (Fvco=3374.40, Nint=064, Nfrac=116) +Fch=843.70 (Fvco=3374.80, Nint=064, Nfrac=117) +Fch=843.80 (Fvco=3375.20, Nint=064, Nfrac=118) +Fch=843.90 (Fvco=3375.60, Nint=064, Nfrac=119) +Fch=844.00 (Fvco=3376.00, Nint=064, Nfrac=120) +Fch=844.10 (Fvco=3376.40, Nint=064, Nfrac=121) +Fch=844.20 (Fvco=3376.80, Nint=064, Nfrac=122) +Fch=844.30 (Fvco=3377.20, Nint=064, Nfrac=123) +Fch=844.40 (Fvco=3377.60, Nint=064, Nfrac=124) +Fch=844.50 (Fvco=3378.00, Nint=064, Nfrac=125) +Fch=844.60 (Fvco=3378.40, Nint=064, Nfrac=126) +Fch=844.70 (Fvco=3378.80, Nint=064, Nfrac=127) +Fch=844.80 (Fvco=3379.20, Nint=064, Nfrac=128) +Fch=844.90 (Fvco=3379.60, Nint=064, Nfrac=129) +Fch=845.00 (Fvco=3380.00, Nint=064, Nfrac=130) +Fch=845.00 (Fvco=3380.00, Nint=065, Nfrac=000) +Fch=845.10 (Fvco=3380.40, Nint=065, Nfrac=001) +Fch=845.20 (Fvco=3380.80, Nint=065, Nfrac=002) +Fch=845.30 (Fvco=3381.20, Nint=065, Nfrac=003) +Fch=845.40 (Fvco=3381.60, Nint=065, Nfrac=004) +Fch=845.50 (Fvco=3382.00, Nint=065, Nfrac=005) +Fch=845.60 (Fvco=3382.40, Nint=065, Nfrac=006) +Fch=845.70 (Fvco=3382.80, Nint=065, Nfrac=007) +Fch=845.80 (Fvco=3383.20, Nint=065, Nfrac=008) +Fch=845.90 (Fvco=3383.60, Nint=065, Nfrac=009) +Fch=846.00 (Fvco=3384.00, Nint=065, Nfrac=010) +Fch=846.10 (Fvco=3384.40, Nint=065, Nfrac=011) +Fch=846.20 (Fvco=3384.80, Nint=065, Nfrac=012) +Fch=846.30 (Fvco=3385.20, Nint=065, Nfrac=013) +Fch=846.40 (Fvco=3385.60, Nint=065, Nfrac=014) +Fch=846.50 (Fvco=3386.00, Nint=065, Nfrac=015) +Fch=846.60 (Fvco=3386.40, Nint=065, Nfrac=016) +Fch=846.70 (Fvco=3386.80, Nint=065, Nfrac=017) +Fch=846.80 (Fvco=3387.20, Nint=065, Nfrac=018) +Fch=846.90 (Fvco=3387.60, Nint=065, Nfrac=019) +Fch=847.00 (Fvco=3388.00, Nint=065, Nfrac=020) +Fch=847.10 (Fvco=3388.40, Nint=065, Nfrac=021) +Fch=847.20 (Fvco=3388.80, Nint=065, Nfrac=022) +Fch=847.30 (Fvco=3389.20, Nint=065, Nfrac=023) +Fch=847.40 (Fvco=3389.60, Nint=065, Nfrac=024) +Fch=847.50 (Fvco=3390.00, Nint=065, Nfrac=025) +Fch=847.60 (Fvco=3390.40, Nint=065, Nfrac=026) +Fch=847.70 (Fvco=3390.80, Nint=065, Nfrac=027) +Fch=847.80 (Fvco=3391.20, Nint=065, Nfrac=028) +Fch=847.90 (Fvco=3391.60, Nint=065, Nfrac=029) +Fch=848.00 (Fvco=3392.00, Nint=065, Nfrac=030) +Fch=848.10 (Fvco=3392.40, Nint=065, Nfrac=031) +Fch=848.20 (Fvco=3392.80, Nint=065, Nfrac=032) +Fch=848.30 (Fvco=3393.20, Nint=065, Nfrac=033) +Fch=848.40 (Fvco=3393.60, Nint=065, Nfrac=034) +Fch=848.50 (Fvco=3394.00, Nint=065, Nfrac=035) +Fch=848.60 (Fvco=3394.40, Nint=065, Nfrac=036) +Fch=848.70 (Fvco=3394.80, Nint=065, Nfrac=037) +Fch=848.80 (Fvco=3395.20, Nint=065, Nfrac=038) +Fch=848.90 (Fvco=3395.60, Nint=065, Nfrac=039) +Fch=849.00 (Fvco=3396.00, Nint=065, Nfrac=040) +Fch=849.10 (Fvco=3396.40, Nint=065, Nfrac=041) +Fch=849.20 (Fvco=3396.80, Nint=065, Nfrac=042) +Fch=849.30 (Fvco=3397.20, Nint=065, Nfrac=043) +Fch=849.40 (Fvco=3397.60, Nint=065, Nfrac=044) +Fch=849.50 (Fvco=3398.00, Nint=065, Nfrac=045) +Fch=849.60 (Fvco=3398.40, Nint=065, Nfrac=046) +Fch=849.70 (Fvco=3398.80, Nint=065, Nfrac=047) +Fch=849.80 (Fvco=3399.20, Nint=065, Nfrac=048) +Fch=849.90 (Fvco=3399.60, Nint=065, Nfrac=049) +Fch=850.00 (Fvco=3400.00, Nint=065, Nfrac=050) +Fch=850.10 (Fvco=3400.40, Nint=065, Nfrac=051) +Fch=850.20 (Fvco=3400.80, Nint=065, Nfrac=052) +Fch=850.30 (Fvco=3401.20, Nint=065, Nfrac=053) +Fch=850.40 (Fvco=3401.60, Nint=065, Nfrac=054) +Fch=850.50 (Fvco=3402.00, Nint=065, Nfrac=055) +Fch=850.60 (Fvco=3402.40, Nint=065, Nfrac=056) +Fch=850.70 (Fvco=3402.80, Nint=065, Nfrac=057) +Fch=850.80 (Fvco=3403.20, Nint=065, Nfrac=058) +Fch=850.90 (Fvco=3403.60, Nint=065, Nfrac=059) +Fch=851.00 (Fvco=3404.00, Nint=065, Nfrac=060) +Fch=851.10 (Fvco=3404.40, Nint=065, Nfrac=061) +Fch=851.20 (Fvco=3404.80, Nint=065, Nfrac=062) +Fch=851.30 (Fvco=3405.20, Nint=065, Nfrac=063) +Fch=851.40 (Fvco=3405.60, Nint=065, Nfrac=064) +Fch=851.50 (Fvco=3406.00, Nint=065, Nfrac=065) +Fch=851.60 (Fvco=3406.40, Nint=065, Nfrac=066) +Fch=851.70 (Fvco=3406.80, Nint=065, Nfrac=067) +Fch=851.80 (Fvco=3407.20, Nint=065, Nfrac=068) +Fch=851.90 (Fvco=3407.60, Nint=065, Nfrac=069) +Fch=852.00 (Fvco=3408.00, Nint=065, Nfrac=070) +Fch=852.10 (Fvco=3408.40, Nint=065, Nfrac=071) +Fch=852.20 (Fvco=3408.80, Nint=065, Nfrac=072) +Fch=852.30 (Fvco=3409.20, Nint=065, Nfrac=073) +Fch=852.40 (Fvco=3409.60, Nint=065, Nfrac=074) +Fch=852.50 (Fvco=3410.00, Nint=065, Nfrac=075) +Fch=852.60 (Fvco=3410.40, Nint=065, Nfrac=076) +Fch=852.70 (Fvco=3410.80, Nint=065, Nfrac=077) +Fch=852.80 (Fvco=3411.20, Nint=065, Nfrac=078) +Fch=852.90 (Fvco=3411.60, Nint=065, Nfrac=079) +Fch=853.00 (Fvco=3412.00, Nint=065, Nfrac=080) +Fch=853.10 (Fvco=3412.40, Nint=065, Nfrac=081) +Fch=853.20 (Fvco=3412.80, Nint=065, Nfrac=082) +Fch=853.30 (Fvco=3413.20, Nint=065, Nfrac=083) +Fch=853.40 (Fvco=3413.60, Nint=065, Nfrac=084) +Fch=853.50 (Fvco=3414.00, Nint=065, Nfrac=085) +Fch=853.60 (Fvco=3414.40, Nint=065, Nfrac=086) +Fch=853.70 (Fvco=3414.80, Nint=065, Nfrac=087) +Fch=853.80 (Fvco=3415.20, Nint=065, Nfrac=088) +Fch=853.90 (Fvco=3415.60, Nint=065, Nfrac=089) +Fch=854.00 (Fvco=3416.00, Nint=065, Nfrac=090) +Fch=854.10 (Fvco=3416.40, Nint=065, Nfrac=091) +Fch=854.20 (Fvco=3416.80, Nint=065, Nfrac=092) +Fch=854.30 (Fvco=3417.20, Nint=065, Nfrac=093) +Fch=854.40 (Fvco=3417.60, Nint=065, Nfrac=094) +Fch=854.50 (Fvco=3418.00, Nint=065, Nfrac=095) +Fch=854.60 (Fvco=3418.40, Nint=065, Nfrac=096) +Fch=854.70 (Fvco=3418.80, Nint=065, Nfrac=097) +Fch=854.80 (Fvco=3419.20, Nint=065, Nfrac=098) +Fch=854.90 (Fvco=3419.60, Nint=065, Nfrac=099) +Fch=855.00 (Fvco=3420.00, Nint=065, Nfrac=100) +Fch=855.10 (Fvco=3420.40, Nint=065, Nfrac=101) +Fch=855.20 (Fvco=3420.80, Nint=065, Nfrac=102) +Fch=855.30 (Fvco=3421.20, Nint=065, Nfrac=103) +Fch=855.40 (Fvco=3421.60, Nint=065, Nfrac=104) +Fch=855.50 (Fvco=3422.00, Nint=065, Nfrac=105) +Fch=855.60 (Fvco=3422.40, Nint=065, Nfrac=106) +Fch=855.70 (Fvco=3422.80, Nint=065, Nfrac=107) +Fch=855.80 (Fvco=3423.20, Nint=065, Nfrac=108) +Fch=855.90 (Fvco=3423.60, Nint=065, Nfrac=109) +Fch=856.00 (Fvco=3424.00, Nint=065, Nfrac=110) +Fch=856.10 (Fvco=3424.40, Nint=065, Nfrac=111) +Fch=856.20 (Fvco=3424.80, Nint=065, Nfrac=112) +Fch=856.30 (Fvco=3425.20, Nint=065, Nfrac=113) +Fch=856.40 (Fvco=3425.60, Nint=065, Nfrac=114) +Fch=856.50 (Fvco=3426.00, Nint=065, Nfrac=115) +Fch=856.60 (Fvco=3426.40, Nint=065, Nfrac=116) +Fch=856.70 (Fvco=3426.80, Nint=065, Nfrac=117) +Fch=856.80 (Fvco=3427.20, Nint=065, Nfrac=118) +Fch=856.90 (Fvco=3427.60, Nint=065, Nfrac=119) +Fch=857.00 (Fvco=3428.00, Nint=065, Nfrac=120) +Fch=857.10 (Fvco=3428.40, Nint=065, Nfrac=121) +Fch=857.20 (Fvco=3428.80, Nint=065, Nfrac=122) +Fch=857.30 (Fvco=3429.20, Nint=065, Nfrac=123) +Fch=857.40 (Fvco=3429.60, Nint=065, Nfrac=124) +Fch=857.50 (Fvco=3430.00, Nint=065, Nfrac=125) +Fch=857.60 (Fvco=3430.40, Nint=065, Nfrac=126) +Fch=857.70 (Fvco=3430.80, Nint=065, Nfrac=127) +Fch=857.80 (Fvco=3431.20, Nint=065, Nfrac=128) +Fch=857.90 (Fvco=3431.60, Nint=065, Nfrac=129) +Fch=858.00 (Fvco=3432.00, Nint=065, Nfrac=130) +Fch=858.00 (Fvco=3432.00, Nint=066, Nfrac=000) +Fch=858.10 (Fvco=3432.40, Nint=066, Nfrac=001) +Fch=858.20 (Fvco=3432.80, Nint=066, Nfrac=002) +Fch=858.30 (Fvco=3433.20, Nint=066, Nfrac=003) +Fch=858.40 (Fvco=3433.60, Nint=066, Nfrac=004) +Fch=858.50 (Fvco=3434.00, Nint=066, Nfrac=005) +Fch=858.60 (Fvco=3434.40, Nint=066, Nfrac=006) +Fch=858.70 (Fvco=3434.80, Nint=066, Nfrac=007) +Fch=858.80 (Fvco=3435.20, Nint=066, Nfrac=008) +Fch=858.90 (Fvco=3435.60, Nint=066, Nfrac=009) +Fch=859.00 (Fvco=3436.00, Nint=066, Nfrac=010) +Fch=859.10 (Fvco=3436.40, Nint=066, Nfrac=011) +Fch=859.20 (Fvco=3436.80, Nint=066, Nfrac=012) +Fch=859.30 (Fvco=3437.20, Nint=066, Nfrac=013) +Fch=859.40 (Fvco=3437.60, Nint=066, Nfrac=014) +Fch=859.50 (Fvco=3438.00, Nint=066, Nfrac=015) +Fch=859.60 (Fvco=3438.40, Nint=066, Nfrac=016) +Fch=859.70 (Fvco=3438.80, Nint=066, Nfrac=017) +Fch=859.80 (Fvco=3439.20, Nint=066, Nfrac=018) +Fch=859.90 (Fvco=3439.60, Nint=066, Nfrac=019) +Fch=860.00 (Fvco=3440.00, Nint=066, Nfrac=020) +Fch=860.10 (Fvco=3440.40, Nint=066, Nfrac=021) +Fch=860.20 (Fvco=3440.80, Nint=066, Nfrac=022) +Fch=860.30 (Fvco=3441.20, Nint=066, Nfrac=023) +Fch=860.40 (Fvco=3441.60, Nint=066, Nfrac=024) +Fch=860.50 (Fvco=3442.00, Nint=066, Nfrac=025) +Fch=860.60 (Fvco=3442.40, Nint=066, Nfrac=026) +Fch=860.70 (Fvco=3442.80, Nint=066, Nfrac=027) +Fch=860.80 (Fvco=3443.20, Nint=066, Nfrac=028) +Fch=860.90 (Fvco=3443.60, Nint=066, Nfrac=029) +Fch=861.00 (Fvco=3444.00, Nint=066, Nfrac=030) +Fch=861.10 (Fvco=3444.40, Nint=066, Nfrac=031) +Fch=861.20 (Fvco=3444.80, Nint=066, Nfrac=032) +Fch=861.30 (Fvco=3445.20, Nint=066, Nfrac=033) +Fch=861.40 (Fvco=3445.60, Nint=066, Nfrac=034) +Fch=861.50 (Fvco=3446.00, Nint=066, Nfrac=035) +Fch=861.60 (Fvco=3446.40, Nint=066, Nfrac=036) +Fch=861.70 (Fvco=3446.80, Nint=066, Nfrac=037) +Fch=861.80 (Fvco=3447.20, Nint=066, Nfrac=038) +Fch=861.90 (Fvco=3447.60, Nint=066, Nfrac=039) +Fch=862.00 (Fvco=3448.00, Nint=066, Nfrac=040) +Fch=862.10 (Fvco=3448.40, Nint=066, Nfrac=041) +Fch=862.20 (Fvco=3448.80, Nint=066, Nfrac=042) +Fch=862.30 (Fvco=3449.20, Nint=066, Nfrac=043) +Fch=862.40 (Fvco=3449.60, Nint=066, Nfrac=044) +Fch=862.50 (Fvco=3450.00, Nint=066, Nfrac=045) +Fch=862.60 (Fvco=3450.40, Nint=066, Nfrac=046) +Fch=862.70 (Fvco=3450.80, Nint=066, Nfrac=047) +Fch=862.80 (Fvco=3451.20, Nint=066, Nfrac=048) +Fch=862.90 (Fvco=3451.60, Nint=066, Nfrac=049) +Fch=863.00 (Fvco=3452.00, Nint=066, Nfrac=050) +Fch=863.10 (Fvco=3452.40, Nint=066, Nfrac=051) +Fch=863.20 (Fvco=3452.80, Nint=066, Nfrac=052) +Fch=863.30 (Fvco=3453.20, Nint=066, Nfrac=053) +Fch=863.40 (Fvco=3453.60, Nint=066, Nfrac=054) +Fch=863.50 (Fvco=3454.00, Nint=066, Nfrac=055) +Fch=863.60 (Fvco=3454.40, Nint=066, Nfrac=056) +Fch=863.70 (Fvco=3454.80, Nint=066, Nfrac=057) +Fch=863.80 (Fvco=3455.20, Nint=066, Nfrac=058) +Fch=863.90 (Fvco=3455.60, Nint=066, Nfrac=059) +Fch=864.00 (Fvco=3456.00, Nint=066, Nfrac=060) +Fch=864.10 (Fvco=3456.40, Nint=066, Nfrac=061) +Fch=864.20 (Fvco=3456.80, Nint=066, Nfrac=062) +Fch=864.30 (Fvco=3457.20, Nint=066, Nfrac=063) +Fch=864.40 (Fvco=3457.60, Nint=066, Nfrac=064) +Fch=864.50 (Fvco=3458.00, Nint=066, Nfrac=065) +Fch=864.60 (Fvco=3458.40, Nint=066, Nfrac=066) +Fch=864.70 (Fvco=3458.80, Nint=066, Nfrac=067) +Fch=864.80 (Fvco=3459.20, Nint=066, Nfrac=068) +Fch=864.90 (Fvco=3459.60, Nint=066, Nfrac=069) +Fch=865.00 (Fvco=3460.00, Nint=066, Nfrac=070) +Fch=865.10 (Fvco=3460.40, Nint=066, Nfrac=071) +Fch=865.20 (Fvco=3460.80, Nint=066, Nfrac=072) +Fch=865.30 (Fvco=3461.20, Nint=066, Nfrac=073) +Fch=865.40 (Fvco=3461.60, Nint=066, Nfrac=074) +Fch=865.50 (Fvco=3462.00, Nint=066, Nfrac=075) +Fch=865.60 (Fvco=3462.40, Nint=066, Nfrac=076) +Fch=865.70 (Fvco=3462.80, Nint=066, Nfrac=077) +Fch=865.80 (Fvco=3463.20, Nint=066, Nfrac=078) +Fch=865.90 (Fvco=3463.60, Nint=066, Nfrac=079) +Fch=866.00 (Fvco=3464.00, Nint=066, Nfrac=080) +Fch=866.10 (Fvco=3464.40, Nint=066, Nfrac=081) +Fch=866.20 (Fvco=3464.80, Nint=066, Nfrac=082) +Fch=866.30 (Fvco=3465.20, Nint=066, Nfrac=083) +Fch=866.40 (Fvco=3465.60, Nint=066, Nfrac=084) +Fch=866.50 (Fvco=3466.00, Nint=066, Nfrac=085) +Fch=866.60 (Fvco=3466.40, Nint=066, Nfrac=086) +Fch=866.70 (Fvco=3466.80, Nint=066, Nfrac=087) +Fch=866.80 (Fvco=3467.20, Nint=066, Nfrac=088) +Fch=866.90 (Fvco=3467.60, Nint=066, Nfrac=089) +Fch=867.00 (Fvco=3468.00, Nint=066, Nfrac=090) +Fch=867.10 (Fvco=3468.40, Nint=066, Nfrac=091) +Fch=867.20 (Fvco=3468.80, Nint=066, Nfrac=092) +Fch=867.30 (Fvco=3469.20, Nint=066, Nfrac=093) +Fch=867.40 (Fvco=3469.60, Nint=066, Nfrac=094) +Fch=867.50 (Fvco=3470.00, Nint=066, Nfrac=095) +Fch=867.60 (Fvco=3470.40, Nint=066, Nfrac=096) +Fch=867.70 (Fvco=3470.80, Nint=066, Nfrac=097) +Fch=867.80 (Fvco=3471.20, Nint=066, Nfrac=098) +Fch=867.90 (Fvco=3471.60, Nint=066, Nfrac=099) +Fch=868.00 (Fvco=3472.00, Nint=066, Nfrac=100) +Fch=868.10 (Fvco=3472.40, Nint=066, Nfrac=101) +Fch=868.20 (Fvco=3472.80, Nint=066, Nfrac=102) +Fch=868.30 (Fvco=3473.20, Nint=066, Nfrac=103) +Fch=868.40 (Fvco=3473.60, Nint=066, Nfrac=104) +Fch=868.50 (Fvco=3474.00, Nint=066, Nfrac=105) +Fch=868.60 (Fvco=3474.40, Nint=066, Nfrac=106) +Fch=868.70 (Fvco=3474.80, Nint=066, Nfrac=107) +Fch=868.80 (Fvco=3475.20, Nint=066, Nfrac=108) +Fch=868.90 (Fvco=3475.60, Nint=066, Nfrac=109) +Fch=869.00 (Fvco=3476.00, Nint=066, Nfrac=110) +Fch=869.10 (Fvco=3476.40, Nint=066, Nfrac=111) +Fch=869.20 (Fvco=3476.80, Nint=066, Nfrac=112) +Fch=869.30 (Fvco=3477.20, Nint=066, Nfrac=113) +Fch=869.40 (Fvco=3477.60, Nint=066, Nfrac=114) +Fch=869.50 (Fvco=3478.00, Nint=066, Nfrac=115) +Fch=869.60 (Fvco=3478.40, Nint=066, Nfrac=116) +Fch=869.70 (Fvco=3478.80, Nint=066, Nfrac=117) +Fch=869.80 (Fvco=3479.20, Nint=066, Nfrac=118) +Fch=869.90 (Fvco=3479.60, Nint=066, Nfrac=119) +Fch=870.00 (Fvco=3480.00, Nint=066, Nfrac=120) +Fch=870.10 (Fvco=3480.40, Nint=066, Nfrac=121) +Fch=870.20 (Fvco=3480.80, Nint=066, Nfrac=122) +Fch=870.30 (Fvco=3481.20, Nint=066, Nfrac=123) +Fch=870.40 (Fvco=3481.60, Nint=066, Nfrac=124) +Fch=870.50 (Fvco=3482.00, Nint=066, Nfrac=125) +Fch=870.60 (Fvco=3482.40, Nint=066, Nfrac=126) +Fch=870.70 (Fvco=3482.80, Nint=066, Nfrac=127) +Fch=870.80 (Fvco=3483.20, Nint=066, Nfrac=128) +Fch=870.90 (Fvco=3483.60, Nint=066, Nfrac=129) +Fch=871.00 (Fvco=3484.00, Nint=066, Nfrac=130) +Fch=871.00 (Fvco=3484.00, Nint=067, Nfrac=000) +Fch=871.10 (Fvco=3484.40, Nint=067, Nfrac=001) +Fch=871.20 (Fvco=3484.80, Nint=067, Nfrac=002) +Fch=871.30 (Fvco=3485.20, Nint=067, Nfrac=003) +Fch=871.40 (Fvco=3485.60, Nint=067, Nfrac=004) +Fch=871.50 (Fvco=3486.00, Nint=067, Nfrac=005) +Fch=871.60 (Fvco=3486.40, Nint=067, Nfrac=006) +Fch=871.70 (Fvco=3486.80, Nint=067, Nfrac=007) +Fch=871.80 (Fvco=3487.20, Nint=067, Nfrac=008) +Fch=871.90 (Fvco=3487.60, Nint=067, Nfrac=009) +Fch=872.00 (Fvco=3488.00, Nint=067, Nfrac=010) +Fch=872.10 (Fvco=3488.40, Nint=067, Nfrac=011) +Fch=872.20 (Fvco=3488.80, Nint=067, Nfrac=012) +Fch=872.30 (Fvco=3489.20, Nint=067, Nfrac=013) +Fch=872.40 (Fvco=3489.60, Nint=067, Nfrac=014) +Fch=872.50 (Fvco=3490.00, Nint=067, Nfrac=015) +Fch=872.60 (Fvco=3490.40, Nint=067, Nfrac=016) +Fch=872.70 (Fvco=3490.80, Nint=067, Nfrac=017) +Fch=872.80 (Fvco=3491.20, Nint=067, Nfrac=018) +Fch=872.90 (Fvco=3491.60, Nint=067, Nfrac=019) +Fch=873.00 (Fvco=3492.00, Nint=067, Nfrac=020) +Fch=873.10 (Fvco=3492.40, Nint=067, Nfrac=021) +Fch=873.20 (Fvco=3492.80, Nint=067, Nfrac=022) +Fch=873.30 (Fvco=3493.20, Nint=067, Nfrac=023) +Fch=873.40 (Fvco=3493.60, Nint=067, Nfrac=024) +Fch=873.50 (Fvco=3494.00, Nint=067, Nfrac=025) +Fch=873.60 (Fvco=3494.40, Nint=067, Nfrac=026) +Fch=873.70 (Fvco=3494.80, Nint=067, Nfrac=027) +Fch=873.80 (Fvco=3495.20, Nint=067, Nfrac=028) +Fch=873.90 (Fvco=3495.60, Nint=067, Nfrac=029) +Fch=874.00 (Fvco=3496.00, Nint=067, Nfrac=030) +Fch=874.10 (Fvco=3496.40, Nint=067, Nfrac=031) +Fch=874.20 (Fvco=3496.80, Nint=067, Nfrac=032) +Fch=874.30 (Fvco=3497.20, Nint=067, Nfrac=033) +Fch=874.40 (Fvco=3497.60, Nint=067, Nfrac=034) +Fch=874.50 (Fvco=3498.00, Nint=067, Nfrac=035) +Fch=874.60 (Fvco=3498.40, Nint=067, Nfrac=036) +Fch=874.70 (Fvco=3498.80, Nint=067, Nfrac=037) +Fch=874.80 (Fvco=3499.20, Nint=067, Nfrac=038) +Fch=874.90 (Fvco=3499.60, Nint=067, Nfrac=039) +Fch=875.00 (Fvco=3500.00, Nint=067, Nfrac=040) +Fch=875.10 (Fvco=3500.40, Nint=067, Nfrac=041) +Fch=875.20 (Fvco=3500.80, Nint=067, Nfrac=042) +Fch=875.30 (Fvco=3501.20, Nint=067, Nfrac=043) +Fch=875.40 (Fvco=3501.60, Nint=067, Nfrac=044) +Fch=875.50 (Fvco=3502.00, Nint=067, Nfrac=045) +Fch=875.60 (Fvco=3502.40, Nint=067, Nfrac=046) +Fch=875.70 (Fvco=3502.80, Nint=067, Nfrac=047) +Fch=875.80 (Fvco=3503.20, Nint=067, Nfrac=048) +Fch=875.90 (Fvco=3503.60, Nint=067, Nfrac=049) +Fch=876.00 (Fvco=3504.00, Nint=067, Nfrac=050) +Fch=876.10 (Fvco=3504.40, Nint=067, Nfrac=051) +Fch=876.20 (Fvco=3504.80, Nint=067, Nfrac=052) +Fch=876.30 (Fvco=3505.20, Nint=067, Nfrac=053) +Fch=876.40 (Fvco=3505.60, Nint=067, Nfrac=054) +Fch=876.50 (Fvco=3506.00, Nint=067, Nfrac=055) +Fch=876.60 (Fvco=3506.40, Nint=067, Nfrac=056) +Fch=876.70 (Fvco=3506.80, Nint=067, Nfrac=057) +Fch=876.80 (Fvco=3507.20, Nint=067, Nfrac=058) +Fch=876.90 (Fvco=3507.60, Nint=067, Nfrac=059) +Fch=877.00 (Fvco=3508.00, Nint=067, Nfrac=060) +Fch=877.10 (Fvco=3508.40, Nint=067, Nfrac=061) +Fch=877.20 (Fvco=3508.80, Nint=067, Nfrac=062) +Fch=877.30 (Fvco=3509.20, Nint=067, Nfrac=063) +Fch=877.40 (Fvco=3509.60, Nint=067, Nfrac=064) +Fch=877.50 (Fvco=3510.00, Nint=067, Nfrac=065) +Fch=877.60 (Fvco=3510.40, Nint=067, Nfrac=066) +Fch=877.70 (Fvco=3510.80, Nint=067, Nfrac=067) +Fch=877.80 (Fvco=3511.20, Nint=067, Nfrac=068) +Fch=877.90 (Fvco=3511.60, Nint=067, Nfrac=069) +Fch=878.00 (Fvco=3512.00, Nint=067, Nfrac=070) +Fch=878.10 (Fvco=3512.40, Nint=067, Nfrac=071) +Fch=878.20 (Fvco=3512.80, Nint=067, Nfrac=072) +Fch=878.30 (Fvco=3513.20, Nint=067, Nfrac=073) +Fch=878.40 (Fvco=3513.60, Nint=067, Nfrac=074) +Fch=878.50 (Fvco=3514.00, Nint=067, Nfrac=075) +Fch=878.60 (Fvco=3514.40, Nint=067, Nfrac=076) +Fch=878.70 (Fvco=3514.80, Nint=067, Nfrac=077) +Fch=878.80 (Fvco=3515.20, Nint=067, Nfrac=078) +Fch=878.90 (Fvco=3515.60, Nint=067, Nfrac=079) +Fch=879.00 (Fvco=3516.00, Nint=067, Nfrac=080) +Fch=879.10 (Fvco=3516.40, Nint=067, Nfrac=081) +Fch=879.20 (Fvco=3516.80, Nint=067, Nfrac=082) +Fch=879.30 (Fvco=3517.20, Nint=067, Nfrac=083) +Fch=879.40 (Fvco=3517.60, Nint=067, Nfrac=084) +Fch=879.50 (Fvco=3518.00, Nint=067, Nfrac=085) +Fch=879.60 (Fvco=3518.40, Nint=067, Nfrac=086) +Fch=879.70 (Fvco=3518.80, Nint=067, Nfrac=087) +Fch=879.80 (Fvco=3519.20, Nint=067, Nfrac=088) +Fch=879.90 (Fvco=3519.60, Nint=067, Nfrac=089) +Fch=880.00 (Fvco=3520.00, Nint=067, Nfrac=090) +Fch=880.10 (Fvco=3520.40, Nint=067, Nfrac=091) +Fch=880.20 (Fvco=3520.80, Nint=067, Nfrac=092) +Fch=880.30 (Fvco=3521.20, Nint=067, Nfrac=093) +Fch=880.40 (Fvco=3521.60, Nint=067, Nfrac=094) +Fch=880.50 (Fvco=3522.00, Nint=067, Nfrac=095) +Fch=880.60 (Fvco=3522.40, Nint=067, Nfrac=096) +Fch=880.70 (Fvco=3522.80, Nint=067, Nfrac=097) +Fch=880.80 (Fvco=3523.20, Nint=067, Nfrac=098) +Fch=880.90 (Fvco=3523.60, Nint=067, Nfrac=099) +Fch=881.00 (Fvco=3524.00, Nint=067, Nfrac=100) +Fch=881.10 (Fvco=3524.40, Nint=067, Nfrac=101) +Fch=881.20 (Fvco=3524.80, Nint=067, Nfrac=102) +Fch=881.30 (Fvco=3525.20, Nint=067, Nfrac=103) +Fch=881.40 (Fvco=3525.60, Nint=067, Nfrac=104) +Fch=881.50 (Fvco=3526.00, Nint=067, Nfrac=105) +Fch=881.60 (Fvco=3526.40, Nint=067, Nfrac=106) +Fch=881.70 (Fvco=3526.80, Nint=067, Nfrac=107) +Fch=881.80 (Fvco=3527.20, Nint=067, Nfrac=108) +Fch=881.90 (Fvco=3527.60, Nint=067, Nfrac=109) +Fch=882.00 (Fvco=3528.00, Nint=067, Nfrac=110) +Fch=882.10 (Fvco=3528.40, Nint=067, Nfrac=111) +Fch=882.20 (Fvco=3528.80, Nint=067, Nfrac=112) +Fch=882.30 (Fvco=3529.20, Nint=067, Nfrac=113) +Fch=882.40 (Fvco=3529.60, Nint=067, Nfrac=114) +Fch=882.50 (Fvco=3530.00, Nint=067, Nfrac=115) +Fch=882.60 (Fvco=3530.40, Nint=067, Nfrac=116) +Fch=882.70 (Fvco=3530.80, Nint=067, Nfrac=117) +Fch=882.80 (Fvco=3531.20, Nint=067, Nfrac=118) +Fch=882.90 (Fvco=3531.60, Nint=067, Nfrac=119) +Fch=883.00 (Fvco=3532.00, Nint=067, Nfrac=120) +Fch=883.10 (Fvco=3532.40, Nint=067, Nfrac=121) +Fch=883.20 (Fvco=3532.80, Nint=067, Nfrac=122) +Fch=883.30 (Fvco=3533.20, Nint=067, Nfrac=123) +Fch=883.40 (Fvco=3533.60, Nint=067, Nfrac=124) +Fch=883.50 (Fvco=3534.00, Nint=067, Nfrac=125) +Fch=883.60 (Fvco=3534.40, Nint=067, Nfrac=126) +Fch=883.70 (Fvco=3534.80, Nint=067, Nfrac=127) +Fch=883.80 (Fvco=3535.20, Nint=067, Nfrac=128) +Fch=883.90 (Fvco=3535.60, Nint=067, Nfrac=129) +Fch=884.00 (Fvco=3536.00, Nint=067, Nfrac=130) +Fch=884.00 (Fvco=3536.00, Nint=068, Nfrac=000) +Fch=884.10 (Fvco=3536.40, Nint=068, Nfrac=001) +Fch=884.20 (Fvco=3536.80, Nint=068, Nfrac=002) +Fch=884.30 (Fvco=3537.20, Nint=068, Nfrac=003) +Fch=884.40 (Fvco=3537.60, Nint=068, Nfrac=004) +Fch=884.50 (Fvco=3538.00, Nint=068, Nfrac=005) +Fch=884.60 (Fvco=3538.40, Nint=068, Nfrac=006) +Fch=884.70 (Fvco=3538.80, Nint=068, Nfrac=007) +Fch=884.80 (Fvco=3539.20, Nint=068, Nfrac=008) +Fch=884.90 (Fvco=3539.60, Nint=068, Nfrac=009) +Fch=885.00 (Fvco=3540.00, Nint=068, Nfrac=010) +Fch=885.10 (Fvco=3540.40, Nint=068, Nfrac=011) +Fch=885.20 (Fvco=3540.80, Nint=068, Nfrac=012) +Fch=885.30 (Fvco=3541.20, Nint=068, Nfrac=013) +Fch=885.40 (Fvco=3541.60, Nint=068, Nfrac=014) +Fch=885.50 (Fvco=3542.00, Nint=068, Nfrac=015) +Fch=885.60 (Fvco=3542.40, Nint=068, Nfrac=016) +Fch=885.70 (Fvco=3542.80, Nint=068, Nfrac=017) +Fch=885.80 (Fvco=3543.20, Nint=068, Nfrac=018) +Fch=885.90 (Fvco=3543.60, Nint=068, Nfrac=019) +Fch=886.00 (Fvco=3544.00, Nint=068, Nfrac=020) +Fch=886.10 (Fvco=3544.40, Nint=068, Nfrac=021) +Fch=886.20 (Fvco=3544.80, Nint=068, Nfrac=022) +Fch=886.30 (Fvco=3545.20, Nint=068, Nfrac=023) +Fch=886.40 (Fvco=3545.60, Nint=068, Nfrac=024) +Fch=886.50 (Fvco=3546.00, Nint=068, Nfrac=025) +Fch=886.60 (Fvco=3546.40, Nint=068, Nfrac=026) +Fch=886.70 (Fvco=3546.80, Nint=068, Nfrac=027) +Fch=886.80 (Fvco=3547.20, Nint=068, Nfrac=028) +Fch=886.90 (Fvco=3547.60, Nint=068, Nfrac=029) +Fch=887.00 (Fvco=3548.00, Nint=068, Nfrac=030) +Fch=887.10 (Fvco=3548.40, Nint=068, Nfrac=031) +Fch=887.20 (Fvco=3548.80, Nint=068, Nfrac=032) +Fch=887.30 (Fvco=3549.20, Nint=068, Nfrac=033) +Fch=887.40 (Fvco=3549.60, Nint=068, Nfrac=034) +Fch=887.50 (Fvco=3550.00, Nint=068, Nfrac=035) +Fch=887.60 (Fvco=3550.40, Nint=068, Nfrac=036) +Fch=887.70 (Fvco=3550.80, Nint=068, Nfrac=037) +Fch=887.80 (Fvco=3551.20, Nint=068, Nfrac=038) +Fch=887.90 (Fvco=3551.60, Nint=068, Nfrac=039) +Fch=888.00 (Fvco=3552.00, Nint=068, Nfrac=040) +Fch=888.10 (Fvco=3552.40, Nint=068, Nfrac=041) +Fch=888.20 (Fvco=3552.80, Nint=068, Nfrac=042) +Fch=888.30 (Fvco=3553.20, Nint=068, Nfrac=043) +Fch=888.40 (Fvco=3553.60, Nint=068, Nfrac=044) +Fch=888.50 (Fvco=3554.00, Nint=068, Nfrac=045) +Fch=888.60 (Fvco=3554.40, Nint=068, Nfrac=046) +Fch=888.70 (Fvco=3554.80, Nint=068, Nfrac=047) +Fch=888.80 (Fvco=3555.20, Nint=068, Nfrac=048) +Fch=888.90 (Fvco=3555.60, Nint=068, Nfrac=049) +Fch=889.00 (Fvco=3556.00, Nint=068, Nfrac=050) +Fch=889.10 (Fvco=3556.40, Nint=068, Nfrac=051) +Fch=889.20 (Fvco=3556.80, Nint=068, Nfrac=052) +Fch=889.30 (Fvco=3557.20, Nint=068, Nfrac=053) +Fch=889.40 (Fvco=3557.60, Nint=068, Nfrac=054) +Fch=889.50 (Fvco=3558.00, Nint=068, Nfrac=055) +Fch=889.60 (Fvco=3558.40, Nint=068, Nfrac=056) +Fch=889.70 (Fvco=3558.80, Nint=068, Nfrac=057) +Fch=889.80 (Fvco=3559.20, Nint=068, Nfrac=058) +Fch=889.90 (Fvco=3559.60, Nint=068, Nfrac=059) +Fch=890.00 (Fvco=3560.00, Nint=068, Nfrac=060) +Fch=890.10 (Fvco=3560.40, Nint=068, Nfrac=061) +Fch=890.20 (Fvco=3560.80, Nint=068, Nfrac=062) +Fch=890.30 (Fvco=3561.20, Nint=068, Nfrac=063) +Fch=890.40 (Fvco=3561.60, Nint=068, Nfrac=064) +Fch=890.50 (Fvco=3562.00, Nint=068, Nfrac=065) +Fch=890.60 (Fvco=3562.40, Nint=068, Nfrac=066) +Fch=890.70 (Fvco=3562.80, Nint=068, Nfrac=067) +Fch=890.80 (Fvco=3563.20, Nint=068, Nfrac=068) +Fch=890.90 (Fvco=3563.60, Nint=068, Nfrac=069) +Fch=891.00 (Fvco=3564.00, Nint=068, Nfrac=070) +Fch=891.10 (Fvco=3564.40, Nint=068, Nfrac=071) +Fch=891.20 (Fvco=3564.80, Nint=068, Nfrac=072) +Fch=891.30 (Fvco=3565.20, Nint=068, Nfrac=073) +Fch=891.40 (Fvco=3565.60, Nint=068, Nfrac=074) +Fch=891.50 (Fvco=3566.00, Nint=068, Nfrac=075) +Fch=891.60 (Fvco=3566.40, Nint=068, Nfrac=076) +Fch=891.70 (Fvco=3566.80, Nint=068, Nfrac=077) +Fch=891.80 (Fvco=3567.20, Nint=068, Nfrac=078) +Fch=891.90 (Fvco=3567.60, Nint=068, Nfrac=079) +Fch=892.00 (Fvco=3568.00, Nint=068, Nfrac=080) +Fch=892.10 (Fvco=3568.40, Nint=068, Nfrac=081) +Fch=892.20 (Fvco=3568.80, Nint=068, Nfrac=082) +Fch=892.30 (Fvco=3569.20, Nint=068, Nfrac=083) +Fch=892.40 (Fvco=3569.60, Nint=068, Nfrac=084) +Fch=892.50 (Fvco=3570.00, Nint=068, Nfrac=085) +Fch=892.60 (Fvco=3570.40, Nint=068, Nfrac=086) +Fch=892.70 (Fvco=3570.80, Nint=068, Nfrac=087) +Fch=892.80 (Fvco=3571.20, Nint=068, Nfrac=088) +Fch=892.90 (Fvco=3571.60, Nint=068, Nfrac=089) +Fch=893.00 (Fvco=3572.00, Nint=068, Nfrac=090) +Fch=893.10 (Fvco=3572.40, Nint=068, Nfrac=091) +Fch=893.20 (Fvco=3572.80, Nint=068, Nfrac=092) +Fch=893.30 (Fvco=3573.20, Nint=068, Nfrac=093) +Fch=893.40 (Fvco=3573.60, Nint=068, Nfrac=094) +Fch=893.50 (Fvco=3574.00, Nint=068, Nfrac=095) +Fch=893.60 (Fvco=3574.40, Nint=068, Nfrac=096) +Fch=893.70 (Fvco=3574.80, Nint=068, Nfrac=097) +Fch=893.80 (Fvco=3575.20, Nint=068, Nfrac=098) +Fch=893.90 (Fvco=3575.60, Nint=068, Nfrac=099) +Fch=894.00 (Fvco=3576.00, Nint=068, Nfrac=100) +Fch=894.10 (Fvco=3576.40, Nint=068, Nfrac=101) +Fch=894.20 (Fvco=3576.80, Nint=068, Nfrac=102) +Fch=894.30 (Fvco=3577.20, Nint=068, Nfrac=103) +Fch=894.40 (Fvco=3577.60, Nint=068, Nfrac=104) +Fch=894.50 (Fvco=3578.00, Nint=068, Nfrac=105) +Fch=894.60 (Fvco=3578.40, Nint=068, Nfrac=106) +Fch=894.70 (Fvco=3578.80, Nint=068, Nfrac=107) +Fch=894.80 (Fvco=3579.20, Nint=068, Nfrac=108) +Fch=894.90 (Fvco=3579.60, Nint=068, Nfrac=109) +Fch=895.00 (Fvco=3580.00, Nint=068, Nfrac=110) +Fch=895.10 (Fvco=3580.40, Nint=068, Nfrac=111) +Fch=895.20 (Fvco=3580.80, Nint=068, Nfrac=112) +Fch=895.30 (Fvco=3581.20, Nint=068, Nfrac=113) +Fch=895.40 (Fvco=3581.60, Nint=068, Nfrac=114) +Fch=895.50 (Fvco=3582.00, Nint=068, Nfrac=115) +Fch=895.60 (Fvco=3582.40, Nint=068, Nfrac=116) +Fch=895.70 (Fvco=3582.80, Nint=068, Nfrac=117) +Fch=895.80 (Fvco=3583.20, Nint=068, Nfrac=118) +Fch=895.90 (Fvco=3583.60, Nint=068, Nfrac=119) +Fch=896.00 (Fvco=3584.00, Nint=068, Nfrac=120) +Fch=896.10 (Fvco=3584.40, Nint=068, Nfrac=121) +Fch=896.20 (Fvco=3584.80, Nint=068, Nfrac=122) +Fch=896.30 (Fvco=3585.20, Nint=068, Nfrac=123) +Fch=896.40 (Fvco=3585.60, Nint=068, Nfrac=124) +Fch=896.50 (Fvco=3586.00, Nint=068, Nfrac=125) +Fch=896.60 (Fvco=3586.40, Nint=068, Nfrac=126) +Fch=896.70 (Fvco=3586.80, Nint=068, Nfrac=127) +Fch=896.80 (Fvco=3587.20, Nint=068, Nfrac=128) +Fch=896.90 (Fvco=3587.60, Nint=068, Nfrac=129) +Fch=897.00 (Fvco=3588.00, Nint=068, Nfrac=130) +Fch=897.00 (Fvco=3588.00, Nint=069, Nfrac=000) +Fch=897.10 (Fvco=3588.40, Nint=069, Nfrac=001) +Fch=897.20 (Fvco=3588.80, Nint=069, Nfrac=002) +Fch=897.30 (Fvco=3589.20, Nint=069, Nfrac=003) +Fch=897.40 (Fvco=3589.60, Nint=069, Nfrac=004) +Fch=897.50 (Fvco=3590.00, Nint=069, Nfrac=005) +Fch=897.60 (Fvco=3590.40, Nint=069, Nfrac=006) +Fch=897.70 (Fvco=3590.80, Nint=069, Nfrac=007) +Fch=897.80 (Fvco=3591.20, Nint=069, Nfrac=008) +Fch=897.90 (Fvco=3591.60, Nint=069, Nfrac=009) +Fch=898.00 (Fvco=3592.00, Nint=069, Nfrac=010) +Fch=898.10 (Fvco=3592.40, Nint=069, Nfrac=011) +Fch=898.20 (Fvco=3592.80, Nint=069, Nfrac=012) +Fch=898.30 (Fvco=3593.20, Nint=069, Nfrac=013) +Fch=898.40 (Fvco=3593.60, Nint=069, Nfrac=014) +Fch=898.50 (Fvco=3594.00, Nint=069, Nfrac=015) +Fch=898.60 (Fvco=3594.40, Nint=069, Nfrac=016) +Fch=898.70 (Fvco=3594.80, Nint=069, Nfrac=017) +Fch=898.80 (Fvco=3595.20, Nint=069, Nfrac=018) +Fch=898.90 (Fvco=3595.60, Nint=069, Nfrac=019) +Fch=899.00 (Fvco=3596.00, Nint=069, Nfrac=020) +Fch=899.10 (Fvco=3596.40, Nint=069, Nfrac=021) +Fch=899.20 (Fvco=3596.80, Nint=069, Nfrac=022) +Fch=899.30 (Fvco=3597.20, Nint=069, Nfrac=023) +Fch=899.40 (Fvco=3597.60, Nint=069, Nfrac=024) +Fch=899.50 (Fvco=3598.00, Nint=069, Nfrac=025) +Fch=899.60 (Fvco=3598.40, Nint=069, Nfrac=026) +Fch=899.70 (Fvco=3598.80, Nint=069, Nfrac=027) +Fch=899.80 (Fvco=3599.20, Nint=069, Nfrac=028) +Fch=899.90 (Fvco=3599.60, Nint=069, Nfrac=029) +Fch=900.00 (Fvco=3600.00, Nint=069, Nfrac=030) +Fch=900.10 (Fvco=3600.40, Nint=069, Nfrac=031) +Fch=900.20 (Fvco=3600.80, Nint=069, Nfrac=032) +Fch=900.30 (Fvco=3601.20, Nint=069, Nfrac=033) +Fch=900.40 (Fvco=3601.60, Nint=069, Nfrac=034) +Fch=900.50 (Fvco=3602.00, Nint=069, Nfrac=035) +Fch=900.60 (Fvco=3602.40, Nint=069, Nfrac=036) +Fch=900.70 (Fvco=3602.80, Nint=069, Nfrac=037) +Fch=900.80 (Fvco=3603.20, Nint=069, Nfrac=038) +Fch=900.90 (Fvco=3603.60, Nint=069, Nfrac=039) +Fch=901.00 (Fvco=3604.00, Nint=069, Nfrac=040) +Fch=901.10 (Fvco=3604.40, Nint=069, Nfrac=041) +Fch=901.20 (Fvco=3604.80, Nint=069, Nfrac=042) +Fch=901.30 (Fvco=3605.20, Nint=069, Nfrac=043) +Fch=901.40 (Fvco=3605.60, Nint=069, Nfrac=044) +Fch=901.50 (Fvco=3606.00, Nint=069, Nfrac=045) +Fch=901.60 (Fvco=3606.40, Nint=069, Nfrac=046) +Fch=901.70 (Fvco=3606.80, Nint=069, Nfrac=047) +Fch=901.80 (Fvco=3607.20, Nint=069, Nfrac=048) +Fch=901.90 (Fvco=3607.60, Nint=069, Nfrac=049) +Fch=902.00 (Fvco=3608.00, Nint=069, Nfrac=050) +Fch=902.10 (Fvco=3608.40, Nint=069, Nfrac=051) +Fch=902.20 (Fvco=3608.80, Nint=069, Nfrac=052) +Fch=902.30 (Fvco=3609.20, Nint=069, Nfrac=053) +Fch=902.40 (Fvco=3609.60, Nint=069, Nfrac=054) +Fch=902.50 (Fvco=3610.00, Nint=069, Nfrac=055) +Fch=902.60 (Fvco=3610.40, Nint=069, Nfrac=056) +Fch=902.70 (Fvco=3610.80, Nint=069, Nfrac=057) +Fch=902.80 (Fvco=3611.20, Nint=069, Nfrac=058) +Fch=902.90 (Fvco=3611.60, Nint=069, Nfrac=059) +Fch=903.00 (Fvco=3612.00, Nint=069, Nfrac=060) +Fch=903.10 (Fvco=3612.40, Nint=069, Nfrac=061) +Fch=903.20 (Fvco=3612.80, Nint=069, Nfrac=062) +Fch=903.30 (Fvco=3613.20, Nint=069, Nfrac=063) +Fch=903.40 (Fvco=3613.60, Nint=069, Nfrac=064) +Fch=903.50 (Fvco=3614.00, Nint=069, Nfrac=065) +Fch=903.60 (Fvco=3614.40, Nint=069, Nfrac=066) +Fch=903.70 (Fvco=3614.80, Nint=069, Nfrac=067) +Fch=903.80 (Fvco=3615.20, Nint=069, Nfrac=068) +Fch=903.90 (Fvco=3615.60, Nint=069, Nfrac=069) +Fch=904.00 (Fvco=3616.00, Nint=069, Nfrac=070) +Fch=904.10 (Fvco=3616.40, Nint=069, Nfrac=071) +Fch=904.20 (Fvco=3616.80, Nint=069, Nfrac=072) +Fch=904.30 (Fvco=3617.20, Nint=069, Nfrac=073) +Fch=904.40 (Fvco=3617.60, Nint=069, Nfrac=074) +Fch=904.50 (Fvco=3618.00, Nint=069, Nfrac=075) +Fch=904.60 (Fvco=3618.40, Nint=069, Nfrac=076) +Fch=904.70 (Fvco=3618.80, Nint=069, Nfrac=077) +Fch=904.80 (Fvco=3619.20, Nint=069, Nfrac=078) +Fch=904.90 (Fvco=3619.60, Nint=069, Nfrac=079) +Fch=905.00 (Fvco=3620.00, Nint=069, Nfrac=080) +Fch=905.10 (Fvco=3620.40, Nint=069, Nfrac=081) +Fch=905.20 (Fvco=3620.80, Nint=069, Nfrac=082) +Fch=905.30 (Fvco=3621.20, Nint=069, Nfrac=083) +Fch=905.40 (Fvco=3621.60, Nint=069, Nfrac=084) +Fch=905.50 (Fvco=3622.00, Nint=069, Nfrac=085) +Fch=905.60 (Fvco=3622.40, Nint=069, Nfrac=086) +Fch=905.70 (Fvco=3622.80, Nint=069, Nfrac=087) +Fch=905.80 (Fvco=3623.20, Nint=069, Nfrac=088) +Fch=905.90 (Fvco=3623.60, Nint=069, Nfrac=089) +Fch=906.00 (Fvco=3624.00, Nint=069, Nfrac=090) +Fch=906.10 (Fvco=3624.40, Nint=069, Nfrac=091) +Fch=906.20 (Fvco=3624.80, Nint=069, Nfrac=092) +Fch=906.30 (Fvco=3625.20, Nint=069, Nfrac=093) +Fch=906.40 (Fvco=3625.60, Nint=069, Nfrac=094) +Fch=906.50 (Fvco=3626.00, Nint=069, Nfrac=095) +Fch=906.60 (Fvco=3626.40, Nint=069, Nfrac=096) +Fch=906.70 (Fvco=3626.80, Nint=069, Nfrac=097) +Fch=906.80 (Fvco=3627.20, Nint=069, Nfrac=098) +Fch=906.90 (Fvco=3627.60, Nint=069, Nfrac=099) +Fch=907.00 (Fvco=3628.00, Nint=069, Nfrac=100) +Fch=907.10 (Fvco=3628.40, Nint=069, Nfrac=101) +Fch=907.20 (Fvco=3628.80, Nint=069, Nfrac=102) +Fch=907.30 (Fvco=3629.20, Nint=069, Nfrac=103) +Fch=907.40 (Fvco=3629.60, Nint=069, Nfrac=104) +Fch=907.50 (Fvco=3630.00, Nint=069, Nfrac=105) +Fch=907.60 (Fvco=3630.40, Nint=069, Nfrac=106) +Fch=907.70 (Fvco=3630.80, Nint=069, Nfrac=107) +Fch=907.80 (Fvco=3631.20, Nint=069, Nfrac=108) +Fch=907.90 (Fvco=3631.60, Nint=069, Nfrac=109) +Fch=908.00 (Fvco=3632.00, Nint=069, Nfrac=110) +Fch=908.10 (Fvco=3632.40, Nint=069, Nfrac=111) +Fch=908.20 (Fvco=3632.80, Nint=069, Nfrac=112) +Fch=908.30 (Fvco=3633.20, Nint=069, Nfrac=113) +Fch=908.40 (Fvco=3633.60, Nint=069, Nfrac=114) +Fch=908.50 (Fvco=3634.00, Nint=069, Nfrac=115) +Fch=908.60 (Fvco=3634.40, Nint=069, Nfrac=116) +Fch=908.70 (Fvco=3634.80, Nint=069, Nfrac=117) +Fch=908.80 (Fvco=3635.20, Nint=069, Nfrac=118) +Fch=908.90 (Fvco=3635.60, Nint=069, Nfrac=119) +Fch=909.00 (Fvco=3636.00, Nint=069, Nfrac=120) +Fch=909.10 (Fvco=3636.40, Nint=069, Nfrac=121) +Fch=909.20 (Fvco=3636.80, Nint=069, Nfrac=122) +Fch=909.30 (Fvco=3637.20, Nint=069, Nfrac=123) +Fch=909.40 (Fvco=3637.60, Nint=069, Nfrac=124) +Fch=909.50 (Fvco=3638.00, Nint=069, Nfrac=125) +Fch=909.60 (Fvco=3638.40, Nint=069, Nfrac=126) +Fch=909.70 (Fvco=3638.80, Nint=069, Nfrac=127) +Fch=909.80 (Fvco=3639.20, Nint=069, Nfrac=128) +Fch=909.90 (Fvco=3639.60, Nint=069, Nfrac=129) +Fch=910.00 (Fvco=3640.00, Nint=069, Nfrac=130) +Fch=910.00 (Fvco=3640.00, Nint=070, Nfrac=000) +Fch=910.10 (Fvco=3640.40, Nint=070, Nfrac=001) +Fch=910.20 (Fvco=3640.80, Nint=070, Nfrac=002) +Fch=910.30 (Fvco=3641.20, Nint=070, Nfrac=003) +Fch=910.40 (Fvco=3641.60, Nint=070, Nfrac=004) +Fch=910.50 (Fvco=3642.00, Nint=070, Nfrac=005) +Fch=910.60 (Fvco=3642.40, Nint=070, Nfrac=006) +Fch=910.70 (Fvco=3642.80, Nint=070, Nfrac=007) +Fch=910.80 (Fvco=3643.20, Nint=070, Nfrac=008) +Fch=910.90 (Fvco=3643.60, Nint=070, Nfrac=009) +Fch=911.00 (Fvco=3644.00, Nint=070, Nfrac=010) +Fch=911.10 (Fvco=3644.40, Nint=070, Nfrac=011) +Fch=911.20 (Fvco=3644.80, Nint=070, Nfrac=012) +Fch=911.30 (Fvco=3645.20, Nint=070, Nfrac=013) +Fch=911.40 (Fvco=3645.60, Nint=070, Nfrac=014) +Fch=911.50 (Fvco=3646.00, Nint=070, Nfrac=015) +Fch=911.60 (Fvco=3646.40, Nint=070, Nfrac=016) +Fch=911.70 (Fvco=3646.80, Nint=070, Nfrac=017) +Fch=911.80 (Fvco=3647.20, Nint=070, Nfrac=018) +Fch=911.90 (Fvco=3647.60, Nint=070, Nfrac=019) +Fch=912.00 (Fvco=3648.00, Nint=070, Nfrac=020) +Fch=912.10 (Fvco=3648.40, Nint=070, Nfrac=021) +Fch=912.20 (Fvco=3648.80, Nint=070, Nfrac=022) +Fch=912.30 (Fvco=3649.20, Nint=070, Nfrac=023) +Fch=912.40 (Fvco=3649.60, Nint=070, Nfrac=024) +Fch=912.50 (Fvco=3650.00, Nint=070, Nfrac=025) +Fch=912.60 (Fvco=3650.40, Nint=070, Nfrac=026) +Fch=912.70 (Fvco=3650.80, Nint=070, Nfrac=027) +Fch=912.80 (Fvco=3651.20, Nint=070, Nfrac=028) +Fch=912.90 (Fvco=3651.60, Nint=070, Nfrac=029) +Fch=913.00 (Fvco=3652.00, Nint=070, Nfrac=030) +Fch=913.10 (Fvco=3652.40, Nint=070, Nfrac=031) +Fch=913.20 (Fvco=3652.80, Nint=070, Nfrac=032) +Fch=913.30 (Fvco=3653.20, Nint=070, Nfrac=033) +Fch=913.40 (Fvco=3653.60, Nint=070, Nfrac=034) +Fch=913.50 (Fvco=3654.00, Nint=070, Nfrac=035) +Fch=913.60 (Fvco=3654.40, Nint=070, Nfrac=036) +Fch=913.70 (Fvco=3654.80, Nint=070, Nfrac=037) +Fch=913.80 (Fvco=3655.20, Nint=070, Nfrac=038) +Fch=913.90 (Fvco=3655.60, Nint=070, Nfrac=039) +Fch=914.00 (Fvco=3656.00, Nint=070, Nfrac=040) +Fch=914.10 (Fvco=3656.40, Nint=070, Nfrac=041) +Fch=914.20 (Fvco=3656.80, Nint=070, Nfrac=042) +Fch=914.30 (Fvco=3657.20, Nint=070, Nfrac=043) +Fch=914.40 (Fvco=3657.60, Nint=070, Nfrac=044) +Fch=914.50 (Fvco=3658.00, Nint=070, Nfrac=045) +Fch=914.60 (Fvco=3658.40, Nint=070, Nfrac=046) +Fch=914.70 (Fvco=3658.80, Nint=070, Nfrac=047) +Fch=914.80 (Fvco=3659.20, Nint=070, Nfrac=048) +Fch=914.90 (Fvco=3659.60, Nint=070, Nfrac=049) +Fch=915.00 (Fvco=3660.00, Nint=070, Nfrac=050) +Fch=915.10 (Fvco=3660.40, Nint=070, Nfrac=051) +Fch=915.20 (Fvco=3660.80, Nint=070, Nfrac=052) +Fch=915.30 (Fvco=3661.20, Nint=070, Nfrac=053) +Fch=915.40 (Fvco=3661.60, Nint=070, Nfrac=054) +Fch=915.50 (Fvco=3662.00, Nint=070, Nfrac=055) +Fch=915.60 (Fvco=3662.40, Nint=070, Nfrac=056) +Fch=915.70 (Fvco=3662.80, Nint=070, Nfrac=057) +Fch=915.80 (Fvco=3663.20, Nint=070, Nfrac=058) +Fch=915.90 (Fvco=3663.60, Nint=070, Nfrac=059) +Fch=916.00 (Fvco=3664.00, Nint=070, Nfrac=060) +Fch=916.10 (Fvco=3664.40, Nint=070, Nfrac=061) +Fch=916.20 (Fvco=3664.80, Nint=070, Nfrac=062) +Fch=916.30 (Fvco=3665.20, Nint=070, Nfrac=063) +Fch=916.40 (Fvco=3665.60, Nint=070, Nfrac=064) +Fch=916.50 (Fvco=3666.00, Nint=070, Nfrac=065) +Fch=916.60 (Fvco=3666.40, Nint=070, Nfrac=066) +Fch=916.70 (Fvco=3666.80, Nint=070, Nfrac=067) +Fch=916.80 (Fvco=3667.20, Nint=070, Nfrac=068) +Fch=916.90 (Fvco=3667.60, Nint=070, Nfrac=069) +Fch=917.00 (Fvco=3668.00, Nint=070, Nfrac=070) +Fch=917.10 (Fvco=3668.40, Nint=070, Nfrac=071) +Fch=917.20 (Fvco=3668.80, Nint=070, Nfrac=072) +Fch=917.30 (Fvco=3669.20, Nint=070, Nfrac=073) +Fch=917.40 (Fvco=3669.60, Nint=070, Nfrac=074) +Fch=917.50 (Fvco=3670.00, Nint=070, Nfrac=075) +Fch=917.60 (Fvco=3670.40, Nint=070, Nfrac=076) +Fch=917.70 (Fvco=3670.80, Nint=070, Nfrac=077) +Fch=917.80 (Fvco=3671.20, Nint=070, Nfrac=078) +Fch=917.90 (Fvco=3671.60, Nint=070, Nfrac=079) +Fch=918.00 (Fvco=3672.00, Nint=070, Nfrac=080) +Fch=918.10 (Fvco=3672.40, Nint=070, Nfrac=081) +Fch=918.20 (Fvco=3672.80, Nint=070, Nfrac=082) +Fch=918.30 (Fvco=3673.20, Nint=070, Nfrac=083) +Fch=918.40 (Fvco=3673.60, Nint=070, Nfrac=084) +Fch=918.50 (Fvco=3674.00, Nint=070, Nfrac=085) +Fch=918.60 (Fvco=3674.40, Nint=070, Nfrac=086) +Fch=918.70 (Fvco=3674.80, Nint=070, Nfrac=087) +Fch=918.80 (Fvco=3675.20, Nint=070, Nfrac=088) +Fch=918.90 (Fvco=3675.60, Nint=070, Nfrac=089) +Fch=919.00 (Fvco=3676.00, Nint=070, Nfrac=090) +Fch=919.10 (Fvco=3676.40, Nint=070, Nfrac=091) +Fch=919.20 (Fvco=3676.80, Nint=070, Nfrac=092) +Fch=919.30 (Fvco=3677.20, Nint=070, Nfrac=093) +Fch=919.40 (Fvco=3677.60, Nint=070, Nfrac=094) +Fch=919.50 (Fvco=3678.00, Nint=070, Nfrac=095) +Fch=919.60 (Fvco=3678.40, Nint=070, Nfrac=096) +Fch=919.70 (Fvco=3678.80, Nint=070, Nfrac=097) +Fch=919.80 (Fvco=3679.20, Nint=070, Nfrac=098) +Fch=919.90 (Fvco=3679.60, Nint=070, Nfrac=099) +Fch=920.00 (Fvco=3680.00, Nint=070, Nfrac=100) +Fch=920.10 (Fvco=3680.40, Nint=070, Nfrac=101) +Fch=920.20 (Fvco=3680.80, Nint=070, Nfrac=102) +Fch=920.30 (Fvco=3681.20, Nint=070, Nfrac=103) +Fch=920.40 (Fvco=3681.60, Nint=070, Nfrac=104) +Fch=920.50 (Fvco=3682.00, Nint=070, Nfrac=105) +Fch=920.60 (Fvco=3682.40, Nint=070, Nfrac=106) +Fch=920.70 (Fvco=3682.80, Nint=070, Nfrac=107) +Fch=920.80 (Fvco=3683.20, Nint=070, Nfrac=108) +Fch=920.90 (Fvco=3683.60, Nint=070, Nfrac=109) +Fch=921.00 (Fvco=3684.00, Nint=070, Nfrac=110) +Fch=921.10 (Fvco=3684.40, Nint=070, Nfrac=111) +Fch=921.20 (Fvco=3684.80, Nint=070, Nfrac=112) +Fch=921.30 (Fvco=3685.20, Nint=070, Nfrac=113) +Fch=921.40 (Fvco=3685.60, Nint=070, Nfrac=114) +Fch=921.50 (Fvco=3686.00, Nint=070, Nfrac=115) +Fch=921.60 (Fvco=3686.40, Nint=070, Nfrac=116) +Fch=921.70 (Fvco=3686.80, Nint=070, Nfrac=117) +Fch=921.80 (Fvco=3687.20, Nint=070, Nfrac=118) +Fch=921.90 (Fvco=3687.60, Nint=070, Nfrac=119) +Fch=922.00 (Fvco=3688.00, Nint=070, Nfrac=120) +Fch=922.10 (Fvco=3688.40, Nint=070, Nfrac=121) +Fch=922.20 (Fvco=3688.80, Nint=070, Nfrac=122) +Fch=922.30 (Fvco=3689.20, Nint=070, Nfrac=123) +Fch=922.40 (Fvco=3689.60, Nint=070, Nfrac=124) +Fch=922.50 (Fvco=3690.00, Nint=070, Nfrac=125) +Fch=922.60 (Fvco=3690.40, Nint=070, Nfrac=126) +Fch=922.70 (Fvco=3690.80, Nint=070, Nfrac=127) +Fch=922.80 (Fvco=3691.20, Nint=070, Nfrac=128) +Fch=922.90 (Fvco=3691.60, Nint=070, Nfrac=129) +Fch=923.00 (Fvco=3692.00, Nint=070, Nfrac=130) +====================================================================== +PLL Tx High Band +Fch=1690.00 (Fvco=3380.00, Nint=065, Nfrac=000) +Fch=1690.20 (Fvco=3380.40, Nint=065, Nfrac=001) +Fch=1690.40 (Fvco=3380.80, Nint=065, Nfrac=002) +Fch=1690.60 (Fvco=3381.20, Nint=065, Nfrac=003) +Fch=1690.80 (Fvco=3381.60, Nint=065, Nfrac=004) +Fch=1691.00 (Fvco=3382.00, Nint=065, Nfrac=005) +Fch=1691.20 (Fvco=3382.40, Nint=065, Nfrac=006) +Fch=1691.40 (Fvco=3382.80, Nint=065, Nfrac=007) +Fch=1691.60 (Fvco=3383.20, Nint=065, Nfrac=008) +Fch=1691.80 (Fvco=3383.60, Nint=065, Nfrac=009) +Fch=1692.00 (Fvco=3384.00, Nint=065, Nfrac=010) +Fch=1692.20 (Fvco=3384.40, Nint=065, Nfrac=011) +Fch=1692.40 (Fvco=3384.80, Nint=065, Nfrac=012) +Fch=1692.60 (Fvco=3385.20, Nint=065, Nfrac=013) +Fch=1692.80 (Fvco=3385.60, Nint=065, Nfrac=014) +Fch=1693.00 (Fvco=3386.00, Nint=065, Nfrac=015) +Fch=1693.20 (Fvco=3386.40, Nint=065, Nfrac=016) +Fch=1693.40 (Fvco=3386.80, Nint=065, Nfrac=017) +Fch=1693.60 (Fvco=3387.20, Nint=065, Nfrac=018) +Fch=1693.80 (Fvco=3387.60, Nint=065, Nfrac=019) +Fch=1694.00 (Fvco=3388.00, Nint=065, Nfrac=020) +Fch=1694.20 (Fvco=3388.40, Nint=065, Nfrac=021) +Fch=1694.40 (Fvco=3388.80, Nint=065, Nfrac=022) +Fch=1694.60 (Fvco=3389.20, Nint=065, Nfrac=023) +Fch=1694.80 (Fvco=3389.60, Nint=065, Nfrac=024) +Fch=1695.00 (Fvco=3390.00, Nint=065, Nfrac=025) +Fch=1695.20 (Fvco=3390.40, Nint=065, Nfrac=026) +Fch=1695.40 (Fvco=3390.80, Nint=065, Nfrac=027) +Fch=1695.60 (Fvco=3391.20, Nint=065, Nfrac=028) +Fch=1695.80 (Fvco=3391.60, Nint=065, Nfrac=029) +Fch=1696.00 (Fvco=3392.00, Nint=065, Nfrac=030) +Fch=1696.20 (Fvco=3392.40, Nint=065, Nfrac=031) +Fch=1696.40 (Fvco=3392.80, Nint=065, Nfrac=032) +Fch=1696.60 (Fvco=3393.20, Nint=065, Nfrac=033) +Fch=1696.80 (Fvco=3393.60, Nint=065, Nfrac=034) +Fch=1697.00 (Fvco=3394.00, Nint=065, Nfrac=035) +Fch=1697.20 (Fvco=3394.40, Nint=065, Nfrac=036) +Fch=1697.40 (Fvco=3394.80, Nint=065, Nfrac=037) +Fch=1697.60 (Fvco=3395.20, Nint=065, Nfrac=038) +Fch=1697.80 (Fvco=3395.60, Nint=065, Nfrac=039) +Fch=1698.00 (Fvco=3396.00, Nint=065, Nfrac=040) +Fch=1698.20 (Fvco=3396.40, Nint=065, Nfrac=041) +Fch=1698.40 (Fvco=3396.80, Nint=065, Nfrac=042) +Fch=1698.60 (Fvco=3397.20, Nint=065, Nfrac=043) +Fch=1698.80 (Fvco=3397.60, Nint=065, Nfrac=044) +Fch=1699.00 (Fvco=3398.00, Nint=065, Nfrac=045) +Fch=1699.20 (Fvco=3398.40, Nint=065, Nfrac=046) +Fch=1699.40 (Fvco=3398.80, Nint=065, Nfrac=047) +Fch=1699.60 (Fvco=3399.20, Nint=065, Nfrac=048) +Fch=1699.80 (Fvco=3399.60, Nint=065, Nfrac=049) +Fch=1700.00 (Fvco=3400.00, Nint=065, Nfrac=050) +Fch=1700.20 (Fvco=3400.40, Nint=065, Nfrac=051) +Fch=1700.40 (Fvco=3400.80, Nint=065, Nfrac=052) +Fch=1700.60 (Fvco=3401.20, Nint=065, Nfrac=053) +Fch=1700.80 (Fvco=3401.60, Nint=065, Nfrac=054) +Fch=1701.00 (Fvco=3402.00, Nint=065, Nfrac=055) +Fch=1701.20 (Fvco=3402.40, Nint=065, Nfrac=056) +Fch=1701.40 (Fvco=3402.80, Nint=065, Nfrac=057) +Fch=1701.60 (Fvco=3403.20, Nint=065, Nfrac=058) +Fch=1701.80 (Fvco=3403.60, Nint=065, Nfrac=059) +Fch=1702.00 (Fvco=3404.00, Nint=065, Nfrac=060) +Fch=1702.20 (Fvco=3404.40, Nint=065, Nfrac=061) +Fch=1702.40 (Fvco=3404.80, Nint=065, Nfrac=062) +Fch=1702.60 (Fvco=3405.20, Nint=065, Nfrac=063) +Fch=1702.80 (Fvco=3405.60, Nint=065, Nfrac=064) +Fch=1703.00 (Fvco=3406.00, Nint=065, Nfrac=065) +Fch=1703.20 (Fvco=3406.40, Nint=065, Nfrac=066) +Fch=1703.40 (Fvco=3406.80, Nint=065, Nfrac=067) +Fch=1703.60 (Fvco=3407.20, Nint=065, Nfrac=068) +Fch=1703.80 (Fvco=3407.60, Nint=065, Nfrac=069) +Fch=1704.00 (Fvco=3408.00, Nint=065, Nfrac=070) +Fch=1704.20 (Fvco=3408.40, Nint=065, Nfrac=071) +Fch=1704.40 (Fvco=3408.80, Nint=065, Nfrac=072) +Fch=1704.60 (Fvco=3409.20, Nint=065, Nfrac=073) +Fch=1704.80 (Fvco=3409.60, Nint=065, Nfrac=074) +Fch=1705.00 (Fvco=3410.00, Nint=065, Nfrac=075) +Fch=1705.20 (Fvco=3410.40, Nint=065, Nfrac=076) +Fch=1705.40 (Fvco=3410.80, Nint=065, Nfrac=077) +Fch=1705.60 (Fvco=3411.20, Nint=065, Nfrac=078) +Fch=1705.80 (Fvco=3411.60, Nint=065, Nfrac=079) +Fch=1706.00 (Fvco=3412.00, Nint=065, Nfrac=080) +Fch=1706.20 (Fvco=3412.40, Nint=065, Nfrac=081) +Fch=1706.40 (Fvco=3412.80, Nint=065, Nfrac=082) +Fch=1706.60 (Fvco=3413.20, Nint=065, Nfrac=083) +Fch=1706.80 (Fvco=3413.60, Nint=065, Nfrac=084) +Fch=1707.00 (Fvco=3414.00, Nint=065, Nfrac=085) +Fch=1707.20 (Fvco=3414.40, Nint=065, Nfrac=086) +Fch=1707.40 (Fvco=3414.80, Nint=065, Nfrac=087) +Fch=1707.60 (Fvco=3415.20, Nint=065, Nfrac=088) +Fch=1707.80 (Fvco=3415.60, Nint=065, Nfrac=089) +Fch=1708.00 (Fvco=3416.00, Nint=065, Nfrac=090) +Fch=1708.20 (Fvco=3416.40, Nint=065, Nfrac=091) +Fch=1708.40 (Fvco=3416.80, Nint=065, Nfrac=092) +Fch=1708.60 (Fvco=3417.20, Nint=065, Nfrac=093) +Fch=1708.80 (Fvco=3417.60, Nint=065, Nfrac=094) +Fch=1709.00 (Fvco=3418.00, Nint=065, Nfrac=095) +Fch=1709.20 (Fvco=3418.40, Nint=065, Nfrac=096) +Fch=1709.40 (Fvco=3418.80, Nint=065, Nfrac=097) +Fch=1709.60 (Fvco=3419.20, Nint=065, Nfrac=098) +Fch=1709.80 (Fvco=3419.60, Nint=065, Nfrac=099) +Fch=1710.00 (Fvco=3420.00, Nint=065, Nfrac=100) +Fch=1710.20 (Fvco=3420.40, Nint=065, Nfrac=101) +Fch=1710.40 (Fvco=3420.80, Nint=065, Nfrac=102) +Fch=1710.60 (Fvco=3421.20, Nint=065, Nfrac=103) +Fch=1710.80 (Fvco=3421.60, Nint=065, Nfrac=104) +Fch=1711.00 (Fvco=3422.00, Nint=065, Nfrac=105) +Fch=1711.20 (Fvco=3422.40, Nint=065, Nfrac=106) +Fch=1711.40 (Fvco=3422.80, Nint=065, Nfrac=107) +Fch=1711.60 (Fvco=3423.20, Nint=065, Nfrac=108) +Fch=1711.80 (Fvco=3423.60, Nint=065, Nfrac=109) +Fch=1712.00 (Fvco=3424.00, Nint=065, Nfrac=110) +Fch=1712.20 (Fvco=3424.40, Nint=065, Nfrac=111) +Fch=1712.40 (Fvco=3424.80, Nint=065, Nfrac=112) +Fch=1712.60 (Fvco=3425.20, Nint=065, Nfrac=113) +Fch=1712.80 (Fvco=3425.60, Nint=065, Nfrac=114) +Fch=1713.00 (Fvco=3426.00, Nint=065, Nfrac=115) +Fch=1713.20 (Fvco=3426.40, Nint=065, Nfrac=116) +Fch=1713.40 (Fvco=3426.80, Nint=065, Nfrac=117) +Fch=1713.60 (Fvco=3427.20, Nint=065, Nfrac=118) +Fch=1713.80 (Fvco=3427.60, Nint=065, Nfrac=119) +Fch=1714.00 (Fvco=3428.00, Nint=065, Nfrac=120) +Fch=1714.20 (Fvco=3428.40, Nint=065, Nfrac=121) +Fch=1714.40 (Fvco=3428.80, Nint=065, Nfrac=122) +Fch=1714.60 (Fvco=3429.20, Nint=065, Nfrac=123) +Fch=1714.80 (Fvco=3429.60, Nint=065, Nfrac=124) +Fch=1715.00 (Fvco=3430.00, Nint=065, Nfrac=125) +Fch=1715.20 (Fvco=3430.40, Nint=065, Nfrac=126) +Fch=1715.40 (Fvco=3430.80, Nint=065, Nfrac=127) +Fch=1715.60 (Fvco=3431.20, Nint=065, Nfrac=128) +Fch=1715.80 (Fvco=3431.60, Nint=065, Nfrac=129) +Fch=1716.00 (Fvco=3432.00, Nint=065, Nfrac=130) +Fch=1716.00 (Fvco=3432.00, Nint=066, Nfrac=000) +Fch=1716.20 (Fvco=3432.40, Nint=066, Nfrac=001) +Fch=1716.40 (Fvco=3432.80, Nint=066, Nfrac=002) +Fch=1716.60 (Fvco=3433.20, Nint=066, Nfrac=003) +Fch=1716.80 (Fvco=3433.60, Nint=066, Nfrac=004) +Fch=1717.00 (Fvco=3434.00, Nint=066, Nfrac=005) +Fch=1717.20 (Fvco=3434.40, Nint=066, Nfrac=006) +Fch=1717.40 (Fvco=3434.80, Nint=066, Nfrac=007) +Fch=1717.60 (Fvco=3435.20, Nint=066, Nfrac=008) +Fch=1717.80 (Fvco=3435.60, Nint=066, Nfrac=009) +Fch=1718.00 (Fvco=3436.00, Nint=066, Nfrac=010) +Fch=1718.20 (Fvco=3436.40, Nint=066, Nfrac=011) +Fch=1718.40 (Fvco=3436.80, Nint=066, Nfrac=012) +Fch=1718.60 (Fvco=3437.20, Nint=066, Nfrac=013) +Fch=1718.80 (Fvco=3437.60, Nint=066, Nfrac=014) +Fch=1719.00 (Fvco=3438.00, Nint=066, Nfrac=015) +Fch=1719.20 (Fvco=3438.40, Nint=066, Nfrac=016) +Fch=1719.40 (Fvco=3438.80, Nint=066, Nfrac=017) +Fch=1719.60 (Fvco=3439.20, Nint=066, Nfrac=018) +Fch=1719.80 (Fvco=3439.60, Nint=066, Nfrac=019) +Fch=1720.00 (Fvco=3440.00, Nint=066, Nfrac=020) +Fch=1720.20 (Fvco=3440.40, Nint=066, Nfrac=021) +Fch=1720.40 (Fvco=3440.80, Nint=066, Nfrac=022) +Fch=1720.60 (Fvco=3441.20, Nint=066, Nfrac=023) +Fch=1720.80 (Fvco=3441.60, Nint=066, Nfrac=024) +Fch=1721.00 (Fvco=3442.00, Nint=066, Nfrac=025) +Fch=1721.20 (Fvco=3442.40, Nint=066, Nfrac=026) +Fch=1721.40 (Fvco=3442.80, Nint=066, Nfrac=027) +Fch=1721.60 (Fvco=3443.20, Nint=066, Nfrac=028) +Fch=1721.80 (Fvco=3443.60, Nint=066, Nfrac=029) +Fch=1722.00 (Fvco=3444.00, Nint=066, Nfrac=030) +Fch=1722.20 (Fvco=3444.40, Nint=066, Nfrac=031) +Fch=1722.40 (Fvco=3444.80, Nint=066, Nfrac=032) +Fch=1722.60 (Fvco=3445.20, Nint=066, Nfrac=033) +Fch=1722.80 (Fvco=3445.60, Nint=066, Nfrac=034) +Fch=1723.00 (Fvco=3446.00, Nint=066, Nfrac=035) +Fch=1723.20 (Fvco=3446.40, Nint=066, Nfrac=036) +Fch=1723.40 (Fvco=3446.80, Nint=066, Nfrac=037) +Fch=1723.60 (Fvco=3447.20, Nint=066, Nfrac=038) +Fch=1723.80 (Fvco=3447.60, Nint=066, Nfrac=039) +Fch=1724.00 (Fvco=3448.00, Nint=066, Nfrac=040) +Fch=1724.20 (Fvco=3448.40, Nint=066, Nfrac=041) +Fch=1724.40 (Fvco=3448.80, Nint=066, Nfrac=042) +Fch=1724.60 (Fvco=3449.20, Nint=066, Nfrac=043) +Fch=1724.80 (Fvco=3449.60, Nint=066, Nfrac=044) +Fch=1725.00 (Fvco=3450.00, Nint=066, Nfrac=045) +Fch=1725.20 (Fvco=3450.40, Nint=066, Nfrac=046) +Fch=1725.40 (Fvco=3450.80, Nint=066, Nfrac=047) +Fch=1725.60 (Fvco=3451.20, Nint=066, Nfrac=048) +Fch=1725.80 (Fvco=3451.60, Nint=066, Nfrac=049) +Fch=1726.00 (Fvco=3452.00, Nint=066, Nfrac=050) +Fch=1726.20 (Fvco=3452.40, Nint=066, Nfrac=051) +Fch=1726.40 (Fvco=3452.80, Nint=066, Nfrac=052) +Fch=1726.60 (Fvco=3453.20, Nint=066, Nfrac=053) +Fch=1726.80 (Fvco=3453.60, Nint=066, Nfrac=054) +Fch=1727.00 (Fvco=3454.00, Nint=066, Nfrac=055) +Fch=1727.20 (Fvco=3454.40, Nint=066, Nfrac=056) +Fch=1727.40 (Fvco=3454.80, Nint=066, Nfrac=057) +Fch=1727.60 (Fvco=3455.20, Nint=066, Nfrac=058) +Fch=1727.80 (Fvco=3455.60, Nint=066, Nfrac=059) +Fch=1728.00 (Fvco=3456.00, Nint=066, Nfrac=060) +Fch=1728.20 (Fvco=3456.40, Nint=066, Nfrac=061) +Fch=1728.40 (Fvco=3456.80, Nint=066, Nfrac=062) +Fch=1728.60 (Fvco=3457.20, Nint=066, Nfrac=063) +Fch=1728.80 (Fvco=3457.60, Nint=066, Nfrac=064) +Fch=1729.00 (Fvco=3458.00, Nint=066, Nfrac=065) +Fch=1729.20 (Fvco=3458.40, Nint=066, Nfrac=066) +Fch=1729.40 (Fvco=3458.80, Nint=066, Nfrac=067) +Fch=1729.60 (Fvco=3459.20, Nint=066, Nfrac=068) +Fch=1729.80 (Fvco=3459.60, Nint=066, Nfrac=069) +Fch=1730.00 (Fvco=3460.00, Nint=066, Nfrac=070) +Fch=1730.20 (Fvco=3460.40, Nint=066, Nfrac=071) +Fch=1730.40 (Fvco=3460.80, Nint=066, Nfrac=072) +Fch=1730.60 (Fvco=3461.20, Nint=066, Nfrac=073) +Fch=1730.80 (Fvco=3461.60, Nint=066, Nfrac=074) +Fch=1731.00 (Fvco=3462.00, Nint=066, Nfrac=075) +Fch=1731.20 (Fvco=3462.40, Nint=066, Nfrac=076) +Fch=1731.40 (Fvco=3462.80, Nint=066, Nfrac=077) +Fch=1731.60 (Fvco=3463.20, Nint=066, Nfrac=078) +Fch=1731.80 (Fvco=3463.60, Nint=066, Nfrac=079) +Fch=1732.00 (Fvco=3464.00, Nint=066, Nfrac=080) +Fch=1732.20 (Fvco=3464.40, Nint=066, Nfrac=081) +Fch=1732.40 (Fvco=3464.80, Nint=066, Nfrac=082) +Fch=1732.60 (Fvco=3465.20, Nint=066, Nfrac=083) +Fch=1732.80 (Fvco=3465.60, Nint=066, Nfrac=084) +Fch=1733.00 (Fvco=3466.00, Nint=066, Nfrac=085) +Fch=1733.20 (Fvco=3466.40, Nint=066, Nfrac=086) +Fch=1733.40 (Fvco=3466.80, Nint=066, Nfrac=087) +Fch=1733.60 (Fvco=3467.20, Nint=066, Nfrac=088) +Fch=1733.80 (Fvco=3467.60, Nint=066, Nfrac=089) +Fch=1734.00 (Fvco=3468.00, Nint=066, Nfrac=090) +Fch=1734.20 (Fvco=3468.40, Nint=066, Nfrac=091) +Fch=1734.40 (Fvco=3468.80, Nint=066, Nfrac=092) +Fch=1734.60 (Fvco=3469.20, Nint=066, Nfrac=093) +Fch=1734.80 (Fvco=3469.60, Nint=066, Nfrac=094) +Fch=1735.00 (Fvco=3470.00, Nint=066, Nfrac=095) +Fch=1735.20 (Fvco=3470.40, Nint=066, Nfrac=096) +Fch=1735.40 (Fvco=3470.80, Nint=066, Nfrac=097) +Fch=1735.60 (Fvco=3471.20, Nint=066, Nfrac=098) +Fch=1735.80 (Fvco=3471.60, Nint=066, Nfrac=099) +Fch=1736.00 (Fvco=3472.00, Nint=066, Nfrac=100) +Fch=1736.20 (Fvco=3472.40, Nint=066, Nfrac=101) +Fch=1736.40 (Fvco=3472.80, Nint=066, Nfrac=102) +Fch=1736.60 (Fvco=3473.20, Nint=066, Nfrac=103) +Fch=1736.80 (Fvco=3473.60, Nint=066, Nfrac=104) +Fch=1737.00 (Fvco=3474.00, Nint=066, Nfrac=105) +Fch=1737.20 (Fvco=3474.40, Nint=066, Nfrac=106) +Fch=1737.40 (Fvco=3474.80, Nint=066, Nfrac=107) +Fch=1737.60 (Fvco=3475.20, Nint=066, Nfrac=108) +Fch=1737.80 (Fvco=3475.60, Nint=066, Nfrac=109) +Fch=1738.00 (Fvco=3476.00, Nint=066, Nfrac=110) +Fch=1738.20 (Fvco=3476.40, Nint=066, Nfrac=111) +Fch=1738.40 (Fvco=3476.80, Nint=066, Nfrac=112) +Fch=1738.60 (Fvco=3477.20, Nint=066, Nfrac=113) +Fch=1738.80 (Fvco=3477.60, Nint=066, Nfrac=114) +Fch=1739.00 (Fvco=3478.00, Nint=066, Nfrac=115) +Fch=1739.20 (Fvco=3478.40, Nint=066, Nfrac=116) +Fch=1739.40 (Fvco=3478.80, Nint=066, Nfrac=117) +Fch=1739.60 (Fvco=3479.20, Nint=066, Nfrac=118) +Fch=1739.80 (Fvco=3479.60, Nint=066, Nfrac=119) +Fch=1740.00 (Fvco=3480.00, Nint=066, Nfrac=120) +Fch=1740.20 (Fvco=3480.40, Nint=066, Nfrac=121) +Fch=1740.40 (Fvco=3480.80, Nint=066, Nfrac=122) +Fch=1740.60 (Fvco=3481.20, Nint=066, Nfrac=123) +Fch=1740.80 (Fvco=3481.60, Nint=066, Nfrac=124) +Fch=1741.00 (Fvco=3482.00, Nint=066, Nfrac=125) +Fch=1741.20 (Fvco=3482.40, Nint=066, Nfrac=126) +Fch=1741.40 (Fvco=3482.80, Nint=066, Nfrac=127) +Fch=1741.60 (Fvco=3483.20, Nint=066, Nfrac=128) +Fch=1741.80 (Fvco=3483.60, Nint=066, Nfrac=129) +Fch=1742.00 (Fvco=3484.00, Nint=066, Nfrac=130) +Fch=1742.00 (Fvco=3484.00, Nint=067, Nfrac=000) +Fch=1742.20 (Fvco=3484.40, Nint=067, Nfrac=001) +Fch=1742.40 (Fvco=3484.80, Nint=067, Nfrac=002) +Fch=1742.60 (Fvco=3485.20, Nint=067, Nfrac=003) +Fch=1742.80 (Fvco=3485.60, Nint=067, Nfrac=004) +Fch=1743.00 (Fvco=3486.00, Nint=067, Nfrac=005) +Fch=1743.20 (Fvco=3486.40, Nint=067, Nfrac=006) +Fch=1743.40 (Fvco=3486.80, Nint=067, Nfrac=007) +Fch=1743.60 (Fvco=3487.20, Nint=067, Nfrac=008) +Fch=1743.80 (Fvco=3487.60, Nint=067, Nfrac=009) +Fch=1744.00 (Fvco=3488.00, Nint=067, Nfrac=010) +Fch=1744.20 (Fvco=3488.40, Nint=067, Nfrac=011) +Fch=1744.40 (Fvco=3488.80, Nint=067, Nfrac=012) +Fch=1744.60 (Fvco=3489.20, Nint=067, Nfrac=013) +Fch=1744.80 (Fvco=3489.60, Nint=067, Nfrac=014) +Fch=1745.00 (Fvco=3490.00, Nint=067, Nfrac=015) +Fch=1745.20 (Fvco=3490.40, Nint=067, Nfrac=016) +Fch=1745.40 (Fvco=3490.80, Nint=067, Nfrac=017) +Fch=1745.60 (Fvco=3491.20, Nint=067, Nfrac=018) +Fch=1745.80 (Fvco=3491.60, Nint=067, Nfrac=019) +Fch=1746.00 (Fvco=3492.00, Nint=067, Nfrac=020) +Fch=1746.20 (Fvco=3492.40, Nint=067, Nfrac=021) +Fch=1746.40 (Fvco=3492.80, Nint=067, Nfrac=022) +Fch=1746.60 (Fvco=3493.20, Nint=067, Nfrac=023) +Fch=1746.80 (Fvco=3493.60, Nint=067, Nfrac=024) +Fch=1747.00 (Fvco=3494.00, Nint=067, Nfrac=025) +Fch=1747.20 (Fvco=3494.40, Nint=067, Nfrac=026) +Fch=1747.40 (Fvco=3494.80, Nint=067, Nfrac=027) +Fch=1747.60 (Fvco=3495.20, Nint=067, Nfrac=028) +Fch=1747.80 (Fvco=3495.60, Nint=067, Nfrac=029) +Fch=1748.00 (Fvco=3496.00, Nint=067, Nfrac=030) +Fch=1748.20 (Fvco=3496.40, Nint=067, Nfrac=031) +Fch=1748.40 (Fvco=3496.80, Nint=067, Nfrac=032) +Fch=1748.60 (Fvco=3497.20, Nint=067, Nfrac=033) +Fch=1748.80 (Fvco=3497.60, Nint=067, Nfrac=034) +Fch=1749.00 (Fvco=3498.00, Nint=067, Nfrac=035) +Fch=1749.20 (Fvco=3498.40, Nint=067, Nfrac=036) +Fch=1749.40 (Fvco=3498.80, Nint=067, Nfrac=037) +Fch=1749.60 (Fvco=3499.20, Nint=067, Nfrac=038) +Fch=1749.80 (Fvco=3499.60, Nint=067, Nfrac=039) +Fch=1750.00 (Fvco=3500.00, Nint=067, Nfrac=040) +Fch=1750.20 (Fvco=3500.40, Nint=067, Nfrac=041) +Fch=1750.40 (Fvco=3500.80, Nint=067, Nfrac=042) +Fch=1750.60 (Fvco=3501.20, Nint=067, Nfrac=043) +Fch=1750.80 (Fvco=3501.60, Nint=067, Nfrac=044) +Fch=1751.00 (Fvco=3502.00, Nint=067, Nfrac=045) +Fch=1751.20 (Fvco=3502.40, Nint=067, Nfrac=046) +Fch=1751.40 (Fvco=3502.80, Nint=067, Nfrac=047) +Fch=1751.60 (Fvco=3503.20, Nint=067, Nfrac=048) +Fch=1751.80 (Fvco=3503.60, Nint=067, Nfrac=049) +Fch=1752.00 (Fvco=3504.00, Nint=067, Nfrac=050) +Fch=1752.20 (Fvco=3504.40, Nint=067, Nfrac=051) +Fch=1752.40 (Fvco=3504.80, Nint=067, Nfrac=052) +Fch=1752.60 (Fvco=3505.20, Nint=067, Nfrac=053) +Fch=1752.80 (Fvco=3505.60, Nint=067, Nfrac=054) +Fch=1753.00 (Fvco=3506.00, Nint=067, Nfrac=055) +Fch=1753.20 (Fvco=3506.40, Nint=067, Nfrac=056) +Fch=1753.40 (Fvco=3506.80, Nint=067, Nfrac=057) +Fch=1753.60 (Fvco=3507.20, Nint=067, Nfrac=058) +Fch=1753.80 (Fvco=3507.60, Nint=067, Nfrac=059) +Fch=1754.00 (Fvco=3508.00, Nint=067, Nfrac=060) +Fch=1754.20 (Fvco=3508.40, Nint=067, Nfrac=061) +Fch=1754.40 (Fvco=3508.80, Nint=067, Nfrac=062) +Fch=1754.60 (Fvco=3509.20, Nint=067, Nfrac=063) +Fch=1754.80 (Fvco=3509.60, Nint=067, Nfrac=064) +Fch=1755.00 (Fvco=3510.00, Nint=067, Nfrac=065) +Fch=1755.20 (Fvco=3510.40, Nint=067, Nfrac=066) +Fch=1755.40 (Fvco=3510.80, Nint=067, Nfrac=067) +Fch=1755.60 (Fvco=3511.20, Nint=067, Nfrac=068) +Fch=1755.80 (Fvco=3511.60, Nint=067, Nfrac=069) +Fch=1756.00 (Fvco=3512.00, Nint=067, Nfrac=070) +Fch=1756.20 (Fvco=3512.40, Nint=067, Nfrac=071) +Fch=1756.40 (Fvco=3512.80, Nint=067, Nfrac=072) +Fch=1756.60 (Fvco=3513.20, Nint=067, Nfrac=073) +Fch=1756.80 (Fvco=3513.60, Nint=067, Nfrac=074) +Fch=1757.00 (Fvco=3514.00, Nint=067, Nfrac=075) +Fch=1757.20 (Fvco=3514.40, Nint=067, Nfrac=076) +Fch=1757.40 (Fvco=3514.80, Nint=067, Nfrac=077) +Fch=1757.60 (Fvco=3515.20, Nint=067, Nfrac=078) +Fch=1757.80 (Fvco=3515.60, Nint=067, Nfrac=079) +Fch=1758.00 (Fvco=3516.00, Nint=067, Nfrac=080) +Fch=1758.20 (Fvco=3516.40, Nint=067, Nfrac=081) +Fch=1758.40 (Fvco=3516.80, Nint=067, Nfrac=082) +Fch=1758.60 (Fvco=3517.20, Nint=067, Nfrac=083) +Fch=1758.80 (Fvco=3517.60, Nint=067, Nfrac=084) +Fch=1759.00 (Fvco=3518.00, Nint=067, Nfrac=085) +Fch=1759.20 (Fvco=3518.40, Nint=067, Nfrac=086) +Fch=1759.40 (Fvco=3518.80, Nint=067, Nfrac=087) +Fch=1759.60 (Fvco=3519.20, Nint=067, Nfrac=088) +Fch=1759.80 (Fvco=3519.60, Nint=067, Nfrac=089) +Fch=1760.00 (Fvco=3520.00, Nint=067, Nfrac=090) +Fch=1760.20 (Fvco=3520.40, Nint=067, Nfrac=091) +Fch=1760.40 (Fvco=3520.80, Nint=067, Nfrac=092) +Fch=1760.60 (Fvco=3521.20, Nint=067, Nfrac=093) +Fch=1760.80 (Fvco=3521.60, Nint=067, Nfrac=094) +Fch=1761.00 (Fvco=3522.00, Nint=067, Nfrac=095) +Fch=1761.20 (Fvco=3522.40, Nint=067, Nfrac=096) +Fch=1761.40 (Fvco=3522.80, Nint=067, Nfrac=097) +Fch=1761.60 (Fvco=3523.20, Nint=067, Nfrac=098) +Fch=1761.80 (Fvco=3523.60, Nint=067, Nfrac=099) +Fch=1762.00 (Fvco=3524.00, Nint=067, Nfrac=100) +Fch=1762.20 (Fvco=3524.40, Nint=067, Nfrac=101) +Fch=1762.40 (Fvco=3524.80, Nint=067, Nfrac=102) +Fch=1762.60 (Fvco=3525.20, Nint=067, Nfrac=103) +Fch=1762.80 (Fvco=3525.60, Nint=067, Nfrac=104) +Fch=1763.00 (Fvco=3526.00, Nint=067, Nfrac=105) +Fch=1763.20 (Fvco=3526.40, Nint=067, Nfrac=106) +Fch=1763.40 (Fvco=3526.80, Nint=067, Nfrac=107) +Fch=1763.60 (Fvco=3527.20, Nint=067, Nfrac=108) +Fch=1763.80 (Fvco=3527.60, Nint=067, Nfrac=109) +Fch=1764.00 (Fvco=3528.00, Nint=067, Nfrac=110) +Fch=1764.20 (Fvco=3528.40, Nint=067, Nfrac=111) +Fch=1764.40 (Fvco=3528.80, Nint=067, Nfrac=112) +Fch=1764.60 (Fvco=3529.20, Nint=067, Nfrac=113) +Fch=1764.80 (Fvco=3529.60, Nint=067, Nfrac=114) +Fch=1765.00 (Fvco=3530.00, Nint=067, Nfrac=115) +Fch=1765.20 (Fvco=3530.40, Nint=067, Nfrac=116) +Fch=1765.40 (Fvco=3530.80, Nint=067, Nfrac=117) +Fch=1765.60 (Fvco=3531.20, Nint=067, Nfrac=118) +Fch=1765.80 (Fvco=3531.60, Nint=067, Nfrac=119) +Fch=1766.00 (Fvco=3532.00, Nint=067, Nfrac=120) +Fch=1766.20 (Fvco=3532.40, Nint=067, Nfrac=121) +Fch=1766.40 (Fvco=3532.80, Nint=067, Nfrac=122) +Fch=1766.60 (Fvco=3533.20, Nint=067, Nfrac=123) +Fch=1766.80 (Fvco=3533.60, Nint=067, Nfrac=124) +Fch=1767.00 (Fvco=3534.00, Nint=067, Nfrac=125) +Fch=1767.20 (Fvco=3534.40, Nint=067, Nfrac=126) +Fch=1767.40 (Fvco=3534.80, Nint=067, Nfrac=127) +Fch=1767.60 (Fvco=3535.20, Nint=067, Nfrac=128) +Fch=1767.80 (Fvco=3535.60, Nint=067, Nfrac=129) +Fch=1768.00 (Fvco=3536.00, Nint=067, Nfrac=130) +Fch=1768.00 (Fvco=3536.00, Nint=068, Nfrac=000) +Fch=1768.20 (Fvco=3536.40, Nint=068, Nfrac=001) +Fch=1768.40 (Fvco=3536.80, Nint=068, Nfrac=002) +Fch=1768.60 (Fvco=3537.20, Nint=068, Nfrac=003) +Fch=1768.80 (Fvco=3537.60, Nint=068, Nfrac=004) +Fch=1769.00 (Fvco=3538.00, Nint=068, Nfrac=005) +Fch=1769.20 (Fvco=3538.40, Nint=068, Nfrac=006) +Fch=1769.40 (Fvco=3538.80, Nint=068, Nfrac=007) +Fch=1769.60 (Fvco=3539.20, Nint=068, Nfrac=008) +Fch=1769.80 (Fvco=3539.60, Nint=068, Nfrac=009) +Fch=1770.00 (Fvco=3540.00, Nint=068, Nfrac=010) +Fch=1770.20 (Fvco=3540.40, Nint=068, Nfrac=011) +Fch=1770.40 (Fvco=3540.80, Nint=068, Nfrac=012) +Fch=1770.60 (Fvco=3541.20, Nint=068, Nfrac=013) +Fch=1770.80 (Fvco=3541.60, Nint=068, Nfrac=014) +Fch=1771.00 (Fvco=3542.00, Nint=068, Nfrac=015) +Fch=1771.20 (Fvco=3542.40, Nint=068, Nfrac=016) +Fch=1771.40 (Fvco=3542.80, Nint=068, Nfrac=017) +Fch=1771.60 (Fvco=3543.20, Nint=068, Nfrac=018) +Fch=1771.80 (Fvco=3543.60, Nint=068, Nfrac=019) +Fch=1772.00 (Fvco=3544.00, Nint=068, Nfrac=020) +Fch=1772.20 (Fvco=3544.40, Nint=068, Nfrac=021) +Fch=1772.40 (Fvco=3544.80, Nint=068, Nfrac=022) +Fch=1772.60 (Fvco=3545.20, Nint=068, Nfrac=023) +Fch=1772.80 (Fvco=3545.60, Nint=068, Nfrac=024) +Fch=1773.00 (Fvco=3546.00, Nint=068, Nfrac=025) +Fch=1773.20 (Fvco=3546.40, Nint=068, Nfrac=026) +Fch=1773.40 (Fvco=3546.80, Nint=068, Nfrac=027) +Fch=1773.60 (Fvco=3547.20, Nint=068, Nfrac=028) +Fch=1773.80 (Fvco=3547.60, Nint=068, Nfrac=029) +Fch=1774.00 (Fvco=3548.00, Nint=068, Nfrac=030) +Fch=1774.20 (Fvco=3548.40, Nint=068, Nfrac=031) +Fch=1774.40 (Fvco=3548.80, Nint=068, Nfrac=032) +Fch=1774.60 (Fvco=3549.20, Nint=068, Nfrac=033) +Fch=1774.80 (Fvco=3549.60, Nint=068, Nfrac=034) +Fch=1775.00 (Fvco=3550.00, Nint=068, Nfrac=035) +Fch=1775.20 (Fvco=3550.40, Nint=068, Nfrac=036) +Fch=1775.40 (Fvco=3550.80, Nint=068, Nfrac=037) +Fch=1775.60 (Fvco=3551.20, Nint=068, Nfrac=038) +Fch=1775.80 (Fvco=3551.60, Nint=068, Nfrac=039) +Fch=1776.00 (Fvco=3552.00, Nint=068, Nfrac=040) +Fch=1776.20 (Fvco=3552.40, Nint=068, Nfrac=041) +Fch=1776.40 (Fvco=3552.80, Nint=068, Nfrac=042) +Fch=1776.60 (Fvco=3553.20, Nint=068, Nfrac=043) +Fch=1776.80 (Fvco=3553.60, Nint=068, Nfrac=044) +Fch=1777.00 (Fvco=3554.00, Nint=068, Nfrac=045) +Fch=1777.20 (Fvco=3554.40, Nint=068, Nfrac=046) +Fch=1777.40 (Fvco=3554.80, Nint=068, Nfrac=047) +Fch=1777.60 (Fvco=3555.20, Nint=068, Nfrac=048) +Fch=1777.80 (Fvco=3555.60, Nint=068, Nfrac=049) +Fch=1778.00 (Fvco=3556.00, Nint=068, Nfrac=050) +Fch=1778.20 (Fvco=3556.40, Nint=068, Nfrac=051) +Fch=1778.40 (Fvco=3556.80, Nint=068, Nfrac=052) +Fch=1778.60 (Fvco=3557.20, Nint=068, Nfrac=053) +Fch=1778.80 (Fvco=3557.60, Nint=068, Nfrac=054) +Fch=1779.00 (Fvco=3558.00, Nint=068, Nfrac=055) +Fch=1779.20 (Fvco=3558.40, Nint=068, Nfrac=056) +Fch=1779.40 (Fvco=3558.80, Nint=068, Nfrac=057) +Fch=1779.60 (Fvco=3559.20, Nint=068, Nfrac=058) +Fch=1779.80 (Fvco=3559.60, Nint=068, Nfrac=059) +Fch=1780.00 (Fvco=3560.00, Nint=068, Nfrac=060) +Fch=1780.20 (Fvco=3560.40, Nint=068, Nfrac=061) +Fch=1780.40 (Fvco=3560.80, Nint=068, Nfrac=062) +Fch=1780.60 (Fvco=3561.20, Nint=068, Nfrac=063) +Fch=1780.80 (Fvco=3561.60, Nint=068, Nfrac=064) +Fch=1781.00 (Fvco=3562.00, Nint=068, Nfrac=065) +Fch=1781.20 (Fvco=3562.40, Nint=068, Nfrac=066) +Fch=1781.40 (Fvco=3562.80, Nint=068, Nfrac=067) +Fch=1781.60 (Fvco=3563.20, Nint=068, Nfrac=068) +Fch=1781.80 (Fvco=3563.60, Nint=068, Nfrac=069) +Fch=1782.00 (Fvco=3564.00, Nint=068, Nfrac=070) +Fch=1782.20 (Fvco=3564.40, Nint=068, Nfrac=071) +Fch=1782.40 (Fvco=3564.80, Nint=068, Nfrac=072) +Fch=1782.60 (Fvco=3565.20, Nint=068, Nfrac=073) +Fch=1782.80 (Fvco=3565.60, Nint=068, Nfrac=074) +Fch=1783.00 (Fvco=3566.00, Nint=068, Nfrac=075) +Fch=1783.20 (Fvco=3566.40, Nint=068, Nfrac=076) +Fch=1783.40 (Fvco=3566.80, Nint=068, Nfrac=077) +Fch=1783.60 (Fvco=3567.20, Nint=068, Nfrac=078) +Fch=1783.80 (Fvco=3567.60, Nint=068, Nfrac=079) +Fch=1784.00 (Fvco=3568.00, Nint=068, Nfrac=080) +Fch=1784.20 (Fvco=3568.40, Nint=068, Nfrac=081) +Fch=1784.40 (Fvco=3568.80, Nint=068, Nfrac=082) +Fch=1784.60 (Fvco=3569.20, Nint=068, Nfrac=083) +Fch=1784.80 (Fvco=3569.60, Nint=068, Nfrac=084) +Fch=1785.00 (Fvco=3570.00, Nint=068, Nfrac=085) +Fch=1785.20 (Fvco=3570.40, Nint=068, Nfrac=086) +Fch=1785.40 (Fvco=3570.80, Nint=068, Nfrac=087) +Fch=1785.60 (Fvco=3571.20, Nint=068, Nfrac=088) +Fch=1785.80 (Fvco=3571.60, Nint=068, Nfrac=089) +Fch=1786.00 (Fvco=3572.00, Nint=068, Nfrac=090) +Fch=1786.20 (Fvco=3572.40, Nint=068, Nfrac=091) +Fch=1786.40 (Fvco=3572.80, Nint=068, Nfrac=092) +Fch=1786.60 (Fvco=3573.20, Nint=068, Nfrac=093) +Fch=1786.80 (Fvco=3573.60, Nint=068, Nfrac=094) +Fch=1787.00 (Fvco=3574.00, Nint=068, Nfrac=095) +Fch=1787.20 (Fvco=3574.40, Nint=068, Nfrac=096) +Fch=1787.40 (Fvco=3574.80, Nint=068, Nfrac=097) +Fch=1787.60 (Fvco=3575.20, Nint=068, Nfrac=098) +Fch=1787.80 (Fvco=3575.60, Nint=068, Nfrac=099) +Fch=1788.00 (Fvco=3576.00, Nint=068, Nfrac=100) +Fch=1788.20 (Fvco=3576.40, Nint=068, Nfrac=101) +Fch=1788.40 (Fvco=3576.80, Nint=068, Nfrac=102) +Fch=1788.60 (Fvco=3577.20, Nint=068, Nfrac=103) +Fch=1788.80 (Fvco=3577.60, Nint=068, Nfrac=104) +Fch=1789.00 (Fvco=3578.00, Nint=068, Nfrac=105) +Fch=1789.20 (Fvco=3578.40, Nint=068, Nfrac=106) +Fch=1789.40 (Fvco=3578.80, Nint=068, Nfrac=107) +Fch=1789.60 (Fvco=3579.20, Nint=068, Nfrac=108) +Fch=1789.80 (Fvco=3579.60, Nint=068, Nfrac=109) +Fch=1790.00 (Fvco=3580.00, Nint=068, Nfrac=110) +Fch=1790.20 (Fvco=3580.40, Nint=068, Nfrac=111) +Fch=1790.40 (Fvco=3580.80, Nint=068, Nfrac=112) +Fch=1790.60 (Fvco=3581.20, Nint=068, Nfrac=113) +Fch=1790.80 (Fvco=3581.60, Nint=068, Nfrac=114) +Fch=1791.00 (Fvco=3582.00, Nint=068, Nfrac=115) +Fch=1791.20 (Fvco=3582.40, Nint=068, Nfrac=116) +Fch=1791.40 (Fvco=3582.80, Nint=068, Nfrac=117) +Fch=1791.60 (Fvco=3583.20, Nint=068, Nfrac=118) +Fch=1791.80 (Fvco=3583.60, Nint=068, Nfrac=119) +Fch=1792.00 (Fvco=3584.00, Nint=068, Nfrac=120) +Fch=1792.20 (Fvco=3584.40, Nint=068, Nfrac=121) +Fch=1792.40 (Fvco=3584.80, Nint=068, Nfrac=122) +Fch=1792.60 (Fvco=3585.20, Nint=068, Nfrac=123) +Fch=1792.80 (Fvco=3585.60, Nint=068, Nfrac=124) +Fch=1793.00 (Fvco=3586.00, Nint=068, Nfrac=125) +Fch=1793.20 (Fvco=3586.40, Nint=068, Nfrac=126) +Fch=1793.40 (Fvco=3586.80, Nint=068, Nfrac=127) +Fch=1793.60 (Fvco=3587.20, Nint=068, Nfrac=128) +Fch=1793.80 (Fvco=3587.60, Nint=068, Nfrac=129) +Fch=1794.00 (Fvco=3588.00, Nint=068, Nfrac=130) +Fch=1794.00 (Fvco=3588.00, Nint=069, Nfrac=000) +Fch=1794.20 (Fvco=3588.40, Nint=069, Nfrac=001) +Fch=1794.40 (Fvco=3588.80, Nint=069, Nfrac=002) +Fch=1794.60 (Fvco=3589.20, Nint=069, Nfrac=003) +Fch=1794.80 (Fvco=3589.60, Nint=069, Nfrac=004) +Fch=1795.00 (Fvco=3590.00, Nint=069, Nfrac=005) +Fch=1795.20 (Fvco=3590.40, Nint=069, Nfrac=006) +Fch=1795.40 (Fvco=3590.80, Nint=069, Nfrac=007) +Fch=1795.60 (Fvco=3591.20, Nint=069, Nfrac=008) +Fch=1795.80 (Fvco=3591.60, Nint=069, Nfrac=009) +Fch=1796.00 (Fvco=3592.00, Nint=069, Nfrac=010) +Fch=1796.20 (Fvco=3592.40, Nint=069, Nfrac=011) +Fch=1796.40 (Fvco=3592.80, Nint=069, Nfrac=012) +Fch=1796.60 (Fvco=3593.20, Nint=069, Nfrac=013) +Fch=1796.80 (Fvco=3593.60, Nint=069, Nfrac=014) +Fch=1797.00 (Fvco=3594.00, Nint=069, Nfrac=015) +Fch=1797.20 (Fvco=3594.40, Nint=069, Nfrac=016) +Fch=1797.40 (Fvco=3594.80, Nint=069, Nfrac=017) +Fch=1797.60 (Fvco=3595.20, Nint=069, Nfrac=018) +Fch=1797.80 (Fvco=3595.60, Nint=069, Nfrac=019) +Fch=1798.00 (Fvco=3596.00, Nint=069, Nfrac=020) +Fch=1798.20 (Fvco=3596.40, Nint=069, Nfrac=021) +Fch=1798.40 (Fvco=3596.80, Nint=069, Nfrac=022) +Fch=1798.60 (Fvco=3597.20, Nint=069, Nfrac=023) +Fch=1798.80 (Fvco=3597.60, Nint=069, Nfrac=024) +Fch=1799.00 (Fvco=3598.00, Nint=069, Nfrac=025) +Fch=1799.20 (Fvco=3598.40, Nint=069, Nfrac=026) +Fch=1799.40 (Fvco=3598.80, Nint=069, Nfrac=027) +Fch=1799.60 (Fvco=3599.20, Nint=069, Nfrac=028) +Fch=1799.80 (Fvco=3599.60, Nint=069, Nfrac=029) +Fch=1800.00 (Fvco=3600.00, Nint=069, Nfrac=030) +Fch=1800.20 (Fvco=3600.40, Nint=069, Nfrac=031) +Fch=1800.40 (Fvco=3600.80, Nint=069, Nfrac=032) +Fch=1800.60 (Fvco=3601.20, Nint=069, Nfrac=033) +Fch=1800.80 (Fvco=3601.60, Nint=069, Nfrac=034) +Fch=1801.00 (Fvco=3602.00, Nint=069, Nfrac=035) +Fch=1801.20 (Fvco=3602.40, Nint=069, Nfrac=036) +Fch=1801.40 (Fvco=3602.80, Nint=069, Nfrac=037) +Fch=1801.60 (Fvco=3603.20, Nint=069, Nfrac=038) +Fch=1801.80 (Fvco=3603.60, Nint=069, Nfrac=039) +Fch=1802.00 (Fvco=3604.00, Nint=069, Nfrac=040) +Fch=1802.20 (Fvco=3604.40, Nint=069, Nfrac=041) +Fch=1802.40 (Fvco=3604.80, Nint=069, Nfrac=042) +Fch=1802.60 (Fvco=3605.20, Nint=069, Nfrac=043) +Fch=1802.80 (Fvco=3605.60, Nint=069, Nfrac=044) +Fch=1803.00 (Fvco=3606.00, Nint=069, Nfrac=045) +Fch=1803.20 (Fvco=3606.40, Nint=069, Nfrac=046) +Fch=1803.40 (Fvco=3606.80, Nint=069, Nfrac=047) +Fch=1803.60 (Fvco=3607.20, Nint=069, Nfrac=048) +Fch=1803.80 (Fvco=3607.60, Nint=069, Nfrac=049) +Fch=1804.00 (Fvco=3608.00, Nint=069, Nfrac=050) +Fch=1804.20 (Fvco=3608.40, Nint=069, Nfrac=051) +Fch=1804.40 (Fvco=3608.80, Nint=069, Nfrac=052) +Fch=1804.60 (Fvco=3609.20, Nint=069, Nfrac=053) +Fch=1804.80 (Fvco=3609.60, Nint=069, Nfrac=054) +Fch=1805.00 (Fvco=3610.00, Nint=069, Nfrac=055) +Fch=1805.20 (Fvco=3610.40, Nint=069, Nfrac=056) +Fch=1805.40 (Fvco=3610.80, Nint=069, Nfrac=057) +Fch=1805.60 (Fvco=3611.20, Nint=069, Nfrac=058) +Fch=1805.80 (Fvco=3611.60, Nint=069, Nfrac=059) +Fch=1806.00 (Fvco=3612.00, Nint=069, Nfrac=060) +Fch=1806.20 (Fvco=3612.40, Nint=069, Nfrac=061) +Fch=1806.40 (Fvco=3612.80, Nint=069, Nfrac=062) +Fch=1806.60 (Fvco=3613.20, Nint=069, Nfrac=063) +Fch=1806.80 (Fvco=3613.60, Nint=069, Nfrac=064) +Fch=1807.00 (Fvco=3614.00, Nint=069, Nfrac=065) +Fch=1807.20 (Fvco=3614.40, Nint=069, Nfrac=066) +Fch=1807.40 (Fvco=3614.80, Nint=069, Nfrac=067) +Fch=1807.60 (Fvco=3615.20, Nint=069, Nfrac=068) +Fch=1807.80 (Fvco=3615.60, Nint=069, Nfrac=069) +Fch=1808.00 (Fvco=3616.00, Nint=069, Nfrac=070) +Fch=1808.20 (Fvco=3616.40, Nint=069, Nfrac=071) +Fch=1808.40 (Fvco=3616.80, Nint=069, Nfrac=072) +Fch=1808.60 (Fvco=3617.20, Nint=069, Nfrac=073) +Fch=1808.80 (Fvco=3617.60, Nint=069, Nfrac=074) +Fch=1809.00 (Fvco=3618.00, Nint=069, Nfrac=075) +Fch=1809.20 (Fvco=3618.40, Nint=069, Nfrac=076) +Fch=1809.40 (Fvco=3618.80, Nint=069, Nfrac=077) +Fch=1809.60 (Fvco=3619.20, Nint=069, Nfrac=078) +Fch=1809.80 (Fvco=3619.60, Nint=069, Nfrac=079) +Fch=1810.00 (Fvco=3620.00, Nint=069, Nfrac=080) +Fch=1810.20 (Fvco=3620.40, Nint=069, Nfrac=081) +Fch=1810.40 (Fvco=3620.80, Nint=069, Nfrac=082) +Fch=1810.60 (Fvco=3621.20, Nint=069, Nfrac=083) +Fch=1810.80 (Fvco=3621.60, Nint=069, Nfrac=084) +Fch=1811.00 (Fvco=3622.00, Nint=069, Nfrac=085) +Fch=1811.20 (Fvco=3622.40, Nint=069, Nfrac=086) +Fch=1811.40 (Fvco=3622.80, Nint=069, Nfrac=087) +Fch=1811.60 (Fvco=3623.20, Nint=069, Nfrac=088) +Fch=1811.80 (Fvco=3623.60, Nint=069, Nfrac=089) +Fch=1812.00 (Fvco=3624.00, Nint=069, Nfrac=090) +Fch=1812.20 (Fvco=3624.40, Nint=069, Nfrac=091) +Fch=1812.40 (Fvco=3624.80, Nint=069, Nfrac=092) +Fch=1812.60 (Fvco=3625.20, Nint=069, Nfrac=093) +Fch=1812.80 (Fvco=3625.60, Nint=069, Nfrac=094) +Fch=1813.00 (Fvco=3626.00, Nint=069, Nfrac=095) +Fch=1813.20 (Fvco=3626.40, Nint=069, Nfrac=096) +Fch=1813.40 (Fvco=3626.80, Nint=069, Nfrac=097) +Fch=1813.60 (Fvco=3627.20, Nint=069, Nfrac=098) +Fch=1813.80 (Fvco=3627.60, Nint=069, Nfrac=099) +Fch=1814.00 (Fvco=3628.00, Nint=069, Nfrac=100) +Fch=1814.20 (Fvco=3628.40, Nint=069, Nfrac=101) +Fch=1814.40 (Fvco=3628.80, Nint=069, Nfrac=102) +Fch=1814.60 (Fvco=3629.20, Nint=069, Nfrac=103) +Fch=1814.80 (Fvco=3629.60, Nint=069, Nfrac=104) +Fch=1815.00 (Fvco=3630.00, Nint=069, Nfrac=105) +Fch=1815.20 (Fvco=3630.40, Nint=069, Nfrac=106) +Fch=1815.40 (Fvco=3630.80, Nint=069, Nfrac=107) +Fch=1815.60 (Fvco=3631.20, Nint=069, Nfrac=108) +Fch=1815.80 (Fvco=3631.60, Nint=069, Nfrac=109) +Fch=1816.00 (Fvco=3632.00, Nint=069, Nfrac=110) +Fch=1816.20 (Fvco=3632.40, Nint=069, Nfrac=111) +Fch=1816.40 (Fvco=3632.80, Nint=069, Nfrac=112) +Fch=1816.60 (Fvco=3633.20, Nint=069, Nfrac=113) +Fch=1816.80 (Fvco=3633.60, Nint=069, Nfrac=114) +Fch=1817.00 (Fvco=3634.00, Nint=069, Nfrac=115) +Fch=1817.20 (Fvco=3634.40, Nint=069, Nfrac=116) +Fch=1817.40 (Fvco=3634.80, Nint=069, Nfrac=117) +Fch=1817.60 (Fvco=3635.20, Nint=069, Nfrac=118) +Fch=1817.80 (Fvco=3635.60, Nint=069, Nfrac=119) +Fch=1818.00 (Fvco=3636.00, Nint=069, Nfrac=120) +Fch=1818.20 (Fvco=3636.40, Nint=069, Nfrac=121) +Fch=1818.40 (Fvco=3636.80, Nint=069, Nfrac=122) +Fch=1818.60 (Fvco=3637.20, Nint=069, Nfrac=123) +Fch=1818.80 (Fvco=3637.60, Nint=069, Nfrac=124) +Fch=1819.00 (Fvco=3638.00, Nint=069, Nfrac=125) +Fch=1819.20 (Fvco=3638.40, Nint=069, Nfrac=126) +Fch=1819.40 (Fvco=3638.80, Nint=069, Nfrac=127) +Fch=1819.60 (Fvco=3639.20, Nint=069, Nfrac=128) +Fch=1819.80 (Fvco=3639.60, Nint=069, Nfrac=129) +Fch=1820.00 (Fvco=3640.00, Nint=069, Nfrac=130) +Fch=1820.00 (Fvco=3640.00, Nint=070, Nfrac=000) +Fch=1820.20 (Fvco=3640.40, Nint=070, Nfrac=001) +Fch=1820.40 (Fvco=3640.80, Nint=070, Nfrac=002) +Fch=1820.60 (Fvco=3641.20, Nint=070, Nfrac=003) +Fch=1820.80 (Fvco=3641.60, Nint=070, Nfrac=004) +Fch=1821.00 (Fvco=3642.00, Nint=070, Nfrac=005) +Fch=1821.20 (Fvco=3642.40, Nint=070, Nfrac=006) +Fch=1821.40 (Fvco=3642.80, Nint=070, Nfrac=007) +Fch=1821.60 (Fvco=3643.20, Nint=070, Nfrac=008) +Fch=1821.80 (Fvco=3643.60, Nint=070, Nfrac=009) +Fch=1822.00 (Fvco=3644.00, Nint=070, Nfrac=010) +Fch=1822.20 (Fvco=3644.40, Nint=070, Nfrac=011) +Fch=1822.40 (Fvco=3644.80, Nint=070, Nfrac=012) +Fch=1822.60 (Fvco=3645.20, Nint=070, Nfrac=013) +Fch=1822.80 (Fvco=3645.60, Nint=070, Nfrac=014) +Fch=1823.00 (Fvco=3646.00, Nint=070, Nfrac=015) +Fch=1823.20 (Fvco=3646.40, Nint=070, Nfrac=016) +Fch=1823.40 (Fvco=3646.80, Nint=070, Nfrac=017) +Fch=1823.60 (Fvco=3647.20, Nint=070, Nfrac=018) +Fch=1823.80 (Fvco=3647.60, Nint=070, Nfrac=019) +Fch=1824.00 (Fvco=3648.00, Nint=070, Nfrac=020) +Fch=1824.20 (Fvco=3648.40, Nint=070, Nfrac=021) +Fch=1824.40 (Fvco=3648.80, Nint=070, Nfrac=022) +Fch=1824.60 (Fvco=3649.20, Nint=070, Nfrac=023) +Fch=1824.80 (Fvco=3649.60, Nint=070, Nfrac=024) +Fch=1825.00 (Fvco=3650.00, Nint=070, Nfrac=025) +Fch=1825.20 (Fvco=3650.40, Nint=070, Nfrac=026) +Fch=1825.40 (Fvco=3650.80, Nint=070, Nfrac=027) +Fch=1825.60 (Fvco=3651.20, Nint=070, Nfrac=028) +Fch=1825.80 (Fvco=3651.60, Nint=070, Nfrac=029) +Fch=1826.00 (Fvco=3652.00, Nint=070, Nfrac=030) +Fch=1826.20 (Fvco=3652.40, Nint=070, Nfrac=031) +Fch=1826.40 (Fvco=3652.80, Nint=070, Nfrac=032) +Fch=1826.60 (Fvco=3653.20, Nint=070, Nfrac=033) +Fch=1826.80 (Fvco=3653.60, Nint=070, Nfrac=034) +Fch=1827.00 (Fvco=3654.00, Nint=070, Nfrac=035) +Fch=1827.20 (Fvco=3654.40, Nint=070, Nfrac=036) +Fch=1827.40 (Fvco=3654.80, Nint=070, Nfrac=037) +Fch=1827.60 (Fvco=3655.20, Nint=070, Nfrac=038) +Fch=1827.80 (Fvco=3655.60, Nint=070, Nfrac=039) +Fch=1828.00 (Fvco=3656.00, Nint=070, Nfrac=040) +Fch=1828.20 (Fvco=3656.40, Nint=070, Nfrac=041) +Fch=1828.40 (Fvco=3656.80, Nint=070, Nfrac=042) +Fch=1828.60 (Fvco=3657.20, Nint=070, Nfrac=043) +Fch=1828.80 (Fvco=3657.60, Nint=070, Nfrac=044) +Fch=1829.00 (Fvco=3658.00, Nint=070, Nfrac=045) +Fch=1829.20 (Fvco=3658.40, Nint=070, Nfrac=046) +Fch=1829.40 (Fvco=3658.80, Nint=070, Nfrac=047) +Fch=1829.60 (Fvco=3659.20, Nint=070, Nfrac=048) +Fch=1829.80 (Fvco=3659.60, Nint=070, Nfrac=049) +Fch=1830.00 (Fvco=3660.00, Nint=070, Nfrac=050) +Fch=1830.20 (Fvco=3660.40, Nint=070, Nfrac=051) +Fch=1830.40 (Fvco=3660.80, Nint=070, Nfrac=052) +Fch=1830.60 (Fvco=3661.20, Nint=070, Nfrac=053) +Fch=1830.80 (Fvco=3661.60, Nint=070, Nfrac=054) +Fch=1831.00 (Fvco=3662.00, Nint=070, Nfrac=055) +Fch=1831.20 (Fvco=3662.40, Nint=070, Nfrac=056) +Fch=1831.40 (Fvco=3662.80, Nint=070, Nfrac=057) +Fch=1831.60 (Fvco=3663.20, Nint=070, Nfrac=058) +Fch=1831.80 (Fvco=3663.60, Nint=070, Nfrac=059) +Fch=1832.00 (Fvco=3664.00, Nint=070, Nfrac=060) +Fch=1832.20 (Fvco=3664.40, Nint=070, Nfrac=061) +Fch=1832.40 (Fvco=3664.80, Nint=070, Nfrac=062) +Fch=1832.60 (Fvco=3665.20, Nint=070, Nfrac=063) +Fch=1832.80 (Fvco=3665.60, Nint=070, Nfrac=064) +Fch=1833.00 (Fvco=3666.00, Nint=070, Nfrac=065) +Fch=1833.20 (Fvco=3666.40, Nint=070, Nfrac=066) +Fch=1833.40 (Fvco=3666.80, Nint=070, Nfrac=067) +Fch=1833.60 (Fvco=3667.20, Nint=070, Nfrac=068) +Fch=1833.80 (Fvco=3667.60, Nint=070, Nfrac=069) +Fch=1834.00 (Fvco=3668.00, Nint=070, Nfrac=070) +Fch=1834.20 (Fvco=3668.40, Nint=070, Nfrac=071) +Fch=1834.40 (Fvco=3668.80, Nint=070, Nfrac=072) +Fch=1834.60 (Fvco=3669.20, Nint=070, Nfrac=073) +Fch=1834.80 (Fvco=3669.60, Nint=070, Nfrac=074) +Fch=1835.00 (Fvco=3670.00, Nint=070, Nfrac=075) +Fch=1835.20 (Fvco=3670.40, Nint=070, Nfrac=076) +Fch=1835.40 (Fvco=3670.80, Nint=070, Nfrac=077) +Fch=1835.60 (Fvco=3671.20, Nint=070, Nfrac=078) +Fch=1835.80 (Fvco=3671.60, Nint=070, Nfrac=079) +Fch=1836.00 (Fvco=3672.00, Nint=070, Nfrac=080) +Fch=1836.20 (Fvco=3672.40, Nint=070, Nfrac=081) +Fch=1836.40 (Fvco=3672.80, Nint=070, Nfrac=082) +Fch=1836.60 (Fvco=3673.20, Nint=070, Nfrac=083) +Fch=1836.80 (Fvco=3673.60, Nint=070, Nfrac=084) +Fch=1837.00 (Fvco=3674.00, Nint=070, Nfrac=085) +Fch=1837.20 (Fvco=3674.40, Nint=070, Nfrac=086) +Fch=1837.40 (Fvco=3674.80, Nint=070, Nfrac=087) +Fch=1837.60 (Fvco=3675.20, Nint=070, Nfrac=088) +Fch=1837.80 (Fvco=3675.60, Nint=070, Nfrac=089) +Fch=1838.00 (Fvco=3676.00, Nint=070, Nfrac=090) +Fch=1838.20 (Fvco=3676.40, Nint=070, Nfrac=091) +Fch=1838.40 (Fvco=3676.80, Nint=070, Nfrac=092) +Fch=1838.60 (Fvco=3677.20, Nint=070, Nfrac=093) +Fch=1838.80 (Fvco=3677.60, Nint=070, Nfrac=094) +Fch=1839.00 (Fvco=3678.00, Nint=070, Nfrac=095) +Fch=1839.20 (Fvco=3678.40, Nint=070, Nfrac=096) +Fch=1839.40 (Fvco=3678.80, Nint=070, Nfrac=097) +Fch=1839.60 (Fvco=3679.20, Nint=070, Nfrac=098) +Fch=1839.80 (Fvco=3679.60, Nint=070, Nfrac=099) +Fch=1840.00 (Fvco=3680.00, Nint=070, Nfrac=100) +Fch=1840.20 (Fvco=3680.40, Nint=070, Nfrac=101) +Fch=1840.40 (Fvco=3680.80, Nint=070, Nfrac=102) +Fch=1840.60 (Fvco=3681.20, Nint=070, Nfrac=103) +Fch=1840.80 (Fvco=3681.60, Nint=070, Nfrac=104) +Fch=1841.00 (Fvco=3682.00, Nint=070, Nfrac=105) +Fch=1841.20 (Fvco=3682.40, Nint=070, Nfrac=106) +Fch=1841.40 (Fvco=3682.80, Nint=070, Nfrac=107) +Fch=1841.60 (Fvco=3683.20, Nint=070, Nfrac=108) +Fch=1841.80 (Fvco=3683.60, Nint=070, Nfrac=109) +Fch=1842.00 (Fvco=3684.00, Nint=070, Nfrac=110) +Fch=1842.20 (Fvco=3684.40, Nint=070, Nfrac=111) +Fch=1842.40 (Fvco=3684.80, Nint=070, Nfrac=112) +Fch=1842.60 (Fvco=3685.20, Nint=070, Nfrac=113) +Fch=1842.80 (Fvco=3685.60, Nint=070, Nfrac=114) +Fch=1843.00 (Fvco=3686.00, Nint=070, Nfrac=115) +Fch=1843.20 (Fvco=3686.40, Nint=070, Nfrac=116) +Fch=1843.40 (Fvco=3686.80, Nint=070, Nfrac=117) +Fch=1843.60 (Fvco=3687.20, Nint=070, Nfrac=118) +Fch=1843.80 (Fvco=3687.60, Nint=070, Nfrac=119) +Fch=1844.00 (Fvco=3688.00, Nint=070, Nfrac=120) +Fch=1844.20 (Fvco=3688.40, Nint=070, Nfrac=121) +Fch=1844.40 (Fvco=3688.80, Nint=070, Nfrac=122) +Fch=1844.60 (Fvco=3689.20, Nint=070, Nfrac=123) +Fch=1844.80 (Fvco=3689.60, Nint=070, Nfrac=124) +Fch=1845.00 (Fvco=3690.00, Nint=070, Nfrac=125) +Fch=1845.20 (Fvco=3690.40, Nint=070, Nfrac=126) +Fch=1845.40 (Fvco=3690.80, Nint=070, Nfrac=127) +Fch=1845.60 (Fvco=3691.20, Nint=070, Nfrac=128) +Fch=1845.80 (Fvco=3691.60, Nint=070, Nfrac=129) +Fch=1846.00 (Fvco=3692.00, Nint=070, Nfrac=130) +Fch=1846.00 (Fvco=3692.00, Nint=071, Nfrac=000) +Fch=1846.20 (Fvco=3692.40, Nint=071, Nfrac=001) +Fch=1846.40 (Fvco=3692.80, Nint=071, Nfrac=002) +Fch=1846.60 (Fvco=3693.20, Nint=071, Nfrac=003) +Fch=1846.80 (Fvco=3693.60, Nint=071, Nfrac=004) +Fch=1847.00 (Fvco=3694.00, Nint=071, Nfrac=005) +Fch=1847.20 (Fvco=3694.40, Nint=071, Nfrac=006) +Fch=1847.40 (Fvco=3694.80, Nint=071, Nfrac=007) +Fch=1847.60 (Fvco=3695.20, Nint=071, Nfrac=008) +Fch=1847.80 (Fvco=3695.60, Nint=071, Nfrac=009) +Fch=1848.00 (Fvco=3696.00, Nint=071, Nfrac=010) +Fch=1848.20 (Fvco=3696.40, Nint=071, Nfrac=011) +Fch=1848.40 (Fvco=3696.80, Nint=071, Nfrac=012) +Fch=1848.60 (Fvco=3697.20, Nint=071, Nfrac=013) +Fch=1848.80 (Fvco=3697.60, Nint=071, Nfrac=014) +Fch=1849.00 (Fvco=3698.00, Nint=071, Nfrac=015) +Fch=1849.20 (Fvco=3698.40, Nint=071, Nfrac=016) +Fch=1849.40 (Fvco=3698.80, Nint=071, Nfrac=017) +Fch=1849.60 (Fvco=3699.20, Nint=071, Nfrac=018) +Fch=1849.80 (Fvco=3699.60, Nint=071, Nfrac=019) +Fch=1850.00 (Fvco=3700.00, Nint=071, Nfrac=020) +Fch=1850.20 (Fvco=3700.40, Nint=071, Nfrac=021) +Fch=1850.40 (Fvco=3700.80, Nint=071, Nfrac=022) +Fch=1850.60 (Fvco=3701.20, Nint=071, Nfrac=023) +Fch=1850.80 (Fvco=3701.60, Nint=071, Nfrac=024) +Fch=1851.00 (Fvco=3702.00, Nint=071, Nfrac=025) +Fch=1851.20 (Fvco=3702.40, Nint=071, Nfrac=026) +Fch=1851.40 (Fvco=3702.80, Nint=071, Nfrac=027) +Fch=1851.60 (Fvco=3703.20, Nint=071, Nfrac=028) +Fch=1851.80 (Fvco=3703.60, Nint=071, Nfrac=029) +Fch=1852.00 (Fvco=3704.00, Nint=071, Nfrac=030) +Fch=1852.20 (Fvco=3704.40, Nint=071, Nfrac=031) +Fch=1852.40 (Fvco=3704.80, Nint=071, Nfrac=032) +Fch=1852.60 (Fvco=3705.20, Nint=071, Nfrac=033) +Fch=1852.80 (Fvco=3705.60, Nint=071, Nfrac=034) +Fch=1853.00 (Fvco=3706.00, Nint=071, Nfrac=035) +Fch=1853.20 (Fvco=3706.40, Nint=071, Nfrac=036) +Fch=1853.40 (Fvco=3706.80, Nint=071, Nfrac=037) +Fch=1853.60 (Fvco=3707.20, Nint=071, Nfrac=038) +Fch=1853.80 (Fvco=3707.60, Nint=071, Nfrac=039) +Fch=1854.00 (Fvco=3708.00, Nint=071, Nfrac=040) +Fch=1854.20 (Fvco=3708.40, Nint=071, Nfrac=041) +Fch=1854.40 (Fvco=3708.80, Nint=071, Nfrac=042) +Fch=1854.60 (Fvco=3709.20, Nint=071, Nfrac=043) +Fch=1854.80 (Fvco=3709.60, Nint=071, Nfrac=044) +Fch=1855.00 (Fvco=3710.00, Nint=071, Nfrac=045) +Fch=1855.20 (Fvco=3710.40, Nint=071, Nfrac=046) +Fch=1855.40 (Fvco=3710.80, Nint=071, Nfrac=047) +Fch=1855.60 (Fvco=3711.20, Nint=071, Nfrac=048) +Fch=1855.80 (Fvco=3711.60, Nint=071, Nfrac=049) +Fch=1856.00 (Fvco=3712.00, Nint=071, Nfrac=050) +Fch=1856.20 (Fvco=3712.40, Nint=071, Nfrac=051) +Fch=1856.40 (Fvco=3712.80, Nint=071, Nfrac=052) +Fch=1856.60 (Fvco=3713.20, Nint=071, Nfrac=053) +Fch=1856.80 (Fvco=3713.60, Nint=071, Nfrac=054) +Fch=1857.00 (Fvco=3714.00, Nint=071, Nfrac=055) +Fch=1857.20 (Fvco=3714.40, Nint=071, Nfrac=056) +Fch=1857.40 (Fvco=3714.80, Nint=071, Nfrac=057) +Fch=1857.60 (Fvco=3715.20, Nint=071, Nfrac=058) +Fch=1857.80 (Fvco=3715.60, Nint=071, Nfrac=059) +Fch=1858.00 (Fvco=3716.00, Nint=071, Nfrac=060) +Fch=1858.20 (Fvco=3716.40, Nint=071, Nfrac=061) +Fch=1858.40 (Fvco=3716.80, Nint=071, Nfrac=062) +Fch=1858.60 (Fvco=3717.20, Nint=071, Nfrac=063) +Fch=1858.80 (Fvco=3717.60, Nint=071, Nfrac=064) +Fch=1859.00 (Fvco=3718.00, Nint=071, Nfrac=065) +Fch=1859.20 (Fvco=3718.40, Nint=071, Nfrac=066) +Fch=1859.40 (Fvco=3718.80, Nint=071, Nfrac=067) +Fch=1859.60 (Fvco=3719.20, Nint=071, Nfrac=068) +Fch=1859.80 (Fvco=3719.60, Nint=071, Nfrac=069) +Fch=1860.00 (Fvco=3720.00, Nint=071, Nfrac=070) +Fch=1860.20 (Fvco=3720.40, Nint=071, Nfrac=071) +Fch=1860.40 (Fvco=3720.80, Nint=071, Nfrac=072) +Fch=1860.60 (Fvco=3721.20, Nint=071, Nfrac=073) +Fch=1860.80 (Fvco=3721.60, Nint=071, Nfrac=074) +Fch=1861.00 (Fvco=3722.00, Nint=071, Nfrac=075) +Fch=1861.20 (Fvco=3722.40, Nint=071, Nfrac=076) +Fch=1861.40 (Fvco=3722.80, Nint=071, Nfrac=077) +Fch=1861.60 (Fvco=3723.20, Nint=071, Nfrac=078) +Fch=1861.80 (Fvco=3723.60, Nint=071, Nfrac=079) +Fch=1862.00 (Fvco=3724.00, Nint=071, Nfrac=080) +Fch=1862.20 (Fvco=3724.40, Nint=071, Nfrac=081) +Fch=1862.40 (Fvco=3724.80, Nint=071, Nfrac=082) +Fch=1862.60 (Fvco=3725.20, Nint=071, Nfrac=083) +Fch=1862.80 (Fvco=3725.60, Nint=071, Nfrac=084) +Fch=1863.00 (Fvco=3726.00, Nint=071, Nfrac=085) +Fch=1863.20 (Fvco=3726.40, Nint=071, Nfrac=086) +Fch=1863.40 (Fvco=3726.80, Nint=071, Nfrac=087) +Fch=1863.60 (Fvco=3727.20, Nint=071, Nfrac=088) +Fch=1863.80 (Fvco=3727.60, Nint=071, Nfrac=089) +Fch=1864.00 (Fvco=3728.00, Nint=071, Nfrac=090) +Fch=1864.20 (Fvco=3728.40, Nint=071, Nfrac=091) +Fch=1864.40 (Fvco=3728.80, Nint=071, Nfrac=092) +Fch=1864.60 (Fvco=3729.20, Nint=071, Nfrac=093) +Fch=1864.80 (Fvco=3729.60, Nint=071, Nfrac=094) +Fch=1865.00 (Fvco=3730.00, Nint=071, Nfrac=095) +Fch=1865.20 (Fvco=3730.40, Nint=071, Nfrac=096) +Fch=1865.40 (Fvco=3730.80, Nint=071, Nfrac=097) +Fch=1865.60 (Fvco=3731.20, Nint=071, Nfrac=098) +Fch=1865.80 (Fvco=3731.60, Nint=071, Nfrac=099) +Fch=1866.00 (Fvco=3732.00, Nint=071, Nfrac=100) +Fch=1866.20 (Fvco=3732.40, Nint=071, Nfrac=101) +Fch=1866.40 (Fvco=3732.80, Nint=071, Nfrac=102) +Fch=1866.60 (Fvco=3733.20, Nint=071, Nfrac=103) +Fch=1866.80 (Fvco=3733.60, Nint=071, Nfrac=104) +Fch=1867.00 (Fvco=3734.00, Nint=071, Nfrac=105) +Fch=1867.20 (Fvco=3734.40, Nint=071, Nfrac=106) +Fch=1867.40 (Fvco=3734.80, Nint=071, Nfrac=107) +Fch=1867.60 (Fvco=3735.20, Nint=071, Nfrac=108) +Fch=1867.80 (Fvco=3735.60, Nint=071, Nfrac=109) +Fch=1868.00 (Fvco=3736.00, Nint=071, Nfrac=110) +Fch=1868.20 (Fvco=3736.40, Nint=071, Nfrac=111) +Fch=1868.40 (Fvco=3736.80, Nint=071, Nfrac=112) +Fch=1868.60 (Fvco=3737.20, Nint=071, Nfrac=113) +Fch=1868.80 (Fvco=3737.60, Nint=071, Nfrac=114) +Fch=1869.00 (Fvco=3738.00, Nint=071, Nfrac=115) +Fch=1869.20 (Fvco=3738.40, Nint=071, Nfrac=116) +Fch=1869.40 (Fvco=3738.80, Nint=071, Nfrac=117) +Fch=1869.60 (Fvco=3739.20, Nint=071, Nfrac=118) +Fch=1869.80 (Fvco=3739.60, Nint=071, Nfrac=119) +Fch=1870.00 (Fvco=3740.00, Nint=071, Nfrac=120) +Fch=1870.20 (Fvco=3740.40, Nint=071, Nfrac=121) +Fch=1870.40 (Fvco=3740.80, Nint=071, Nfrac=122) +Fch=1870.60 (Fvco=3741.20, Nint=071, Nfrac=123) +Fch=1870.80 (Fvco=3741.60, Nint=071, Nfrac=124) +Fch=1871.00 (Fvco=3742.00, Nint=071, Nfrac=125) +Fch=1871.20 (Fvco=3742.40, Nint=071, Nfrac=126) +Fch=1871.40 (Fvco=3742.80, Nint=071, Nfrac=127) +Fch=1871.60 (Fvco=3743.20, Nint=071, Nfrac=128) +Fch=1871.80 (Fvco=3743.60, Nint=071, Nfrac=129) +Fch=1872.00 (Fvco=3744.00, Nint=071, Nfrac=130) +Fch=1872.00 (Fvco=3744.00, Nint=072, Nfrac=000) +Fch=1872.20 (Fvco=3744.40, Nint=072, Nfrac=001) +Fch=1872.40 (Fvco=3744.80, Nint=072, Nfrac=002) +Fch=1872.60 (Fvco=3745.20, Nint=072, Nfrac=003) +Fch=1872.80 (Fvco=3745.60, Nint=072, Nfrac=004) +Fch=1873.00 (Fvco=3746.00, Nint=072, Nfrac=005) +Fch=1873.20 (Fvco=3746.40, Nint=072, Nfrac=006) +Fch=1873.40 (Fvco=3746.80, Nint=072, Nfrac=007) +Fch=1873.60 (Fvco=3747.20, Nint=072, Nfrac=008) +Fch=1873.80 (Fvco=3747.60, Nint=072, Nfrac=009) +Fch=1874.00 (Fvco=3748.00, Nint=072, Nfrac=010) +Fch=1874.20 (Fvco=3748.40, Nint=072, Nfrac=011) +Fch=1874.40 (Fvco=3748.80, Nint=072, Nfrac=012) +Fch=1874.60 (Fvco=3749.20, Nint=072, Nfrac=013) +Fch=1874.80 (Fvco=3749.60, Nint=072, Nfrac=014) +Fch=1875.00 (Fvco=3750.00, Nint=072, Nfrac=015) +Fch=1875.20 (Fvco=3750.40, Nint=072, Nfrac=016) +Fch=1875.40 (Fvco=3750.80, Nint=072, Nfrac=017) +Fch=1875.60 (Fvco=3751.20, Nint=072, Nfrac=018) +Fch=1875.80 (Fvco=3751.60, Nint=072, Nfrac=019) +Fch=1876.00 (Fvco=3752.00, Nint=072, Nfrac=020) +Fch=1876.20 (Fvco=3752.40, Nint=072, Nfrac=021) +Fch=1876.40 (Fvco=3752.80, Nint=072, Nfrac=022) +Fch=1876.60 (Fvco=3753.20, Nint=072, Nfrac=023) +Fch=1876.80 (Fvco=3753.60, Nint=072, Nfrac=024) +Fch=1877.00 (Fvco=3754.00, Nint=072, Nfrac=025) +Fch=1877.20 (Fvco=3754.40, Nint=072, Nfrac=026) +Fch=1877.40 (Fvco=3754.80, Nint=072, Nfrac=027) +Fch=1877.60 (Fvco=3755.20, Nint=072, Nfrac=028) +Fch=1877.80 (Fvco=3755.60, Nint=072, Nfrac=029) +Fch=1878.00 (Fvco=3756.00, Nint=072, Nfrac=030) +Fch=1878.20 (Fvco=3756.40, Nint=072, Nfrac=031) +Fch=1878.40 (Fvco=3756.80, Nint=072, Nfrac=032) +Fch=1878.60 (Fvco=3757.20, Nint=072, Nfrac=033) +Fch=1878.80 (Fvco=3757.60, Nint=072, Nfrac=034) +Fch=1879.00 (Fvco=3758.00, Nint=072, Nfrac=035) +Fch=1879.20 (Fvco=3758.40, Nint=072, Nfrac=036) +Fch=1879.40 (Fvco=3758.80, Nint=072, Nfrac=037) +Fch=1879.60 (Fvco=3759.20, Nint=072, Nfrac=038) +Fch=1879.80 (Fvco=3759.60, Nint=072, Nfrac=039) +Fch=1880.00 (Fvco=3760.00, Nint=072, Nfrac=040) +Fch=1880.20 (Fvco=3760.40, Nint=072, Nfrac=041) +Fch=1880.40 (Fvco=3760.80, Nint=072, Nfrac=042) +Fch=1880.60 (Fvco=3761.20, Nint=072, Nfrac=043) +Fch=1880.80 (Fvco=3761.60, Nint=072, Nfrac=044) +Fch=1881.00 (Fvco=3762.00, Nint=072, Nfrac=045) +Fch=1881.20 (Fvco=3762.40, Nint=072, Nfrac=046) +Fch=1881.40 (Fvco=3762.80, Nint=072, Nfrac=047) +Fch=1881.60 (Fvco=3763.20, Nint=072, Nfrac=048) +Fch=1881.80 (Fvco=3763.60, Nint=072, Nfrac=049) +Fch=1882.00 (Fvco=3764.00, Nint=072, Nfrac=050) +Fch=1882.20 (Fvco=3764.40, Nint=072, Nfrac=051) +Fch=1882.40 (Fvco=3764.80, Nint=072, Nfrac=052) +Fch=1882.60 (Fvco=3765.20, Nint=072, Nfrac=053) +Fch=1882.80 (Fvco=3765.60, Nint=072, Nfrac=054) +Fch=1883.00 (Fvco=3766.00, Nint=072, Nfrac=055) +Fch=1883.20 (Fvco=3766.40, Nint=072, Nfrac=056) +Fch=1883.40 (Fvco=3766.80, Nint=072, Nfrac=057) +Fch=1883.60 (Fvco=3767.20, Nint=072, Nfrac=058) +Fch=1883.80 (Fvco=3767.60, Nint=072, Nfrac=059) +Fch=1884.00 (Fvco=3768.00, Nint=072, Nfrac=060) +Fch=1884.20 (Fvco=3768.40, Nint=072, Nfrac=061) +Fch=1884.40 (Fvco=3768.80, Nint=072, Nfrac=062) +Fch=1884.60 (Fvco=3769.20, Nint=072, Nfrac=063) +Fch=1884.80 (Fvco=3769.60, Nint=072, Nfrac=064) +Fch=1885.00 (Fvco=3770.00, Nint=072, Nfrac=065) +Fch=1885.20 (Fvco=3770.40, Nint=072, Nfrac=066) +Fch=1885.40 (Fvco=3770.80, Nint=072, Nfrac=067) +Fch=1885.60 (Fvco=3771.20, Nint=072, Nfrac=068) +Fch=1885.80 (Fvco=3771.60, Nint=072, Nfrac=069) +Fch=1886.00 (Fvco=3772.00, Nint=072, Nfrac=070) +Fch=1886.20 (Fvco=3772.40, Nint=072, Nfrac=071) +Fch=1886.40 (Fvco=3772.80, Nint=072, Nfrac=072) +Fch=1886.60 (Fvco=3773.20, Nint=072, Nfrac=073) +Fch=1886.80 (Fvco=3773.60, Nint=072, Nfrac=074) +Fch=1887.00 (Fvco=3774.00, Nint=072, Nfrac=075) +Fch=1887.20 (Fvco=3774.40, Nint=072, Nfrac=076) +Fch=1887.40 (Fvco=3774.80, Nint=072, Nfrac=077) +Fch=1887.60 (Fvco=3775.20, Nint=072, Nfrac=078) +Fch=1887.80 (Fvco=3775.60, Nint=072, Nfrac=079) +Fch=1888.00 (Fvco=3776.00, Nint=072, Nfrac=080) +Fch=1888.20 (Fvco=3776.40, Nint=072, Nfrac=081) +Fch=1888.40 (Fvco=3776.80, Nint=072, Nfrac=082) +Fch=1888.60 (Fvco=3777.20, Nint=072, Nfrac=083) +Fch=1888.80 (Fvco=3777.60, Nint=072, Nfrac=084) +Fch=1889.00 (Fvco=3778.00, Nint=072, Nfrac=085) +Fch=1889.20 (Fvco=3778.40, Nint=072, Nfrac=086) +Fch=1889.40 (Fvco=3778.80, Nint=072, Nfrac=087) +Fch=1889.60 (Fvco=3779.20, Nint=072, Nfrac=088) +Fch=1889.80 (Fvco=3779.60, Nint=072, Nfrac=089) +Fch=1890.00 (Fvco=3780.00, Nint=072, Nfrac=090) +Fch=1890.20 (Fvco=3780.40, Nint=072, Nfrac=091) +Fch=1890.40 (Fvco=3780.80, Nint=072, Nfrac=092) +Fch=1890.60 (Fvco=3781.20, Nint=072, Nfrac=093) +Fch=1890.80 (Fvco=3781.60, Nint=072, Nfrac=094) +Fch=1891.00 (Fvco=3782.00, Nint=072, Nfrac=095) +Fch=1891.20 (Fvco=3782.40, Nint=072, Nfrac=096) +Fch=1891.40 (Fvco=3782.80, Nint=072, Nfrac=097) +Fch=1891.60 (Fvco=3783.20, Nint=072, Nfrac=098) +Fch=1891.80 (Fvco=3783.60, Nint=072, Nfrac=099) +Fch=1892.00 (Fvco=3784.00, Nint=072, Nfrac=100) +Fch=1892.20 (Fvco=3784.40, Nint=072, Nfrac=101) +Fch=1892.40 (Fvco=3784.80, Nint=072, Nfrac=102) +Fch=1892.60 (Fvco=3785.20, Nint=072, Nfrac=103) +Fch=1892.80 (Fvco=3785.60, Nint=072, Nfrac=104) +Fch=1893.00 (Fvco=3786.00, Nint=072, Nfrac=105) +Fch=1893.20 (Fvco=3786.40, Nint=072, Nfrac=106) +Fch=1893.40 (Fvco=3786.80, Nint=072, Nfrac=107) +Fch=1893.60 (Fvco=3787.20, Nint=072, Nfrac=108) +Fch=1893.80 (Fvco=3787.60, Nint=072, Nfrac=109) +Fch=1894.00 (Fvco=3788.00, Nint=072, Nfrac=110) +Fch=1894.20 (Fvco=3788.40, Nint=072, Nfrac=111) +Fch=1894.40 (Fvco=3788.80, Nint=072, Nfrac=112) +Fch=1894.60 (Fvco=3789.20, Nint=072, Nfrac=113) +Fch=1894.80 (Fvco=3789.60, Nint=072, Nfrac=114) +Fch=1895.00 (Fvco=3790.00, Nint=072, Nfrac=115) +Fch=1895.20 (Fvco=3790.40, Nint=072, Nfrac=116) +Fch=1895.40 (Fvco=3790.80, Nint=072, Nfrac=117) +Fch=1895.60 (Fvco=3791.20, Nint=072, Nfrac=118) +Fch=1895.80 (Fvco=3791.60, Nint=072, Nfrac=119) +Fch=1896.00 (Fvco=3792.00, Nint=072, Nfrac=120) +Fch=1896.20 (Fvco=3792.40, Nint=072, Nfrac=121) +Fch=1896.40 (Fvco=3792.80, Nint=072, Nfrac=122) +Fch=1896.60 (Fvco=3793.20, Nint=072, Nfrac=123) +Fch=1896.80 (Fvco=3793.60, Nint=072, Nfrac=124) +Fch=1897.00 (Fvco=3794.00, Nint=072, Nfrac=125) +Fch=1897.20 (Fvco=3794.40, Nint=072, Nfrac=126) +Fch=1897.40 (Fvco=3794.80, Nint=072, Nfrac=127) +Fch=1897.60 (Fvco=3795.20, Nint=072, Nfrac=128) +Fch=1897.80 (Fvco=3795.60, Nint=072, Nfrac=129) +Fch=1898.00 (Fvco=3796.00, Nint=072, Nfrac=130) +Fch=1898.00 (Fvco=3796.00, Nint=073, Nfrac=000) +Fch=1898.20 (Fvco=3796.40, Nint=073, Nfrac=001) +Fch=1898.40 (Fvco=3796.80, Nint=073, Nfrac=002) +Fch=1898.60 (Fvco=3797.20, Nint=073, Nfrac=003) +Fch=1898.80 (Fvco=3797.60, Nint=073, Nfrac=004) +Fch=1899.00 (Fvco=3798.00, Nint=073, Nfrac=005) +Fch=1899.20 (Fvco=3798.40, Nint=073, Nfrac=006) +Fch=1899.40 (Fvco=3798.80, Nint=073, Nfrac=007) +Fch=1899.60 (Fvco=3799.20, Nint=073, Nfrac=008) +Fch=1899.80 (Fvco=3799.60, Nint=073, Nfrac=009) +Fch=1900.00 (Fvco=3800.00, Nint=073, Nfrac=010) +Fch=1900.20 (Fvco=3800.40, Nint=073, Nfrac=011) +Fch=1900.40 (Fvco=3800.80, Nint=073, Nfrac=012) +Fch=1900.60 (Fvco=3801.20, Nint=073, Nfrac=013) +Fch=1900.80 (Fvco=3801.60, Nint=073, Nfrac=014) +Fch=1901.00 (Fvco=3802.00, Nint=073, Nfrac=015) +Fch=1901.20 (Fvco=3802.40, Nint=073, Nfrac=016) +Fch=1901.40 (Fvco=3802.80, Nint=073, Nfrac=017) +Fch=1901.60 (Fvco=3803.20, Nint=073, Nfrac=018) +Fch=1901.80 (Fvco=3803.60, Nint=073, Nfrac=019) +Fch=1902.00 (Fvco=3804.00, Nint=073, Nfrac=020) +Fch=1902.20 (Fvco=3804.40, Nint=073, Nfrac=021) +Fch=1902.40 (Fvco=3804.80, Nint=073, Nfrac=022) +Fch=1902.60 (Fvco=3805.20, Nint=073, Nfrac=023) +Fch=1902.80 (Fvco=3805.60, Nint=073, Nfrac=024) +Fch=1903.00 (Fvco=3806.00, Nint=073, Nfrac=025) +Fch=1903.20 (Fvco=3806.40, Nint=073, Nfrac=026) +Fch=1903.40 (Fvco=3806.80, Nint=073, Nfrac=027) +Fch=1903.60 (Fvco=3807.20, Nint=073, Nfrac=028) +Fch=1903.80 (Fvco=3807.60, Nint=073, Nfrac=029) +Fch=1904.00 (Fvco=3808.00, Nint=073, Nfrac=030) +Fch=1904.20 (Fvco=3808.40, Nint=073, Nfrac=031) +Fch=1904.40 (Fvco=3808.80, Nint=073, Nfrac=032) +Fch=1904.60 (Fvco=3809.20, Nint=073, Nfrac=033) +Fch=1904.80 (Fvco=3809.60, Nint=073, Nfrac=034) +Fch=1905.00 (Fvco=3810.00, Nint=073, Nfrac=035) +Fch=1905.20 (Fvco=3810.40, Nint=073, Nfrac=036) +Fch=1905.40 (Fvco=3810.80, Nint=073, Nfrac=037) +Fch=1905.60 (Fvco=3811.20, Nint=073, Nfrac=038) +Fch=1905.80 (Fvco=3811.60, Nint=073, Nfrac=039) +Fch=1906.00 (Fvco=3812.00, Nint=073, Nfrac=040) +Fch=1906.20 (Fvco=3812.40, Nint=073, Nfrac=041) +Fch=1906.40 (Fvco=3812.80, Nint=073, Nfrac=042) +Fch=1906.60 (Fvco=3813.20, Nint=073, Nfrac=043) +Fch=1906.80 (Fvco=3813.60, Nint=073, Nfrac=044) +Fch=1907.00 (Fvco=3814.00, Nint=073, Nfrac=045) +Fch=1907.20 (Fvco=3814.40, Nint=073, Nfrac=046) +Fch=1907.40 (Fvco=3814.80, Nint=073, Nfrac=047) +Fch=1907.60 (Fvco=3815.20, Nint=073, Nfrac=048) +Fch=1907.80 (Fvco=3815.60, Nint=073, Nfrac=049) +Fch=1908.00 (Fvco=3816.00, Nint=073, Nfrac=050) +Fch=1908.20 (Fvco=3816.40, Nint=073, Nfrac=051) +Fch=1908.40 (Fvco=3816.80, Nint=073, Nfrac=052) +Fch=1908.60 (Fvco=3817.20, Nint=073, Nfrac=053) +Fch=1908.80 (Fvco=3817.60, Nint=073, Nfrac=054) +Fch=1909.00 (Fvco=3818.00, Nint=073, Nfrac=055) +Fch=1909.20 (Fvco=3818.40, Nint=073, Nfrac=056) +Fch=1909.40 (Fvco=3818.80, Nint=073, Nfrac=057) +Fch=1909.60 (Fvco=3819.20, Nint=073, Nfrac=058) +Fch=1909.80 (Fvco=3819.60, Nint=073, Nfrac=059) +Fch=1910.00 (Fvco=3820.00, Nint=073, Nfrac=060) +Fch=1910.20 (Fvco=3820.40, Nint=073, Nfrac=061) +Fch=1910.40 (Fvco=3820.80, Nint=073, Nfrac=062) +Fch=1910.60 (Fvco=3821.20, Nint=073, Nfrac=063) +Fch=1910.80 (Fvco=3821.60, Nint=073, Nfrac=064) +Fch=1911.00 (Fvco=3822.00, Nint=073, Nfrac=065) +Fch=1911.20 (Fvco=3822.40, Nint=073, Nfrac=066) +Fch=1911.40 (Fvco=3822.80, Nint=073, Nfrac=067) +Fch=1911.60 (Fvco=3823.20, Nint=073, Nfrac=068) +Fch=1911.80 (Fvco=3823.60, Nint=073, Nfrac=069) +Fch=1912.00 (Fvco=3824.00, Nint=073, Nfrac=070) +Fch=1912.20 (Fvco=3824.40, Nint=073, Nfrac=071) +Fch=1912.40 (Fvco=3824.80, Nint=073, Nfrac=072) +Fch=1912.60 (Fvco=3825.20, Nint=073, Nfrac=073) +Fch=1912.80 (Fvco=3825.60, Nint=073, Nfrac=074) +Fch=1913.00 (Fvco=3826.00, Nint=073, Nfrac=075) +Fch=1913.20 (Fvco=3826.40, Nint=073, Nfrac=076) +Fch=1913.40 (Fvco=3826.80, Nint=073, Nfrac=077) +Fch=1913.60 (Fvco=3827.20, Nint=073, Nfrac=078) +Fch=1913.80 (Fvco=3827.60, Nint=073, Nfrac=079) +Fch=1914.00 (Fvco=3828.00, Nint=073, Nfrac=080) +Fch=1914.20 (Fvco=3828.40, Nint=073, Nfrac=081) +Fch=1914.40 (Fvco=3828.80, Nint=073, Nfrac=082) +Fch=1914.60 (Fvco=3829.20, Nint=073, Nfrac=083) +Fch=1914.80 (Fvco=3829.60, Nint=073, Nfrac=084) +Fch=1915.00 (Fvco=3830.00, Nint=073, Nfrac=085) +Fch=1915.20 (Fvco=3830.40, Nint=073, Nfrac=086) +Fch=1915.40 (Fvco=3830.80, Nint=073, Nfrac=087) +Fch=1915.60 (Fvco=3831.20, Nint=073, Nfrac=088) +Fch=1915.80 (Fvco=3831.60, Nint=073, Nfrac=089) +Fch=1916.00 (Fvco=3832.00, Nint=073, Nfrac=090) +Fch=1916.20 (Fvco=3832.40, Nint=073, Nfrac=091) +Fch=1916.40 (Fvco=3832.80, Nint=073, Nfrac=092) +Fch=1916.60 (Fvco=3833.20, Nint=073, Nfrac=093) +Fch=1916.80 (Fvco=3833.60, Nint=073, Nfrac=094) +Fch=1917.00 (Fvco=3834.00, Nint=073, Nfrac=095) +Fch=1917.20 (Fvco=3834.40, Nint=073, Nfrac=096) +Fch=1917.40 (Fvco=3834.80, Nint=073, Nfrac=097) +Fch=1917.60 (Fvco=3835.20, Nint=073, Nfrac=098) +Fch=1917.80 (Fvco=3835.60, Nint=073, Nfrac=099) +Fch=1918.00 (Fvco=3836.00, Nint=073, Nfrac=100) +Fch=1918.20 (Fvco=3836.40, Nint=073, Nfrac=101) +Fch=1918.40 (Fvco=3836.80, Nint=073, Nfrac=102) +Fch=1918.60 (Fvco=3837.20, Nint=073, Nfrac=103) +Fch=1918.80 (Fvco=3837.60, Nint=073, Nfrac=104) +Fch=1919.00 (Fvco=3838.00, Nint=073, Nfrac=105) +Fch=1919.20 (Fvco=3838.40, Nint=073, Nfrac=106) +Fch=1919.40 (Fvco=3838.80, Nint=073, Nfrac=107) +Fch=1919.60 (Fvco=3839.20, Nint=073, Nfrac=108) +Fch=1919.80 (Fvco=3839.60, Nint=073, Nfrac=109) +Fch=1920.00 (Fvco=3840.00, Nint=073, Nfrac=110) +Fch=1920.20 (Fvco=3840.40, Nint=073, Nfrac=111) +Fch=1920.40 (Fvco=3840.80, Nint=073, Nfrac=112) +Fch=1920.60 (Fvco=3841.20, Nint=073, Nfrac=113) +Fch=1920.80 (Fvco=3841.60, Nint=073, Nfrac=114) +Fch=1921.00 (Fvco=3842.00, Nint=073, Nfrac=115) +Fch=1921.20 (Fvco=3842.40, Nint=073, Nfrac=116) +Fch=1921.40 (Fvco=3842.80, Nint=073, Nfrac=117) +Fch=1921.60 (Fvco=3843.20, Nint=073, Nfrac=118) +Fch=1921.80 (Fvco=3843.60, Nint=073, Nfrac=119) +Fch=1922.00 (Fvco=3844.00, Nint=073, Nfrac=120) +Fch=1922.20 (Fvco=3844.40, Nint=073, Nfrac=121) +Fch=1922.40 (Fvco=3844.80, Nint=073, Nfrac=122) +Fch=1922.60 (Fvco=3845.20, Nint=073, Nfrac=123) +Fch=1922.80 (Fvco=3845.60, Nint=073, Nfrac=124) +Fch=1923.00 (Fvco=3846.00, Nint=073, Nfrac=125) +Fch=1923.20 (Fvco=3846.40, Nint=073, Nfrac=126) +Fch=1923.40 (Fvco=3846.80, Nint=073, Nfrac=127) +Fch=1923.60 (Fvco=3847.20, Nint=073, Nfrac=128) +Fch=1923.80 (Fvco=3847.60, Nint=073, Nfrac=129) +Fch=1924.00 (Fvco=3848.00, Nint=073, Nfrac=130) diff --git a/src/host/rita_pll/rita_pll.pl b/src/host/rita_pll/rita_pll.pl new file mode 100755 index 00000000..b5b09443 --- /dev/null +++ b/src/host/rita_pll/rita_pll.pl @@ -0,0 +1,115 @@ +#!/usr/bin/perl + +sub pll_rx($$$$$) { + my ($a, $b, $p, $r, $l) = @_; + + return (($b*$p+$a)/($r*$l))*26; +} + +sub pll_rx_low_band($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 65; my $l = 4; + return pll_rx($a, $b, $p, $r, $l); +} + +sub pll_rx_high_band($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 65; my $l = 2; + return pll_rx($a, $b, $p, $r, $l); +} + +sub pll_tx_gsm850_1($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 55; my $l = 4; my $m = 26; + + my $left = ((1/$l) - (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub pll_tx_gsm850_2($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 30; my $l = 4; my $m = 52; + + my $left = ((1/$l) - (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub pll_tx_gsm900($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 35; my $l = 4; my $m = 52; + + my $left = ((1/$l) + (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub pll_tx_high($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 70; my $l = 2; my $m = 26; + + my $left = ((1/$l) + (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub hr() { + printf("======================================================================\n"); +} + +printf("PLL Rx Low Band:\n"); +for (my $b = 135; $b <= 150; $b++) { +#for GSM 810 +#for (my $b = 132; $b <= 150; $b++) { + for (my $a = 0; $a <= 62; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_rx_low_band($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Rx High Band:\n"); +for (my $b = 141; $b <= 155; $b++) { + for (my $a = 0; $a <= 62; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_rx_high_band($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM850_1\n"); +for (my $b = 128; $b <= 130; $b++) { +#for GSM 810 +#for (my $b = 125; $b <= 130; $b++) { + for (my $a = 0; $a <= 62; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm850_1($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM850_2\n"); +for (my $b = 65; $b <= 66; $b++) { + for (my $a = 0; $a <= 63; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm850_2($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM900\n"); +for (my $b = 68; $b <= 71; $b++) { + for (my $a = 0; $a <= 63; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm900($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM1800/1900\n"); +for (my $b = 133; $b <= 149; $b++) { + for (my $a = 0; $a <= 63; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_high($a, $b), $a, $b); + } +} + diff --git a/src/host/rita_pll/rita_pll.txt b/src/host/rita_pll/rita_pll.txt new file mode 100644 index 00000000..cac2cac2 --- /dev/null +++ b/src/host/rita_pll/rita_pll.txt @@ -0,0 +1,3625 @@ +PLL Rx Low Band: +Fout=864.00 (A=000, B=135) +Fout=864.10 (A=001, B=135) +Fout=864.20 (A=002, B=135) +Fout=864.30 (A=003, B=135) +Fout=864.40 (A=004, B=135) +Fout=864.50 (A=005, B=135) +Fout=864.60 (A=006, B=135) +Fout=864.70 (A=007, B=135) +Fout=864.80 (A=008, B=135) +Fout=864.90 (A=009, B=135) +Fout=865.00 (A=010, B=135) +Fout=865.10 (A=011, B=135) +Fout=865.20 (A=012, B=135) +Fout=865.30 (A=013, B=135) +Fout=865.40 (A=014, B=135) +Fout=865.50 (A=015, B=135) +Fout=865.60 (A=016, B=135) +Fout=865.70 (A=017, B=135) +Fout=865.80 (A=018, B=135) +Fout=865.90 (A=019, B=135) +Fout=866.00 (A=020, B=135) +Fout=866.10 (A=021, B=135) +Fout=866.20 (A=022, B=135) +Fout=866.30 (A=023, B=135) +Fout=866.40 (A=024, B=135) +Fout=866.50 (A=025, B=135) +Fout=866.60 (A=026, B=135) +Fout=866.70 (A=027, B=135) +Fout=866.80 (A=028, B=135) +Fout=866.90 (A=029, B=135) +Fout=867.00 (A=030, B=135) +Fout=867.10 (A=031, B=135) +Fout=867.20 (A=032, B=135) +Fout=867.30 (A=033, B=135) +Fout=867.40 (A=034, B=135) +Fout=867.50 (A=035, B=135) +Fout=867.60 (A=036, B=135) +Fout=867.70 (A=037, B=135) +Fout=867.80 (A=038, B=135) +Fout=867.90 (A=039, B=135) +Fout=868.00 (A=040, B=135) +Fout=868.10 (A=041, B=135) +Fout=868.20 (A=042, B=135) +Fout=868.30 (A=043, B=135) +Fout=868.40 (A=044, B=135) +Fout=868.50 (A=045, B=135) +Fout=868.60 (A=046, B=135) +Fout=868.70 (A=047, B=135) +Fout=868.80 (A=048, B=135) +Fout=868.90 (A=049, B=135) +Fout=869.00 (A=050, B=135) +Fout=869.10 (A=051, B=135) +Fout=869.20 (A=052, B=135) +Fout=869.30 (A=053, B=135) +Fout=869.40 (A=054, B=135) +Fout=869.50 (A=055, B=135) +Fout=869.60 (A=056, B=135) +Fout=869.70 (A=057, B=135) +Fout=869.80 (A=058, B=135) +Fout=869.90 (A=059, B=135) +Fout=870.00 (A=060, B=135) +Fout=870.10 (A=061, B=135) +Fout=870.20 (A=062, B=135) +Fout=870.40 (A=000, B=136) +Fout=870.50 (A=001, B=136) +Fout=870.60 (A=002, B=136) +Fout=870.70 (A=003, B=136) +Fout=870.80 (A=004, B=136) +Fout=870.90 (A=005, B=136) +Fout=871.00 (A=006, B=136) +Fout=871.10 (A=007, B=136) +Fout=871.20 (A=008, B=136) +Fout=871.30 (A=009, B=136) +Fout=871.40 (A=010, B=136) +Fout=871.50 (A=011, B=136) +Fout=871.60 (A=012, B=136) +Fout=871.70 (A=013, B=136) +Fout=871.80 (A=014, B=136) +Fout=871.90 (A=015, B=136) +Fout=872.00 (A=016, B=136) +Fout=872.10 (A=017, B=136) +Fout=872.20 (A=018, B=136) +Fout=872.30 (A=019, B=136) +Fout=872.40 (A=020, B=136) +Fout=872.50 (A=021, B=136) +Fout=872.60 (A=022, B=136) +Fout=872.70 (A=023, B=136) +Fout=872.80 (A=024, B=136) +Fout=872.90 (A=025, B=136) +Fout=873.00 (A=026, B=136) +Fout=873.10 (A=027, B=136) +Fout=873.20 (A=028, B=136) +Fout=873.30 (A=029, B=136) +Fout=873.40 (A=030, B=136) +Fout=873.50 (A=031, B=136) +Fout=873.60 (A=032, B=136) +Fout=873.70 (A=033, B=136) +Fout=873.80 (A=034, B=136) +Fout=873.90 (A=035, B=136) +Fout=874.00 (A=036, B=136) +Fout=874.10 (A=037, B=136) +Fout=874.20 (A=038, B=136) +Fout=874.30 (A=039, B=136) +Fout=874.40 (A=040, B=136) +Fout=874.50 (A=041, B=136) +Fout=874.60 (A=042, B=136) +Fout=874.70 (A=043, B=136) +Fout=874.80 (A=044, B=136) +Fout=874.90 (A=045, B=136) +Fout=875.00 (A=046, B=136) +Fout=875.10 (A=047, B=136) +Fout=875.20 (A=048, B=136) +Fout=875.30 (A=049, B=136) +Fout=875.40 (A=050, B=136) +Fout=875.50 (A=051, B=136) +Fout=875.60 (A=052, B=136) +Fout=875.70 (A=053, B=136) +Fout=875.80 (A=054, B=136) +Fout=875.90 (A=055, B=136) +Fout=876.00 (A=056, B=136) +Fout=876.10 (A=057, B=136) +Fout=876.20 (A=058, B=136) +Fout=876.30 (A=059, B=136) +Fout=876.40 (A=060, B=136) +Fout=876.50 (A=061, B=136) +Fout=876.60 (A=062, B=136) +Fout=876.80 (A=000, B=137) +Fout=876.90 (A=001, B=137) +Fout=877.00 (A=002, B=137) +Fout=877.10 (A=003, B=137) +Fout=877.20 (A=004, B=137) +Fout=877.30 (A=005, B=137) +Fout=877.40 (A=006, B=137) +Fout=877.50 (A=007, B=137) +Fout=877.60 (A=008, B=137) +Fout=877.70 (A=009, B=137) +Fout=877.80 (A=010, B=137) +Fout=877.90 (A=011, B=137) +Fout=878.00 (A=012, B=137) +Fout=878.10 (A=013, B=137) +Fout=878.20 (A=014, B=137) +Fout=878.30 (A=015, B=137) +Fout=878.40 (A=016, B=137) +Fout=878.50 (A=017, B=137) +Fout=878.60 (A=018, B=137) +Fout=878.70 (A=019, B=137) +Fout=878.80 (A=020, B=137) +Fout=878.90 (A=021, B=137) +Fout=879.00 (A=022, B=137) +Fout=879.10 (A=023, B=137) +Fout=879.20 (A=024, B=137) +Fout=879.30 (A=025, B=137) +Fout=879.40 (A=026, B=137) +Fout=879.50 (A=027, B=137) +Fout=879.60 (A=028, B=137) +Fout=879.70 (A=029, B=137) +Fout=879.80 (A=030, B=137) +Fout=879.90 (A=031, B=137) +Fout=880.00 (A=032, B=137) +Fout=880.10 (A=033, B=137) +Fout=880.20 (A=034, B=137) +Fout=880.30 (A=035, B=137) +Fout=880.40 (A=036, B=137) +Fout=880.50 (A=037, B=137) +Fout=880.60 (A=038, B=137) +Fout=880.70 (A=039, B=137) +Fout=880.80 (A=040, B=137) +Fout=880.90 (A=041, B=137) +Fout=881.00 (A=042, B=137) +Fout=881.10 (A=043, B=137) +Fout=881.20 (A=044, B=137) +Fout=881.30 (A=045, B=137) +Fout=881.40 (A=046, B=137) +Fout=881.50 (A=047, B=137) +Fout=881.60 (A=048, B=137) +Fout=881.70 (A=049, B=137) +Fout=881.80 (A=050, B=137) +Fout=881.90 (A=051, B=137) +Fout=882.00 (A=052, B=137) +Fout=882.10 (A=053, B=137) +Fout=882.20 (A=054, B=137) +Fout=882.30 (A=055, B=137) +Fout=882.40 (A=056, B=137) +Fout=882.50 (A=057, B=137) +Fout=882.60 (A=058, B=137) +Fout=882.70 (A=059, B=137) +Fout=882.80 (A=060, B=137) +Fout=882.90 (A=061, B=137) +Fout=883.00 (A=062, B=137) +Fout=883.20 (A=000, B=138) +Fout=883.30 (A=001, B=138) +Fout=883.40 (A=002, B=138) +Fout=883.50 (A=003, B=138) +Fout=883.60 (A=004, B=138) +Fout=883.70 (A=005, B=138) +Fout=883.80 (A=006, B=138) +Fout=883.90 (A=007, B=138) +Fout=884.00 (A=008, B=138) +Fout=884.10 (A=009, B=138) +Fout=884.20 (A=010, B=138) +Fout=884.30 (A=011, B=138) +Fout=884.40 (A=012, B=138) +Fout=884.50 (A=013, B=138) +Fout=884.60 (A=014, B=138) +Fout=884.70 (A=015, B=138) +Fout=884.80 (A=016, B=138) +Fout=884.90 (A=017, B=138) +Fout=885.00 (A=018, B=138) +Fout=885.10 (A=019, B=138) +Fout=885.20 (A=020, B=138) +Fout=885.30 (A=021, B=138) +Fout=885.40 (A=022, B=138) +Fout=885.50 (A=023, B=138) +Fout=885.60 (A=024, B=138) +Fout=885.70 (A=025, B=138) +Fout=885.80 (A=026, B=138) +Fout=885.90 (A=027, B=138) +Fout=886.00 (A=028, B=138) +Fout=886.10 (A=029, B=138) +Fout=886.20 (A=030, B=138) +Fout=886.30 (A=031, B=138) +Fout=886.40 (A=032, B=138) +Fout=886.50 (A=033, B=138) +Fout=886.60 (A=034, B=138) +Fout=886.70 (A=035, B=138) +Fout=886.80 (A=036, B=138) +Fout=886.90 (A=037, B=138) +Fout=887.00 (A=038, B=138) +Fout=887.10 (A=039, B=138) +Fout=887.20 (A=040, B=138) +Fout=887.30 (A=041, B=138) +Fout=887.40 (A=042, B=138) +Fout=887.50 (A=043, B=138) +Fout=887.60 (A=044, B=138) +Fout=887.70 (A=045, B=138) +Fout=887.80 (A=046, B=138) +Fout=887.90 (A=047, B=138) +Fout=888.00 (A=048, B=138) +Fout=888.10 (A=049, B=138) +Fout=888.20 (A=050, B=138) +Fout=888.30 (A=051, B=138) +Fout=888.40 (A=052, B=138) +Fout=888.50 (A=053, B=138) +Fout=888.60 (A=054, B=138) +Fout=888.70 (A=055, B=138) +Fout=888.80 (A=056, B=138) +Fout=888.90 (A=057, B=138) +Fout=889.00 (A=058, B=138) +Fout=889.10 (A=059, B=138) +Fout=889.20 (A=060, B=138) +Fout=889.30 (A=061, B=138) +Fout=889.40 (A=062, B=138) +Fout=889.60 (A=000, B=139) +Fout=889.70 (A=001, B=139) +Fout=889.80 (A=002, B=139) +Fout=889.90 (A=003, B=139) +Fout=890.00 (A=004, B=139) +Fout=890.10 (A=005, B=139) +Fout=890.20 (A=006, B=139) +Fout=890.30 (A=007, B=139) +Fout=890.40 (A=008, B=139) +Fout=890.50 (A=009, B=139) +Fout=890.60 (A=010, B=139) +Fout=890.70 (A=011, B=139) +Fout=890.80 (A=012, B=139) +Fout=890.90 (A=013, B=139) +Fout=891.00 (A=014, B=139) +Fout=891.10 (A=015, B=139) +Fout=891.20 (A=016, B=139) +Fout=891.30 (A=017, B=139) +Fout=891.40 (A=018, B=139) +Fout=891.50 (A=019, B=139) +Fout=891.60 (A=020, B=139) +Fout=891.70 (A=021, B=139) +Fout=891.80 (A=022, B=139) +Fout=891.90 (A=023, B=139) +Fout=892.00 (A=024, B=139) +Fout=892.10 (A=025, B=139) +Fout=892.20 (A=026, B=139) +Fout=892.30 (A=027, B=139) +Fout=892.40 (A=028, B=139) +Fout=892.50 (A=029, B=139) +Fout=892.60 (A=030, B=139) +Fout=892.70 (A=031, B=139) +Fout=892.80 (A=032, B=139) +Fout=892.90 (A=033, B=139) +Fout=893.00 (A=034, B=139) +Fout=893.10 (A=035, B=139) +Fout=893.20 (A=036, B=139) +Fout=893.30 (A=037, B=139) +Fout=893.40 (A=038, B=139) +Fout=893.50 (A=039, B=139) +Fout=893.60 (A=040, B=139) +Fout=893.70 (A=041, B=139) +Fout=893.80 (A=042, B=139) +Fout=893.90 (A=043, B=139) +Fout=894.00 (A=044, B=139) +Fout=894.10 (A=045, B=139) +Fout=894.20 (A=046, B=139) +Fout=894.30 (A=047, B=139) +Fout=894.40 (A=048, B=139) +Fout=894.50 (A=049, B=139) +Fout=894.60 (A=050, B=139) +Fout=894.70 (A=051, B=139) +Fout=894.80 (A=052, B=139) +Fout=894.90 (A=053, B=139) +Fout=895.00 (A=054, B=139) +Fout=895.10 (A=055, B=139) +Fout=895.20 (A=056, B=139) +Fout=895.30 (A=057, B=139) +Fout=895.40 (A=058, B=139) +Fout=895.50 (A=059, B=139) +Fout=895.60 (A=060, B=139) +Fout=895.70 (A=061, B=139) +Fout=895.80 (A=062, B=139) +Fout=896.00 (A=000, B=140) +Fout=896.10 (A=001, B=140) +Fout=896.20 (A=002, B=140) +Fout=896.30 (A=003, B=140) +Fout=896.40 (A=004, B=140) +Fout=896.50 (A=005, B=140) +Fout=896.60 (A=006, B=140) +Fout=896.70 (A=007, B=140) +Fout=896.80 (A=008, B=140) +Fout=896.90 (A=009, B=140) +Fout=897.00 (A=010, B=140) +Fout=897.10 (A=011, B=140) +Fout=897.20 (A=012, B=140) +Fout=897.30 (A=013, B=140) +Fout=897.40 (A=014, B=140) +Fout=897.50 (A=015, B=140) +Fout=897.60 (A=016, B=140) +Fout=897.70 (A=017, B=140) +Fout=897.80 (A=018, B=140) +Fout=897.90 (A=019, B=140) +Fout=898.00 (A=020, B=140) +Fout=898.10 (A=021, B=140) +Fout=898.20 (A=022, B=140) +Fout=898.30 (A=023, B=140) +Fout=898.40 (A=024, B=140) +Fout=898.50 (A=025, B=140) +Fout=898.60 (A=026, B=140) +Fout=898.70 (A=027, B=140) +Fout=898.80 (A=028, B=140) +Fout=898.90 (A=029, B=140) +Fout=899.00 (A=030, B=140) +Fout=899.10 (A=031, B=140) +Fout=899.20 (A=032, B=140) +Fout=899.30 (A=033, B=140) +Fout=899.40 (A=034, B=140) +Fout=899.50 (A=035, B=140) +Fout=899.60 (A=036, B=140) +Fout=899.70 (A=037, B=140) +Fout=899.80 (A=038, B=140) +Fout=899.90 (A=039, B=140) +Fout=900.00 (A=040, B=140) +Fout=900.10 (A=041, B=140) +Fout=900.20 (A=042, B=140) +Fout=900.30 (A=043, B=140) +Fout=900.40 (A=044, B=140) +Fout=900.50 (A=045, B=140) +Fout=900.60 (A=046, B=140) +Fout=900.70 (A=047, B=140) +Fout=900.80 (A=048, B=140) +Fout=900.90 (A=049, B=140) +Fout=901.00 (A=050, B=140) +Fout=901.10 (A=051, B=140) +Fout=901.20 (A=052, B=140) +Fout=901.30 (A=053, B=140) +Fout=901.40 (A=054, B=140) +Fout=901.50 (A=055, B=140) +Fout=901.60 (A=056, B=140) +Fout=901.70 (A=057, B=140) +Fout=901.80 (A=058, B=140) +Fout=901.90 (A=059, B=140) +Fout=902.00 (A=060, B=140) +Fout=902.10 (A=061, B=140) +Fout=902.20 (A=062, B=140) +Fout=902.40 (A=000, B=141) +Fout=902.50 (A=001, B=141) +Fout=902.60 (A=002, B=141) +Fout=902.70 (A=003, B=141) +Fout=902.80 (A=004, B=141) +Fout=902.90 (A=005, B=141) +Fout=903.00 (A=006, B=141) +Fout=903.10 (A=007, B=141) +Fout=903.20 (A=008, B=141) +Fout=903.30 (A=009, B=141) +Fout=903.40 (A=010, B=141) +Fout=903.50 (A=011, B=141) +Fout=903.60 (A=012, B=141) +Fout=903.70 (A=013, B=141) +Fout=903.80 (A=014, B=141) +Fout=903.90 (A=015, B=141) +Fout=904.00 (A=016, B=141) +Fout=904.10 (A=017, B=141) +Fout=904.20 (A=018, B=141) +Fout=904.30 (A=019, B=141) +Fout=904.40 (A=020, B=141) +Fout=904.50 (A=021, B=141) +Fout=904.60 (A=022, B=141) +Fout=904.70 (A=023, B=141) +Fout=904.80 (A=024, B=141) +Fout=904.90 (A=025, B=141) +Fout=905.00 (A=026, B=141) +Fout=905.10 (A=027, B=141) +Fout=905.20 (A=028, B=141) +Fout=905.30 (A=029, B=141) +Fout=905.40 (A=030, B=141) +Fout=905.50 (A=031, B=141) +Fout=905.60 (A=032, B=141) +Fout=905.70 (A=033, B=141) +Fout=905.80 (A=034, B=141) +Fout=905.90 (A=035, B=141) +Fout=906.00 (A=036, B=141) +Fout=906.10 (A=037, B=141) +Fout=906.20 (A=038, B=141) +Fout=906.30 (A=039, B=141) +Fout=906.40 (A=040, B=141) +Fout=906.50 (A=041, B=141) +Fout=906.60 (A=042, B=141) +Fout=906.70 (A=043, B=141) +Fout=906.80 (A=044, B=141) +Fout=906.90 (A=045, B=141) +Fout=907.00 (A=046, B=141) +Fout=907.10 (A=047, B=141) +Fout=907.20 (A=048, B=141) +Fout=907.30 (A=049, B=141) +Fout=907.40 (A=050, B=141) +Fout=907.50 (A=051, B=141) +Fout=907.60 (A=052, B=141) +Fout=907.70 (A=053, B=141) +Fout=907.80 (A=054, B=141) +Fout=907.90 (A=055, B=141) +Fout=908.00 (A=056, B=141) +Fout=908.10 (A=057, B=141) +Fout=908.20 (A=058, B=141) +Fout=908.30 (A=059, B=141) +Fout=908.40 (A=060, B=141) +Fout=908.50 (A=061, B=141) +Fout=908.60 (A=062, B=141) +Fout=908.80 (A=000, B=142) +Fout=908.90 (A=001, B=142) +Fout=909.00 (A=002, B=142) +Fout=909.10 (A=003, B=142) +Fout=909.20 (A=004, B=142) +Fout=909.30 (A=005, B=142) +Fout=909.40 (A=006, B=142) +Fout=909.50 (A=007, B=142) +Fout=909.60 (A=008, B=142) +Fout=909.70 (A=009, B=142) +Fout=909.80 (A=010, B=142) +Fout=909.90 (A=011, B=142) +Fout=910.00 (A=012, B=142) +Fout=910.10 (A=013, B=142) +Fout=910.20 (A=014, B=142) +Fout=910.30 (A=015, B=142) +Fout=910.40 (A=016, B=142) +Fout=910.50 (A=017, B=142) +Fout=910.60 (A=018, B=142) +Fout=910.70 (A=019, B=142) +Fout=910.80 (A=020, B=142) +Fout=910.90 (A=021, B=142) +Fout=911.00 (A=022, B=142) +Fout=911.10 (A=023, B=142) +Fout=911.20 (A=024, B=142) +Fout=911.30 (A=025, B=142) +Fout=911.40 (A=026, B=142) +Fout=911.50 (A=027, B=142) +Fout=911.60 (A=028, B=142) +Fout=911.70 (A=029, B=142) +Fout=911.80 (A=030, B=142) +Fout=911.90 (A=031, B=142) +Fout=912.00 (A=032, B=142) +Fout=912.10 (A=033, B=142) +Fout=912.20 (A=034, B=142) +Fout=912.30 (A=035, B=142) +Fout=912.40 (A=036, B=142) +Fout=912.50 (A=037, B=142) +Fout=912.60 (A=038, B=142) +Fout=912.70 (A=039, B=142) +Fout=912.80 (A=040, B=142) +Fout=912.90 (A=041, B=142) +Fout=913.00 (A=042, B=142) +Fout=913.10 (A=043, B=142) +Fout=913.20 (A=044, B=142) +Fout=913.30 (A=045, B=142) +Fout=913.40 (A=046, B=142) +Fout=913.50 (A=047, B=142) +Fout=913.60 (A=048, B=142) +Fout=913.70 (A=049, B=142) +Fout=913.80 (A=050, B=142) +Fout=913.90 (A=051, B=142) +Fout=914.00 (A=052, B=142) +Fout=914.10 (A=053, B=142) +Fout=914.20 (A=054, B=142) +Fout=914.30 (A=055, B=142) +Fout=914.40 (A=056, B=142) +Fout=914.50 (A=057, B=142) +Fout=914.60 (A=058, B=142) +Fout=914.70 (A=059, B=142) +Fout=914.80 (A=060, B=142) +Fout=914.90 (A=061, B=142) +Fout=915.00 (A=062, B=142) +Fout=915.20 (A=000, B=143) +Fout=915.30 (A=001, B=143) +Fout=915.40 (A=002, B=143) +Fout=915.50 (A=003, B=143) +Fout=915.60 (A=004, B=143) +Fout=915.70 (A=005, B=143) +Fout=915.80 (A=006, B=143) +Fout=915.90 (A=007, B=143) +Fout=916.00 (A=008, B=143) +Fout=916.10 (A=009, B=143) +Fout=916.20 (A=010, B=143) +Fout=916.30 (A=011, B=143) +Fout=916.40 (A=012, B=143) +Fout=916.50 (A=013, B=143) +Fout=916.60 (A=014, B=143) +Fout=916.70 (A=015, B=143) +Fout=916.80 (A=016, B=143) +Fout=916.90 (A=017, B=143) +Fout=917.00 (A=018, B=143) +Fout=917.10 (A=019, B=143) +Fout=917.20 (A=020, B=143) +Fout=917.30 (A=021, B=143) +Fout=917.40 (A=022, B=143) +Fout=917.50 (A=023, B=143) +Fout=917.60 (A=024, B=143) +Fout=917.70 (A=025, B=143) +Fout=917.80 (A=026, B=143) +Fout=917.90 (A=027, B=143) +Fout=918.00 (A=028, B=143) +Fout=918.10 (A=029, B=143) +Fout=918.20 (A=030, B=143) +Fout=918.30 (A=031, B=143) +Fout=918.40 (A=032, B=143) +Fout=918.50 (A=033, B=143) +Fout=918.60 (A=034, B=143) +Fout=918.70 (A=035, B=143) +Fout=918.80 (A=036, B=143) +Fout=918.90 (A=037, B=143) +Fout=919.00 (A=038, B=143) +Fout=919.10 (A=039, B=143) +Fout=919.20 (A=040, B=143) +Fout=919.30 (A=041, B=143) +Fout=919.40 (A=042, B=143) +Fout=919.50 (A=043, B=143) +Fout=919.60 (A=044, B=143) +Fout=919.70 (A=045, B=143) +Fout=919.80 (A=046, B=143) +Fout=919.90 (A=047, B=143) +Fout=920.00 (A=048, B=143) +Fout=920.10 (A=049, B=143) +Fout=920.20 (A=050, B=143) +Fout=920.30 (A=051, B=143) +Fout=920.40 (A=052, B=143) +Fout=920.50 (A=053, B=143) +Fout=920.60 (A=054, B=143) +Fout=920.70 (A=055, B=143) +Fout=920.80 (A=056, B=143) +Fout=920.90 (A=057, B=143) +Fout=921.00 (A=058, B=143) +Fout=921.10 (A=059, B=143) +Fout=921.20 (A=060, B=143) +Fout=921.30 (A=061, B=143) +Fout=921.40 (A=062, B=143) +Fout=921.60 (A=000, B=144) +Fout=921.70 (A=001, B=144) +Fout=921.80 (A=002, B=144) +Fout=921.90 (A=003, B=144) +Fout=922.00 (A=004, B=144) +Fout=922.10 (A=005, B=144) +Fout=922.20 (A=006, B=144) +Fout=922.30 (A=007, B=144) +Fout=922.40 (A=008, B=144) +Fout=922.50 (A=009, B=144) +Fout=922.60 (A=010, B=144) +Fout=922.70 (A=011, B=144) +Fout=922.80 (A=012, B=144) +Fout=922.90 (A=013, B=144) +Fout=923.00 (A=014, B=144) +Fout=923.10 (A=015, B=144) +Fout=923.20 (A=016, B=144) +Fout=923.30 (A=017, B=144) +Fout=923.40 (A=018, B=144) +Fout=923.50 (A=019, B=144) +Fout=923.60 (A=020, B=144) +Fout=923.70 (A=021, B=144) +Fout=923.80 (A=022, B=144) +Fout=923.90 (A=023, B=144) +Fout=924.00 (A=024, B=144) +Fout=924.10 (A=025, B=144) +Fout=924.20 (A=026, B=144) +Fout=924.30 (A=027, B=144) +Fout=924.40 (A=028, B=144) +Fout=924.50 (A=029, B=144) +Fout=924.60 (A=030, B=144) +Fout=924.70 (A=031, B=144) +Fout=924.80 (A=032, B=144) +Fout=924.90 (A=033, B=144) +Fout=925.00 (A=034, B=144) +Fout=925.10 (A=035, B=144) +Fout=925.20 (A=036, B=144) +Fout=925.30 (A=037, B=144) +Fout=925.40 (A=038, B=144) +Fout=925.50 (A=039, B=144) +Fout=925.60 (A=040, B=144) +Fout=925.70 (A=041, B=144) +Fout=925.80 (A=042, B=144) +Fout=925.90 (A=043, B=144) +Fout=926.00 (A=044, B=144) +Fout=926.10 (A=045, B=144) +Fout=926.20 (A=046, B=144) +Fout=926.30 (A=047, B=144) +Fout=926.40 (A=048, B=144) +Fout=926.50 (A=049, B=144) +Fout=926.60 (A=050, B=144) +Fout=926.70 (A=051, B=144) +Fout=926.80 (A=052, B=144) +Fout=926.90 (A=053, B=144) +Fout=927.00 (A=054, B=144) +Fout=927.10 (A=055, B=144) +Fout=927.20 (A=056, B=144) +Fout=927.30 (A=057, B=144) +Fout=927.40 (A=058, B=144) +Fout=927.50 (A=059, B=144) +Fout=927.60 (A=060, B=144) +Fout=927.70 (A=061, B=144) +Fout=927.80 (A=062, B=144) +Fout=928.00 (A=000, B=145) +Fout=928.10 (A=001, B=145) +Fout=928.20 (A=002, B=145) +Fout=928.30 (A=003, B=145) +Fout=928.40 (A=004, B=145) +Fout=928.50 (A=005, B=145) +Fout=928.60 (A=006, B=145) +Fout=928.70 (A=007, B=145) +Fout=928.80 (A=008, B=145) +Fout=928.90 (A=009, B=145) +Fout=929.00 (A=010, B=145) +Fout=929.10 (A=011, B=145) +Fout=929.20 (A=012, B=145) +Fout=929.30 (A=013, B=145) +Fout=929.40 (A=014, B=145) +Fout=929.50 (A=015, B=145) +Fout=929.60 (A=016, B=145) +Fout=929.70 (A=017, B=145) +Fout=929.80 (A=018, B=145) +Fout=929.90 (A=019, B=145) +Fout=930.00 (A=020, B=145) +Fout=930.10 (A=021, B=145) +Fout=930.20 (A=022, B=145) +Fout=930.30 (A=023, B=145) +Fout=930.40 (A=024, B=145) +Fout=930.50 (A=025, B=145) +Fout=930.60 (A=026, B=145) +Fout=930.70 (A=027, B=145) +Fout=930.80 (A=028, B=145) +Fout=930.90 (A=029, B=145) +Fout=931.00 (A=030, B=145) +Fout=931.10 (A=031, B=145) +Fout=931.20 (A=032, B=145) +Fout=931.30 (A=033, B=145) +Fout=931.40 (A=034, B=145) +Fout=931.50 (A=035, B=145) +Fout=931.60 (A=036, B=145) +Fout=931.70 (A=037, B=145) +Fout=931.80 (A=038, B=145) +Fout=931.90 (A=039, B=145) +Fout=932.00 (A=040, B=145) +Fout=932.10 (A=041, B=145) +Fout=932.20 (A=042, B=145) +Fout=932.30 (A=043, B=145) +Fout=932.40 (A=044, B=145) +Fout=932.50 (A=045, B=145) +Fout=932.60 (A=046, B=145) +Fout=932.70 (A=047, B=145) +Fout=932.80 (A=048, B=145) +Fout=932.90 (A=049, B=145) +Fout=933.00 (A=050, B=145) +Fout=933.10 (A=051, B=145) +Fout=933.20 (A=052, B=145) +Fout=933.30 (A=053, B=145) +Fout=933.40 (A=054, B=145) +Fout=933.50 (A=055, B=145) +Fout=933.60 (A=056, B=145) +Fout=933.70 (A=057, B=145) +Fout=933.80 (A=058, B=145) +Fout=933.90 (A=059, B=145) +Fout=934.00 (A=060, B=145) +Fout=934.10 (A=061, B=145) +Fout=934.20 (A=062, B=145) +Fout=934.40 (A=000, B=146) +Fout=934.50 (A=001, B=146) +Fout=934.60 (A=002, B=146) +Fout=934.70 (A=003, B=146) +Fout=934.80 (A=004, B=146) +Fout=934.90 (A=005, B=146) +Fout=935.00 (A=006, B=146) +Fout=935.10 (A=007, B=146) +Fout=935.20 (A=008, B=146) +Fout=935.30 (A=009, B=146) +Fout=935.40 (A=010, B=146) +Fout=935.50 (A=011, B=146) +Fout=935.60 (A=012, B=146) +Fout=935.70 (A=013, B=146) +Fout=935.80 (A=014, B=146) +Fout=935.90 (A=015, B=146) +Fout=936.00 (A=016, B=146) +Fout=936.10 (A=017, B=146) +Fout=936.20 (A=018, B=146) +Fout=936.30 (A=019, B=146) +Fout=936.40 (A=020, B=146) +Fout=936.50 (A=021, B=146) +Fout=936.60 (A=022, B=146) +Fout=936.70 (A=023, B=146) +Fout=936.80 (A=024, B=146) +Fout=936.90 (A=025, B=146) +Fout=937.00 (A=026, B=146) +Fout=937.10 (A=027, B=146) +Fout=937.20 (A=028, B=146) +Fout=937.30 (A=029, B=146) +Fout=937.40 (A=030, B=146) +Fout=937.50 (A=031, B=146) +Fout=937.60 (A=032, B=146) +Fout=937.70 (A=033, B=146) +Fout=937.80 (A=034, B=146) +Fout=937.90 (A=035, B=146) +Fout=938.00 (A=036, B=146) +Fout=938.10 (A=037, B=146) +Fout=938.20 (A=038, B=146) +Fout=938.30 (A=039, B=146) +Fout=938.40 (A=040, B=146) +Fout=938.50 (A=041, B=146) +Fout=938.60 (A=042, B=146) +Fout=938.70 (A=043, B=146) +Fout=938.80 (A=044, B=146) +Fout=938.90 (A=045, B=146) +Fout=939.00 (A=046, B=146) +Fout=939.10 (A=047, B=146) +Fout=939.20 (A=048, B=146) +Fout=939.30 (A=049, B=146) +Fout=939.40 (A=050, B=146) +Fout=939.50 (A=051, B=146) +Fout=939.60 (A=052, B=146) +Fout=939.70 (A=053, B=146) +Fout=939.80 (A=054, B=146) +Fout=939.90 (A=055, B=146) +Fout=940.00 (A=056, B=146) +Fout=940.10 (A=057, B=146) +Fout=940.20 (A=058, B=146) +Fout=940.30 (A=059, B=146) +Fout=940.40 (A=060, B=146) +Fout=940.50 (A=061, B=146) +Fout=940.60 (A=062, B=146) +Fout=940.80 (A=000, B=147) +Fout=940.90 (A=001, B=147) +Fout=941.00 (A=002, B=147) +Fout=941.10 (A=003, B=147) +Fout=941.20 (A=004, B=147) +Fout=941.30 (A=005, B=147) +Fout=941.40 (A=006, B=147) +Fout=941.50 (A=007, B=147) +Fout=941.60 (A=008, B=147) +Fout=941.70 (A=009, B=147) +Fout=941.80 (A=010, B=147) +Fout=941.90 (A=011, B=147) +Fout=942.00 (A=012, B=147) +Fout=942.10 (A=013, B=147) +Fout=942.20 (A=014, B=147) +Fout=942.30 (A=015, B=147) +Fout=942.40 (A=016, B=147) +Fout=942.50 (A=017, B=147) +Fout=942.60 (A=018, B=147) +Fout=942.70 (A=019, B=147) +Fout=942.80 (A=020, B=147) +Fout=942.90 (A=021, B=147) +Fout=943.00 (A=022, B=147) +Fout=943.10 (A=023, B=147) +Fout=943.20 (A=024, B=147) +Fout=943.30 (A=025, B=147) +Fout=943.40 (A=026, B=147) +Fout=943.50 (A=027, B=147) +Fout=943.60 (A=028, B=147) +Fout=943.70 (A=029, B=147) +Fout=943.80 (A=030, B=147) +Fout=943.90 (A=031, B=147) +Fout=944.00 (A=032, B=147) +Fout=944.10 (A=033, B=147) +Fout=944.20 (A=034, B=147) +Fout=944.30 (A=035, B=147) +Fout=944.40 (A=036, B=147) +Fout=944.50 (A=037, B=147) +Fout=944.60 (A=038, B=147) +Fout=944.70 (A=039, B=147) +Fout=944.80 (A=040, B=147) +Fout=944.90 (A=041, B=147) +Fout=945.00 (A=042, B=147) +Fout=945.10 (A=043, B=147) +Fout=945.20 (A=044, B=147) +Fout=945.30 (A=045, B=147) +Fout=945.40 (A=046, B=147) +Fout=945.50 (A=047, B=147) +Fout=945.60 (A=048, B=147) +Fout=945.70 (A=049, B=147) +Fout=945.80 (A=050, B=147) +Fout=945.90 (A=051, B=147) +Fout=946.00 (A=052, B=147) +Fout=946.10 (A=053, B=147) +Fout=946.20 (A=054, B=147) +Fout=946.30 (A=055, B=147) +Fout=946.40 (A=056, B=147) +Fout=946.50 (A=057, B=147) +Fout=946.60 (A=058, B=147) +Fout=946.70 (A=059, B=147) +Fout=946.80 (A=060, B=147) +Fout=946.90 (A=061, B=147) +Fout=947.00 (A=062, B=147) +Fout=947.20 (A=000, B=148) +Fout=947.30 (A=001, B=148) +Fout=947.40 (A=002, B=148) +Fout=947.50 (A=003, B=148) +Fout=947.60 (A=004, B=148) +Fout=947.70 (A=005, B=148) +Fout=947.80 (A=006, B=148) +Fout=947.90 (A=007, B=148) +Fout=948.00 (A=008, B=148) +Fout=948.10 (A=009, B=148) +Fout=948.20 (A=010, B=148) +Fout=948.30 (A=011, B=148) +Fout=948.40 (A=012, B=148) +Fout=948.50 (A=013, B=148) +Fout=948.60 (A=014, B=148) +Fout=948.70 (A=015, B=148) +Fout=948.80 (A=016, B=148) +Fout=948.90 (A=017, B=148) +Fout=949.00 (A=018, B=148) +Fout=949.10 (A=019, B=148) +Fout=949.20 (A=020, B=148) +Fout=949.30 (A=021, B=148) +Fout=949.40 (A=022, B=148) +Fout=949.50 (A=023, B=148) +Fout=949.60 (A=024, B=148) +Fout=949.70 (A=025, B=148) +Fout=949.80 (A=026, B=148) +Fout=949.90 (A=027, B=148) +Fout=950.00 (A=028, B=148) +Fout=950.10 (A=029, B=148) +Fout=950.20 (A=030, B=148) +Fout=950.30 (A=031, B=148) +Fout=950.40 (A=032, B=148) +Fout=950.50 (A=033, B=148) +Fout=950.60 (A=034, B=148) +Fout=950.70 (A=035, B=148) +Fout=950.80 (A=036, B=148) +Fout=950.90 (A=037, B=148) +Fout=951.00 (A=038, B=148) +Fout=951.10 (A=039, B=148) +Fout=951.20 (A=040, B=148) +Fout=951.30 (A=041, B=148) +Fout=951.40 (A=042, B=148) +Fout=951.50 (A=043, B=148) +Fout=951.60 (A=044, B=148) +Fout=951.70 (A=045, B=148) +Fout=951.80 (A=046, B=148) +Fout=951.90 (A=047, B=148) +Fout=952.00 (A=048, B=148) +Fout=952.10 (A=049, B=148) +Fout=952.20 (A=050, B=148) +Fout=952.30 (A=051, B=148) +Fout=952.40 (A=052, B=148) +Fout=952.50 (A=053, B=148) +Fout=952.60 (A=054, B=148) +Fout=952.70 (A=055, B=148) +Fout=952.80 (A=056, B=148) +Fout=952.90 (A=057, B=148) +Fout=953.00 (A=058, B=148) +Fout=953.10 (A=059, B=148) +Fout=953.20 (A=060, B=148) +Fout=953.30 (A=061, B=148) +Fout=953.40 (A=062, B=148) +Fout=953.60 (A=000, B=149) +Fout=953.70 (A=001, B=149) +Fout=953.80 (A=002, B=149) +Fout=953.90 (A=003, B=149) +Fout=954.00 (A=004, B=149) +Fout=954.10 (A=005, B=149) +Fout=954.20 (A=006, B=149) +Fout=954.30 (A=007, B=149) +Fout=954.40 (A=008, B=149) +Fout=954.50 (A=009, B=149) +Fout=954.60 (A=010, B=149) +Fout=954.70 (A=011, B=149) +Fout=954.80 (A=012, B=149) +Fout=954.90 (A=013, B=149) +Fout=955.00 (A=014, B=149) +Fout=955.10 (A=015, B=149) +Fout=955.20 (A=016, B=149) +Fout=955.30 (A=017, B=149) +Fout=955.40 (A=018, B=149) +Fout=955.50 (A=019, B=149) +Fout=955.60 (A=020, B=149) +Fout=955.70 (A=021, B=149) +Fout=955.80 (A=022, B=149) +Fout=955.90 (A=023, B=149) +Fout=956.00 (A=024, B=149) +Fout=956.10 (A=025, B=149) +Fout=956.20 (A=026, B=149) +Fout=956.30 (A=027, B=149) +Fout=956.40 (A=028, B=149) +Fout=956.50 (A=029, B=149) +Fout=956.60 (A=030, B=149) +Fout=956.70 (A=031, B=149) +Fout=956.80 (A=032, B=149) +Fout=956.90 (A=033, B=149) +Fout=957.00 (A=034, B=149) +Fout=957.10 (A=035, B=149) +Fout=957.20 (A=036, B=149) +Fout=957.30 (A=037, B=149) +Fout=957.40 (A=038, B=149) +Fout=957.50 (A=039, B=149) +Fout=957.60 (A=040, B=149) +Fout=957.70 (A=041, B=149) +Fout=957.80 (A=042, B=149) +Fout=957.90 (A=043, B=149) +Fout=958.00 (A=044, B=149) +Fout=958.10 (A=045, B=149) +Fout=958.20 (A=046, B=149) +Fout=958.30 (A=047, B=149) +Fout=958.40 (A=048, B=149) +Fout=958.50 (A=049, B=149) +Fout=958.60 (A=050, B=149) +Fout=958.70 (A=051, B=149) +Fout=958.80 (A=052, B=149) +Fout=958.90 (A=053, B=149) +Fout=959.00 (A=054, B=149) +Fout=959.10 (A=055, B=149) +Fout=959.20 (A=056, B=149) +Fout=959.30 (A=057, B=149) +Fout=959.40 (A=058, B=149) +Fout=959.50 (A=059, B=149) +Fout=959.60 (A=060, B=149) +Fout=959.70 (A=061, B=149) +Fout=959.80 (A=062, B=149) +Fout=960.00 (A=000, B=150) +Fout=960.10 (A=001, B=150) +Fout=960.20 (A=002, B=150) +Fout=960.30 (A=003, B=150) +Fout=960.40 (A=004, B=150) +Fout=960.50 (A=005, B=150) +Fout=960.60 (A=006, B=150) +Fout=960.70 (A=007, B=150) +Fout=960.80 (A=008, B=150) +Fout=960.90 (A=009, B=150) +Fout=961.00 (A=010, B=150) +Fout=961.10 (A=011, B=150) +Fout=961.20 (A=012, B=150) +Fout=961.30 (A=013, B=150) +Fout=961.40 (A=014, B=150) +Fout=961.50 (A=015, B=150) +Fout=961.60 (A=016, B=150) +Fout=961.70 (A=017, B=150) +Fout=961.80 (A=018, B=150) +Fout=961.90 (A=019, B=150) +Fout=962.00 (A=020, B=150) +Fout=962.10 (A=021, B=150) +Fout=962.20 (A=022, B=150) +Fout=962.30 (A=023, B=150) +Fout=962.40 (A=024, B=150) +Fout=962.50 (A=025, B=150) +Fout=962.60 (A=026, B=150) +Fout=962.70 (A=027, B=150) +Fout=962.80 (A=028, B=150) +Fout=962.90 (A=029, B=150) +Fout=963.00 (A=030, B=150) +Fout=963.10 (A=031, B=150) +Fout=963.20 (A=032, B=150) +Fout=963.30 (A=033, B=150) +Fout=963.40 (A=034, B=150) +Fout=963.50 (A=035, B=150) +Fout=963.60 (A=036, B=150) +Fout=963.70 (A=037, B=150) +Fout=963.80 (A=038, B=150) +Fout=963.90 (A=039, B=150) +Fout=964.00 (A=040, B=150) +Fout=964.10 (A=041, B=150) +Fout=964.20 (A=042, B=150) +Fout=964.30 (A=043, B=150) +Fout=964.40 (A=044, B=150) +Fout=964.50 (A=045, B=150) +Fout=964.60 (A=046, B=150) +Fout=964.70 (A=047, B=150) +Fout=964.80 (A=048, B=150) +Fout=964.90 (A=049, B=150) +Fout=965.00 (A=050, B=150) +Fout=965.10 (A=051, B=150) +Fout=965.20 (A=052, B=150) +Fout=965.30 (A=053, B=150) +Fout=965.40 (A=054, B=150) +Fout=965.50 (A=055, B=150) +Fout=965.60 (A=056, B=150) +Fout=965.70 (A=057, B=150) +Fout=965.80 (A=058, B=150) +Fout=965.90 (A=059, B=150) +Fout=966.00 (A=060, B=150) +Fout=966.10 (A=061, B=150) +Fout=966.20 (A=062, B=150) +====================================================================== +PLL Rx High Band: +Fout=1804.80 (A=000, B=141) +Fout=1805.00 (A=001, B=141) +Fout=1805.20 (A=002, B=141) +Fout=1805.40 (A=003, B=141) +Fout=1805.60 (A=004, B=141) +Fout=1805.80 (A=005, B=141) +Fout=1806.00 (A=006, B=141) +Fout=1806.20 (A=007, B=141) +Fout=1806.40 (A=008, B=141) +Fout=1806.60 (A=009, B=141) +Fout=1806.80 (A=010, B=141) +Fout=1807.00 (A=011, B=141) +Fout=1807.20 (A=012, B=141) +Fout=1807.40 (A=013, B=141) +Fout=1807.60 (A=014, B=141) +Fout=1807.80 (A=015, B=141) +Fout=1808.00 (A=016, B=141) +Fout=1808.20 (A=017, B=141) +Fout=1808.40 (A=018, B=141) +Fout=1808.60 (A=019, B=141) +Fout=1808.80 (A=020, B=141) +Fout=1809.00 (A=021, B=141) +Fout=1809.20 (A=022, B=141) +Fout=1809.40 (A=023, B=141) +Fout=1809.60 (A=024, B=141) +Fout=1809.80 (A=025, B=141) +Fout=1810.00 (A=026, B=141) +Fout=1810.20 (A=027, B=141) +Fout=1810.40 (A=028, B=141) +Fout=1810.60 (A=029, B=141) +Fout=1810.80 (A=030, B=141) +Fout=1811.00 (A=031, B=141) +Fout=1811.20 (A=032, B=141) +Fout=1811.40 (A=033, B=141) +Fout=1811.60 (A=034, B=141) +Fout=1811.80 (A=035, B=141) +Fout=1812.00 (A=036, B=141) +Fout=1812.20 (A=037, B=141) +Fout=1812.40 (A=038, B=141) +Fout=1812.60 (A=039, B=141) +Fout=1812.80 (A=040, B=141) +Fout=1813.00 (A=041, B=141) +Fout=1813.20 (A=042, B=141) +Fout=1813.40 (A=043, B=141) +Fout=1813.60 (A=044, B=141) +Fout=1813.80 (A=045, B=141) +Fout=1814.00 (A=046, B=141) +Fout=1814.20 (A=047, B=141) +Fout=1814.40 (A=048, B=141) +Fout=1814.60 (A=049, B=141) +Fout=1814.80 (A=050, B=141) +Fout=1815.00 (A=051, B=141) +Fout=1815.20 (A=052, B=141) +Fout=1815.40 (A=053, B=141) +Fout=1815.60 (A=054, B=141) +Fout=1815.80 (A=055, B=141) +Fout=1816.00 (A=056, B=141) +Fout=1816.20 (A=057, B=141) +Fout=1816.40 (A=058, B=141) +Fout=1816.60 (A=059, B=141) +Fout=1816.80 (A=060, B=141) +Fout=1817.00 (A=061, B=141) +Fout=1817.20 (A=062, B=141) +Fout=1817.60 (A=000, B=142) +Fout=1817.80 (A=001, B=142) +Fout=1818.00 (A=002, B=142) +Fout=1818.20 (A=003, B=142) +Fout=1818.40 (A=004, B=142) +Fout=1818.60 (A=005, B=142) +Fout=1818.80 (A=006, B=142) +Fout=1819.00 (A=007, B=142) +Fout=1819.20 (A=008, B=142) +Fout=1819.40 (A=009, B=142) +Fout=1819.60 (A=010, B=142) +Fout=1819.80 (A=011, B=142) +Fout=1820.00 (A=012, B=142) +Fout=1820.20 (A=013, B=142) +Fout=1820.40 (A=014, B=142) +Fout=1820.60 (A=015, B=142) +Fout=1820.80 (A=016, B=142) +Fout=1821.00 (A=017, B=142) +Fout=1821.20 (A=018, B=142) +Fout=1821.40 (A=019, B=142) +Fout=1821.60 (A=020, B=142) +Fout=1821.80 (A=021, B=142) +Fout=1822.00 (A=022, B=142) +Fout=1822.20 (A=023, B=142) +Fout=1822.40 (A=024, B=142) +Fout=1822.60 (A=025, B=142) +Fout=1822.80 (A=026, B=142) +Fout=1823.00 (A=027, B=142) +Fout=1823.20 (A=028, B=142) +Fout=1823.40 (A=029, B=142) +Fout=1823.60 (A=030, B=142) +Fout=1823.80 (A=031, B=142) +Fout=1824.00 (A=032, B=142) +Fout=1824.20 (A=033, B=142) +Fout=1824.40 (A=034, B=142) +Fout=1824.60 (A=035, B=142) +Fout=1824.80 (A=036, B=142) +Fout=1825.00 (A=037, B=142) +Fout=1825.20 (A=038, B=142) +Fout=1825.40 (A=039, B=142) +Fout=1825.60 (A=040, B=142) +Fout=1825.80 (A=041, B=142) +Fout=1826.00 (A=042, B=142) +Fout=1826.20 (A=043, B=142) +Fout=1826.40 (A=044, B=142) +Fout=1826.60 (A=045, B=142) +Fout=1826.80 (A=046, B=142) +Fout=1827.00 (A=047, B=142) +Fout=1827.20 (A=048, B=142) +Fout=1827.40 (A=049, B=142) +Fout=1827.60 (A=050, B=142) +Fout=1827.80 (A=051, B=142) +Fout=1828.00 (A=052, B=142) +Fout=1828.20 (A=053, B=142) +Fout=1828.40 (A=054, B=142) +Fout=1828.60 (A=055, B=142) +Fout=1828.80 (A=056, B=142) +Fout=1829.00 (A=057, B=142) +Fout=1829.20 (A=058, B=142) +Fout=1829.40 (A=059, B=142) +Fout=1829.60 (A=060, B=142) +Fout=1829.80 (A=061, B=142) +Fout=1830.00 (A=062, B=142) +Fout=1830.40 (A=000, B=143) +Fout=1830.60 (A=001, B=143) +Fout=1830.80 (A=002, B=143) +Fout=1831.00 (A=003, B=143) +Fout=1831.20 (A=004, B=143) +Fout=1831.40 (A=005, B=143) +Fout=1831.60 (A=006, B=143) +Fout=1831.80 (A=007, B=143) +Fout=1832.00 (A=008, B=143) +Fout=1832.20 (A=009, B=143) +Fout=1832.40 (A=010, B=143) +Fout=1832.60 (A=011, B=143) +Fout=1832.80 (A=012, B=143) +Fout=1833.00 (A=013, B=143) +Fout=1833.20 (A=014, B=143) +Fout=1833.40 (A=015, B=143) +Fout=1833.60 (A=016, B=143) +Fout=1833.80 (A=017, B=143) +Fout=1834.00 (A=018, B=143) +Fout=1834.20 (A=019, B=143) +Fout=1834.40 (A=020, B=143) +Fout=1834.60 (A=021, B=143) +Fout=1834.80 (A=022, B=143) +Fout=1835.00 (A=023, B=143) +Fout=1835.20 (A=024, B=143) +Fout=1835.40 (A=025, B=143) +Fout=1835.60 (A=026, B=143) +Fout=1835.80 (A=027, B=143) +Fout=1836.00 (A=028, B=143) +Fout=1836.20 (A=029, B=143) +Fout=1836.40 (A=030, B=143) +Fout=1836.60 (A=031, B=143) +Fout=1836.80 (A=032, B=143) +Fout=1837.00 (A=033, B=143) +Fout=1837.20 (A=034, B=143) +Fout=1837.40 (A=035, B=143) +Fout=1837.60 (A=036, B=143) +Fout=1837.80 (A=037, B=143) +Fout=1838.00 (A=038, B=143) +Fout=1838.20 (A=039, B=143) +Fout=1838.40 (A=040, B=143) +Fout=1838.60 (A=041, B=143) +Fout=1838.80 (A=042, B=143) +Fout=1839.00 (A=043, B=143) +Fout=1839.20 (A=044, B=143) +Fout=1839.40 (A=045, B=143) +Fout=1839.60 (A=046, B=143) +Fout=1839.80 (A=047, B=143) +Fout=1840.00 (A=048, B=143) +Fout=1840.20 (A=049, B=143) +Fout=1840.40 (A=050, B=143) +Fout=1840.60 (A=051, B=143) +Fout=1840.80 (A=052, B=143) +Fout=1841.00 (A=053, B=143) +Fout=1841.20 (A=054, B=143) +Fout=1841.40 (A=055, B=143) +Fout=1841.60 (A=056, B=143) +Fout=1841.80 (A=057, B=143) +Fout=1842.00 (A=058, B=143) +Fout=1842.20 (A=059, B=143) +Fout=1842.40 (A=060, B=143) +Fout=1842.60 (A=061, B=143) +Fout=1842.80 (A=062, B=143) +Fout=1843.20 (A=000, B=144) +Fout=1843.40 (A=001, B=144) +Fout=1843.60 (A=002, B=144) +Fout=1843.80 (A=003, B=144) +Fout=1844.00 (A=004, B=144) +Fout=1844.20 (A=005, B=144) +Fout=1844.40 (A=006, B=144) +Fout=1844.60 (A=007, B=144) +Fout=1844.80 (A=008, B=144) +Fout=1845.00 (A=009, B=144) +Fout=1845.20 (A=010, B=144) +Fout=1845.40 (A=011, B=144) +Fout=1845.60 (A=012, B=144) +Fout=1845.80 (A=013, B=144) +Fout=1846.00 (A=014, B=144) +Fout=1846.20 (A=015, B=144) +Fout=1846.40 (A=016, B=144) +Fout=1846.60 (A=017, B=144) +Fout=1846.80 (A=018, B=144) +Fout=1847.00 (A=019, B=144) +Fout=1847.20 (A=020, B=144) +Fout=1847.40 (A=021, B=144) +Fout=1847.60 (A=022, B=144) +Fout=1847.80 (A=023, B=144) +Fout=1848.00 (A=024, B=144) +Fout=1848.20 (A=025, B=144) +Fout=1848.40 (A=026, B=144) +Fout=1848.60 (A=027, B=144) +Fout=1848.80 (A=028, B=144) +Fout=1849.00 (A=029, B=144) +Fout=1849.20 (A=030, B=144) +Fout=1849.40 (A=031, B=144) +Fout=1849.60 (A=032, B=144) +Fout=1849.80 (A=033, B=144) +Fout=1850.00 (A=034, B=144) +Fout=1850.20 (A=035, B=144) +Fout=1850.40 (A=036, B=144) +Fout=1850.60 (A=037, B=144) +Fout=1850.80 (A=038, B=144) +Fout=1851.00 (A=039, B=144) +Fout=1851.20 (A=040, B=144) +Fout=1851.40 (A=041, B=144) +Fout=1851.60 (A=042, B=144) +Fout=1851.80 (A=043, B=144) +Fout=1852.00 (A=044, B=144) +Fout=1852.20 (A=045, B=144) +Fout=1852.40 (A=046, B=144) +Fout=1852.60 (A=047, B=144) +Fout=1852.80 (A=048, B=144) +Fout=1853.00 (A=049, B=144) +Fout=1853.20 (A=050, B=144) +Fout=1853.40 (A=051, B=144) +Fout=1853.60 (A=052, B=144) +Fout=1853.80 (A=053, B=144) +Fout=1854.00 (A=054, B=144) +Fout=1854.20 (A=055, B=144) +Fout=1854.40 (A=056, B=144) +Fout=1854.60 (A=057, B=144) +Fout=1854.80 (A=058, B=144) +Fout=1855.00 (A=059, B=144) +Fout=1855.20 (A=060, B=144) +Fout=1855.40 (A=061, B=144) +Fout=1855.60 (A=062, B=144) +Fout=1856.00 (A=000, B=145) +Fout=1856.20 (A=001, B=145) +Fout=1856.40 (A=002, B=145) +Fout=1856.60 (A=003, B=145) +Fout=1856.80 (A=004, B=145) +Fout=1857.00 (A=005, B=145) +Fout=1857.20 (A=006, B=145) +Fout=1857.40 (A=007, B=145) +Fout=1857.60 (A=008, B=145) +Fout=1857.80 (A=009, B=145) +Fout=1858.00 (A=010, B=145) +Fout=1858.20 (A=011, B=145) +Fout=1858.40 (A=012, B=145) +Fout=1858.60 (A=013, B=145) +Fout=1858.80 (A=014, B=145) +Fout=1859.00 (A=015, B=145) +Fout=1859.20 (A=016, B=145) +Fout=1859.40 (A=017, B=145) +Fout=1859.60 (A=018, B=145) +Fout=1859.80 (A=019, B=145) +Fout=1860.00 (A=020, B=145) +Fout=1860.20 (A=021, B=145) +Fout=1860.40 (A=022, B=145) +Fout=1860.60 (A=023, B=145) +Fout=1860.80 (A=024, B=145) +Fout=1861.00 (A=025, B=145) +Fout=1861.20 (A=026, B=145) +Fout=1861.40 (A=027, B=145) +Fout=1861.60 (A=028, B=145) +Fout=1861.80 (A=029, B=145) +Fout=1862.00 (A=030, B=145) +Fout=1862.20 (A=031, B=145) +Fout=1862.40 (A=032, B=145) +Fout=1862.60 (A=033, B=145) +Fout=1862.80 (A=034, B=145) +Fout=1863.00 (A=035, B=145) +Fout=1863.20 (A=036, B=145) +Fout=1863.40 (A=037, B=145) +Fout=1863.60 (A=038, B=145) +Fout=1863.80 (A=039, B=145) +Fout=1864.00 (A=040, B=145) +Fout=1864.20 (A=041, B=145) +Fout=1864.40 (A=042, B=145) +Fout=1864.60 (A=043, B=145) +Fout=1864.80 (A=044, B=145) +Fout=1865.00 (A=045, B=145) +Fout=1865.20 (A=046, B=145) +Fout=1865.40 (A=047, B=145) +Fout=1865.60 (A=048, B=145) +Fout=1865.80 (A=049, B=145) +Fout=1866.00 (A=050, B=145) +Fout=1866.20 (A=051, B=145) +Fout=1866.40 (A=052, B=145) +Fout=1866.60 (A=053, B=145) +Fout=1866.80 (A=054, B=145) +Fout=1867.00 (A=055, B=145) +Fout=1867.20 (A=056, B=145) +Fout=1867.40 (A=057, B=145) +Fout=1867.60 (A=058, B=145) +Fout=1867.80 (A=059, B=145) +Fout=1868.00 (A=060, B=145) +Fout=1868.20 (A=061, B=145) +Fout=1868.40 (A=062, B=145) +Fout=1868.80 (A=000, B=146) +Fout=1869.00 (A=001, B=146) +Fout=1869.20 (A=002, B=146) +Fout=1869.40 (A=003, B=146) +Fout=1869.60 (A=004, B=146) +Fout=1869.80 (A=005, B=146) +Fout=1870.00 (A=006, B=146) +Fout=1870.20 (A=007, B=146) +Fout=1870.40 (A=008, B=146) +Fout=1870.60 (A=009, B=146) +Fout=1870.80 (A=010, B=146) +Fout=1871.00 (A=011, B=146) +Fout=1871.20 (A=012, B=146) +Fout=1871.40 (A=013, B=146) +Fout=1871.60 (A=014, B=146) +Fout=1871.80 (A=015, B=146) +Fout=1872.00 (A=016, B=146) +Fout=1872.20 (A=017, B=146) +Fout=1872.40 (A=018, B=146) +Fout=1872.60 (A=019, B=146) +Fout=1872.80 (A=020, B=146) +Fout=1873.00 (A=021, B=146) +Fout=1873.20 (A=022, B=146) +Fout=1873.40 (A=023, B=146) +Fout=1873.60 (A=024, B=146) +Fout=1873.80 (A=025, B=146) +Fout=1874.00 (A=026, B=146) +Fout=1874.20 (A=027, B=146) +Fout=1874.40 (A=028, B=146) +Fout=1874.60 (A=029, B=146) +Fout=1874.80 (A=030, B=146) +Fout=1875.00 (A=031, B=146) +Fout=1875.20 (A=032, B=146) +Fout=1875.40 (A=033, B=146) +Fout=1875.60 (A=034, B=146) +Fout=1875.80 (A=035, B=146) +Fout=1876.00 (A=036, B=146) +Fout=1876.20 (A=037, B=146) +Fout=1876.40 (A=038, B=146) +Fout=1876.60 (A=039, B=146) +Fout=1876.80 (A=040, B=146) +Fout=1877.00 (A=041, B=146) +Fout=1877.20 (A=042, B=146) +Fout=1877.40 (A=043, B=146) +Fout=1877.60 (A=044, B=146) +Fout=1877.80 (A=045, B=146) +Fout=1878.00 (A=046, B=146) +Fout=1878.20 (A=047, B=146) +Fout=1878.40 (A=048, B=146) +Fout=1878.60 (A=049, B=146) +Fout=1878.80 (A=050, B=146) +Fout=1879.00 (A=051, B=146) +Fout=1879.20 (A=052, B=146) +Fout=1879.40 (A=053, B=146) +Fout=1879.60 (A=054, B=146) +Fout=1879.80 (A=055, B=146) +Fout=1880.00 (A=056, B=146) +Fout=1880.20 (A=057, B=146) +Fout=1880.40 (A=058, B=146) +Fout=1880.60 (A=059, B=146) +Fout=1880.80 (A=060, B=146) +Fout=1881.00 (A=061, B=146) +Fout=1881.20 (A=062, B=146) +Fout=1881.60 (A=000, B=147) +Fout=1881.80 (A=001, B=147) +Fout=1882.00 (A=002, B=147) +Fout=1882.20 (A=003, B=147) +Fout=1882.40 (A=004, B=147) +Fout=1882.60 (A=005, B=147) +Fout=1882.80 (A=006, B=147) +Fout=1883.00 (A=007, B=147) +Fout=1883.20 (A=008, B=147) +Fout=1883.40 (A=009, B=147) +Fout=1883.60 (A=010, B=147) +Fout=1883.80 (A=011, B=147) +Fout=1884.00 (A=012, B=147) +Fout=1884.20 (A=013, B=147) +Fout=1884.40 (A=014, B=147) +Fout=1884.60 (A=015, B=147) +Fout=1884.80 (A=016, B=147) +Fout=1885.00 (A=017, B=147) +Fout=1885.20 (A=018, B=147) +Fout=1885.40 (A=019, B=147) +Fout=1885.60 (A=020, B=147) +Fout=1885.80 (A=021, B=147) +Fout=1886.00 (A=022, B=147) +Fout=1886.20 (A=023, B=147) +Fout=1886.40 (A=024, B=147) +Fout=1886.60 (A=025, B=147) +Fout=1886.80 (A=026, B=147) +Fout=1887.00 (A=027, B=147) +Fout=1887.20 (A=028, B=147) +Fout=1887.40 (A=029, B=147) +Fout=1887.60 (A=030, B=147) +Fout=1887.80 (A=031, B=147) +Fout=1888.00 (A=032, B=147) +Fout=1888.20 (A=033, B=147) +Fout=1888.40 (A=034, B=147) +Fout=1888.60 (A=035, B=147) +Fout=1888.80 (A=036, B=147) +Fout=1889.00 (A=037, B=147) +Fout=1889.20 (A=038, B=147) +Fout=1889.40 (A=039, B=147) +Fout=1889.60 (A=040, B=147) +Fout=1889.80 (A=041, B=147) +Fout=1890.00 (A=042, B=147) +Fout=1890.20 (A=043, B=147) +Fout=1890.40 (A=044, B=147) +Fout=1890.60 (A=045, B=147) +Fout=1890.80 (A=046, B=147) +Fout=1891.00 (A=047, B=147) +Fout=1891.20 (A=048, B=147) +Fout=1891.40 (A=049, B=147) +Fout=1891.60 (A=050, B=147) +Fout=1891.80 (A=051, B=147) +Fout=1892.00 (A=052, B=147) +Fout=1892.20 (A=053, B=147) +Fout=1892.40 (A=054, B=147) +Fout=1892.60 (A=055, B=147) +Fout=1892.80 (A=056, B=147) +Fout=1893.00 (A=057, B=147) +Fout=1893.20 (A=058, B=147) +Fout=1893.40 (A=059, B=147) +Fout=1893.60 (A=060, B=147) +Fout=1893.80 (A=061, B=147) +Fout=1894.00 (A=062, B=147) +Fout=1894.40 (A=000, B=148) +Fout=1894.60 (A=001, B=148) +Fout=1894.80 (A=002, B=148) +Fout=1895.00 (A=003, B=148) +Fout=1895.20 (A=004, B=148) +Fout=1895.40 (A=005, B=148) +Fout=1895.60 (A=006, B=148) +Fout=1895.80 (A=007, B=148) +Fout=1896.00 (A=008, B=148) +Fout=1896.20 (A=009, B=148) +Fout=1896.40 (A=010, B=148) +Fout=1896.60 (A=011, B=148) +Fout=1896.80 (A=012, B=148) +Fout=1897.00 (A=013, B=148) +Fout=1897.20 (A=014, B=148) +Fout=1897.40 (A=015, B=148) +Fout=1897.60 (A=016, B=148) +Fout=1897.80 (A=017, B=148) +Fout=1898.00 (A=018, B=148) +Fout=1898.20 (A=019, B=148) +Fout=1898.40 (A=020, B=148) +Fout=1898.60 (A=021, B=148) +Fout=1898.80 (A=022, B=148) +Fout=1899.00 (A=023, B=148) +Fout=1899.20 (A=024, B=148) +Fout=1899.40 (A=025, B=148) +Fout=1899.60 (A=026, B=148) +Fout=1899.80 (A=027, B=148) +Fout=1900.00 (A=028, B=148) +Fout=1900.20 (A=029, B=148) +Fout=1900.40 (A=030, B=148) +Fout=1900.60 (A=031, B=148) +Fout=1900.80 (A=032, B=148) +Fout=1901.00 (A=033, B=148) +Fout=1901.20 (A=034, B=148) +Fout=1901.40 (A=035, B=148) +Fout=1901.60 (A=036, B=148) +Fout=1901.80 (A=037, B=148) +Fout=1902.00 (A=038, B=148) +Fout=1902.20 (A=039, B=148) +Fout=1902.40 (A=040, B=148) +Fout=1902.60 (A=041, B=148) +Fout=1902.80 (A=042, B=148) +Fout=1903.00 (A=043, B=148) +Fout=1903.20 (A=044, B=148) +Fout=1903.40 (A=045, B=148) +Fout=1903.60 (A=046, B=148) +Fout=1903.80 (A=047, B=148) +Fout=1904.00 (A=048, B=148) +Fout=1904.20 (A=049, B=148) +Fout=1904.40 (A=050, B=148) +Fout=1904.60 (A=051, B=148) +Fout=1904.80 (A=052, B=148) +Fout=1905.00 (A=053, B=148) +Fout=1905.20 (A=054, B=148) +Fout=1905.40 (A=055, B=148) +Fout=1905.60 (A=056, B=148) +Fout=1905.80 (A=057, B=148) +Fout=1906.00 (A=058, B=148) +Fout=1906.20 (A=059, B=148) +Fout=1906.40 (A=060, B=148) +Fout=1906.60 (A=061, B=148) +Fout=1906.80 (A=062, B=148) +Fout=1907.20 (A=000, B=149) +Fout=1907.40 (A=001, B=149) +Fout=1907.60 (A=002, B=149) +Fout=1907.80 (A=003, B=149) +Fout=1908.00 (A=004, B=149) +Fout=1908.20 (A=005, B=149) +Fout=1908.40 (A=006, B=149) +Fout=1908.60 (A=007, B=149) +Fout=1908.80 (A=008, B=149) +Fout=1909.00 (A=009, B=149) +Fout=1909.20 (A=010, B=149) +Fout=1909.40 (A=011, B=149) +Fout=1909.60 (A=012, B=149) +Fout=1909.80 (A=013, B=149) +Fout=1910.00 (A=014, B=149) +Fout=1910.20 (A=015, B=149) +Fout=1910.40 (A=016, B=149) +Fout=1910.60 (A=017, B=149) +Fout=1910.80 (A=018, B=149) +Fout=1911.00 (A=019, B=149) +Fout=1911.20 (A=020, B=149) +Fout=1911.40 (A=021, B=149) +Fout=1911.60 (A=022, B=149) +Fout=1911.80 (A=023, B=149) +Fout=1912.00 (A=024, B=149) +Fout=1912.20 (A=025, B=149) +Fout=1912.40 (A=026, B=149) +Fout=1912.60 (A=027, B=149) +Fout=1912.80 (A=028, B=149) +Fout=1913.00 (A=029, B=149) +Fout=1913.20 (A=030, B=149) +Fout=1913.40 (A=031, B=149) +Fout=1913.60 (A=032, B=149) +Fout=1913.80 (A=033, B=149) +Fout=1914.00 (A=034, B=149) +Fout=1914.20 (A=035, B=149) +Fout=1914.40 (A=036, B=149) +Fout=1914.60 (A=037, B=149) +Fout=1914.80 (A=038, B=149) +Fout=1915.00 (A=039, B=149) +Fout=1915.20 (A=040, B=149) +Fout=1915.40 (A=041, B=149) +Fout=1915.60 (A=042, B=149) +Fout=1915.80 (A=043, B=149) +Fout=1916.00 (A=044, B=149) +Fout=1916.20 (A=045, B=149) +Fout=1916.40 (A=046, B=149) +Fout=1916.60 (A=047, B=149) +Fout=1916.80 (A=048, B=149) +Fout=1917.00 (A=049, B=149) +Fout=1917.20 (A=050, B=149) +Fout=1917.40 (A=051, B=149) +Fout=1917.60 (A=052, B=149) +Fout=1917.80 (A=053, B=149) +Fout=1918.00 (A=054, B=149) +Fout=1918.20 (A=055, B=149) +Fout=1918.40 (A=056, B=149) +Fout=1918.60 (A=057, B=149) +Fout=1918.80 (A=058, B=149) +Fout=1919.00 (A=059, B=149) +Fout=1919.20 (A=060, B=149) +Fout=1919.40 (A=061, B=149) +Fout=1919.60 (A=062, B=149) +Fout=1920.00 (A=000, B=150) +Fout=1920.20 (A=001, B=150) +Fout=1920.40 (A=002, B=150) +Fout=1920.60 (A=003, B=150) +Fout=1920.80 (A=004, B=150) +Fout=1921.00 (A=005, B=150) +Fout=1921.20 (A=006, B=150) +Fout=1921.40 (A=007, B=150) +Fout=1921.60 (A=008, B=150) +Fout=1921.80 (A=009, B=150) +Fout=1922.00 (A=010, B=150) +Fout=1922.20 (A=011, B=150) +Fout=1922.40 (A=012, B=150) +Fout=1922.60 (A=013, B=150) +Fout=1922.80 (A=014, B=150) +Fout=1923.00 (A=015, B=150) +Fout=1923.20 (A=016, B=150) +Fout=1923.40 (A=017, B=150) +Fout=1923.60 (A=018, B=150) +Fout=1923.80 (A=019, B=150) +Fout=1924.00 (A=020, B=150) +Fout=1924.20 (A=021, B=150) +Fout=1924.40 (A=022, B=150) +Fout=1924.60 (A=023, B=150) +Fout=1924.80 (A=024, B=150) +Fout=1925.00 (A=025, B=150) +Fout=1925.20 (A=026, B=150) +Fout=1925.40 (A=027, B=150) +Fout=1925.60 (A=028, B=150) +Fout=1925.80 (A=029, B=150) +Fout=1926.00 (A=030, B=150) +Fout=1926.20 (A=031, B=150) +Fout=1926.40 (A=032, B=150) +Fout=1926.60 (A=033, B=150) +Fout=1926.80 (A=034, B=150) +Fout=1927.00 (A=035, B=150) +Fout=1927.20 (A=036, B=150) +Fout=1927.40 (A=037, B=150) +Fout=1927.60 (A=038, B=150) +Fout=1927.80 (A=039, B=150) +Fout=1928.00 (A=040, B=150) +Fout=1928.20 (A=041, B=150) +Fout=1928.40 (A=042, B=150) +Fout=1928.60 (A=043, B=150) +Fout=1928.80 (A=044, B=150) +Fout=1929.00 (A=045, B=150) +Fout=1929.20 (A=046, B=150) +Fout=1929.40 (A=047, B=150) +Fout=1929.60 (A=048, B=150) +Fout=1929.80 (A=049, B=150) +Fout=1930.00 (A=050, B=150) +Fout=1930.20 (A=051, B=150) +Fout=1930.40 (A=052, B=150) +Fout=1930.60 (A=053, B=150) +Fout=1930.80 (A=054, B=150) +Fout=1931.00 (A=055, B=150) +Fout=1931.20 (A=056, B=150) +Fout=1931.40 (A=057, B=150) +Fout=1931.60 (A=058, B=150) +Fout=1931.80 (A=059, B=150) +Fout=1932.00 (A=060, B=150) +Fout=1932.20 (A=061, B=150) +Fout=1932.40 (A=062, B=150) +Fout=1932.80 (A=000, B=151) +Fout=1933.00 (A=001, B=151) +Fout=1933.20 (A=002, B=151) +Fout=1933.40 (A=003, B=151) +Fout=1933.60 (A=004, B=151) +Fout=1933.80 (A=005, B=151) +Fout=1934.00 (A=006, B=151) +Fout=1934.20 (A=007, B=151) +Fout=1934.40 (A=008, B=151) +Fout=1934.60 (A=009, B=151) +Fout=1934.80 (A=010, B=151) +Fout=1935.00 (A=011, B=151) +Fout=1935.20 (A=012, B=151) +Fout=1935.40 (A=013, B=151) +Fout=1935.60 (A=014, B=151) +Fout=1935.80 (A=015, B=151) +Fout=1936.00 (A=016, B=151) +Fout=1936.20 (A=017, B=151) +Fout=1936.40 (A=018, B=151) +Fout=1936.60 (A=019, B=151) +Fout=1936.80 (A=020, B=151) +Fout=1937.00 (A=021, B=151) +Fout=1937.20 (A=022, B=151) +Fout=1937.40 (A=023, B=151) +Fout=1937.60 (A=024, B=151) +Fout=1937.80 (A=025, B=151) +Fout=1938.00 (A=026, B=151) +Fout=1938.20 (A=027, B=151) +Fout=1938.40 (A=028, B=151) +Fout=1938.60 (A=029, B=151) +Fout=1938.80 (A=030, B=151) +Fout=1939.00 (A=031, B=151) +Fout=1939.20 (A=032, B=151) +Fout=1939.40 (A=033, B=151) +Fout=1939.60 (A=034, B=151) +Fout=1939.80 (A=035, B=151) +Fout=1940.00 (A=036, B=151) +Fout=1940.20 (A=037, B=151) +Fout=1940.40 (A=038, B=151) +Fout=1940.60 (A=039, B=151) +Fout=1940.80 (A=040, B=151) +Fout=1941.00 (A=041, B=151) +Fout=1941.20 (A=042, B=151) +Fout=1941.40 (A=043, B=151) +Fout=1941.60 (A=044, B=151) +Fout=1941.80 (A=045, B=151) +Fout=1942.00 (A=046, B=151) +Fout=1942.20 (A=047, B=151) +Fout=1942.40 (A=048, B=151) +Fout=1942.60 (A=049, B=151) +Fout=1942.80 (A=050, B=151) +Fout=1943.00 (A=051, B=151) +Fout=1943.20 (A=052, B=151) +Fout=1943.40 (A=053, B=151) +Fout=1943.60 (A=054, B=151) +Fout=1943.80 (A=055, B=151) +Fout=1944.00 (A=056, B=151) +Fout=1944.20 (A=057, B=151) +Fout=1944.40 (A=058, B=151) +Fout=1944.60 (A=059, B=151) +Fout=1944.80 (A=060, B=151) +Fout=1945.00 (A=061, B=151) +Fout=1945.20 (A=062, B=151) +Fout=1945.60 (A=000, B=152) +Fout=1945.80 (A=001, B=152) +Fout=1946.00 (A=002, B=152) +Fout=1946.20 (A=003, B=152) +Fout=1946.40 (A=004, B=152) +Fout=1946.60 (A=005, B=152) +Fout=1946.80 (A=006, B=152) +Fout=1947.00 (A=007, B=152) +Fout=1947.20 (A=008, B=152) +Fout=1947.40 (A=009, B=152) +Fout=1947.60 (A=010, B=152) +Fout=1947.80 (A=011, B=152) +Fout=1948.00 (A=012, B=152) +Fout=1948.20 (A=013, B=152) +Fout=1948.40 (A=014, B=152) +Fout=1948.60 (A=015, B=152) +Fout=1948.80 (A=016, B=152) +Fout=1949.00 (A=017, B=152) +Fout=1949.20 (A=018, B=152) +Fout=1949.40 (A=019, B=152) +Fout=1949.60 (A=020, B=152) +Fout=1949.80 (A=021, B=152) +Fout=1950.00 (A=022, B=152) +Fout=1950.20 (A=023, B=152) +Fout=1950.40 (A=024, B=152) +Fout=1950.60 (A=025, B=152) +Fout=1950.80 (A=026, B=152) +Fout=1951.00 (A=027, B=152) +Fout=1951.20 (A=028, B=152) +Fout=1951.40 (A=029, B=152) +Fout=1951.60 (A=030, B=152) +Fout=1951.80 (A=031, B=152) +Fout=1952.00 (A=032, B=152) +Fout=1952.20 (A=033, B=152) +Fout=1952.40 (A=034, B=152) +Fout=1952.60 (A=035, B=152) +Fout=1952.80 (A=036, B=152) +Fout=1953.00 (A=037, B=152) +Fout=1953.20 (A=038, B=152) +Fout=1953.40 (A=039, B=152) +Fout=1953.60 (A=040, B=152) +Fout=1953.80 (A=041, B=152) +Fout=1954.00 (A=042, B=152) +Fout=1954.20 (A=043, B=152) +Fout=1954.40 (A=044, B=152) +Fout=1954.60 (A=045, B=152) +Fout=1954.80 (A=046, B=152) +Fout=1955.00 (A=047, B=152) +Fout=1955.20 (A=048, B=152) +Fout=1955.40 (A=049, B=152) +Fout=1955.60 (A=050, B=152) +Fout=1955.80 (A=051, B=152) +Fout=1956.00 (A=052, B=152) +Fout=1956.20 (A=053, B=152) +Fout=1956.40 (A=054, B=152) +Fout=1956.60 (A=055, B=152) +Fout=1956.80 (A=056, B=152) +Fout=1957.00 (A=057, B=152) +Fout=1957.20 (A=058, B=152) +Fout=1957.40 (A=059, B=152) +Fout=1957.60 (A=060, B=152) +Fout=1957.80 (A=061, B=152) +Fout=1958.00 (A=062, B=152) +Fout=1958.40 (A=000, B=153) +Fout=1958.60 (A=001, B=153) +Fout=1958.80 (A=002, B=153) +Fout=1959.00 (A=003, B=153) +Fout=1959.20 (A=004, B=153) +Fout=1959.40 (A=005, B=153) +Fout=1959.60 (A=006, B=153) +Fout=1959.80 (A=007, B=153) +Fout=1960.00 (A=008, B=153) +Fout=1960.20 (A=009, B=153) +Fout=1960.40 (A=010, B=153) +Fout=1960.60 (A=011, B=153) +Fout=1960.80 (A=012, B=153) +Fout=1961.00 (A=013, B=153) +Fout=1961.20 (A=014, B=153) +Fout=1961.40 (A=015, B=153) +Fout=1961.60 (A=016, B=153) +Fout=1961.80 (A=017, B=153) +Fout=1962.00 (A=018, B=153) +Fout=1962.20 (A=019, B=153) +Fout=1962.40 (A=020, B=153) +Fout=1962.60 (A=021, B=153) +Fout=1962.80 (A=022, B=153) +Fout=1963.00 (A=023, B=153) +Fout=1963.20 (A=024, B=153) +Fout=1963.40 (A=025, B=153) +Fout=1963.60 (A=026, B=153) +Fout=1963.80 (A=027, B=153) +Fout=1964.00 (A=028, B=153) +Fout=1964.20 (A=029, B=153) +Fout=1964.40 (A=030, B=153) +Fout=1964.60 (A=031, B=153) +Fout=1964.80 (A=032, B=153) +Fout=1965.00 (A=033, B=153) +Fout=1965.20 (A=034, B=153) +Fout=1965.40 (A=035, B=153) +Fout=1965.60 (A=036, B=153) +Fout=1965.80 (A=037, B=153) +Fout=1966.00 (A=038, B=153) +Fout=1966.20 (A=039, B=153) +Fout=1966.40 (A=040, B=153) +Fout=1966.60 (A=041, B=153) +Fout=1966.80 (A=042, B=153) +Fout=1967.00 (A=043, B=153) +Fout=1967.20 (A=044, B=153) +Fout=1967.40 (A=045, B=153) +Fout=1967.60 (A=046, B=153) +Fout=1967.80 (A=047, B=153) +Fout=1968.00 (A=048, B=153) +Fout=1968.20 (A=049, B=153) +Fout=1968.40 (A=050, B=153) +Fout=1968.60 (A=051, B=153) +Fout=1968.80 (A=052, B=153) +Fout=1969.00 (A=053, B=153) +Fout=1969.20 (A=054, B=153) +Fout=1969.40 (A=055, B=153) +Fout=1969.60 (A=056, B=153) +Fout=1969.80 (A=057, B=153) +Fout=1970.00 (A=058, B=153) +Fout=1970.20 (A=059, B=153) +Fout=1970.40 (A=060, B=153) +Fout=1970.60 (A=061, B=153) +Fout=1970.80 (A=062, B=153) +Fout=1971.20 (A=000, B=154) +Fout=1971.40 (A=001, B=154) +Fout=1971.60 (A=002, B=154) +Fout=1971.80 (A=003, B=154) +Fout=1972.00 (A=004, B=154) +Fout=1972.20 (A=005, B=154) +Fout=1972.40 (A=006, B=154) +Fout=1972.60 (A=007, B=154) +Fout=1972.80 (A=008, B=154) +Fout=1973.00 (A=009, B=154) +Fout=1973.20 (A=010, B=154) +Fout=1973.40 (A=011, B=154) +Fout=1973.60 (A=012, B=154) +Fout=1973.80 (A=013, B=154) +Fout=1974.00 (A=014, B=154) +Fout=1974.20 (A=015, B=154) +Fout=1974.40 (A=016, B=154) +Fout=1974.60 (A=017, B=154) +Fout=1974.80 (A=018, B=154) +Fout=1975.00 (A=019, B=154) +Fout=1975.20 (A=020, B=154) +Fout=1975.40 (A=021, B=154) +Fout=1975.60 (A=022, B=154) +Fout=1975.80 (A=023, B=154) +Fout=1976.00 (A=024, B=154) +Fout=1976.20 (A=025, B=154) +Fout=1976.40 (A=026, B=154) +Fout=1976.60 (A=027, B=154) +Fout=1976.80 (A=028, B=154) +Fout=1977.00 (A=029, B=154) +Fout=1977.20 (A=030, B=154) +Fout=1977.40 (A=031, B=154) +Fout=1977.60 (A=032, B=154) +Fout=1977.80 (A=033, B=154) +Fout=1978.00 (A=034, B=154) +Fout=1978.20 (A=035, B=154) +Fout=1978.40 (A=036, B=154) +Fout=1978.60 (A=037, B=154) +Fout=1978.80 (A=038, B=154) +Fout=1979.00 (A=039, B=154) +Fout=1979.20 (A=040, B=154) +Fout=1979.40 (A=041, B=154) +Fout=1979.60 (A=042, B=154) +Fout=1979.80 (A=043, B=154) +Fout=1980.00 (A=044, B=154) +Fout=1980.20 (A=045, B=154) +Fout=1980.40 (A=046, B=154) +Fout=1980.60 (A=047, B=154) +Fout=1980.80 (A=048, B=154) +Fout=1981.00 (A=049, B=154) +Fout=1981.20 (A=050, B=154) +Fout=1981.40 (A=051, B=154) +Fout=1981.60 (A=052, B=154) +Fout=1981.80 (A=053, B=154) +Fout=1982.00 (A=054, B=154) +Fout=1982.20 (A=055, B=154) +Fout=1982.40 (A=056, B=154) +Fout=1982.60 (A=057, B=154) +Fout=1982.80 (A=058, B=154) +Fout=1983.00 (A=059, B=154) +Fout=1983.20 (A=060, B=154) +Fout=1983.40 (A=061, B=154) +Fout=1983.60 (A=062, B=154) +Fout=1984.00 (A=000, B=155) +Fout=1984.20 (A=001, B=155) +Fout=1984.40 (A=002, B=155) +Fout=1984.60 (A=003, B=155) +Fout=1984.80 (A=004, B=155) +Fout=1985.00 (A=005, B=155) +Fout=1985.20 (A=006, B=155) +Fout=1985.40 (A=007, B=155) +Fout=1985.60 (A=008, B=155) +Fout=1985.80 (A=009, B=155) +Fout=1986.00 (A=010, B=155) +Fout=1986.20 (A=011, B=155) +Fout=1986.40 (A=012, B=155) +Fout=1986.60 (A=013, B=155) +Fout=1986.80 (A=014, B=155) +Fout=1987.00 (A=015, B=155) +Fout=1987.20 (A=016, B=155) +Fout=1987.40 (A=017, B=155) +Fout=1987.60 (A=018, B=155) +Fout=1987.80 (A=019, B=155) +Fout=1988.00 (A=020, B=155) +Fout=1988.20 (A=021, B=155) +Fout=1988.40 (A=022, B=155) +Fout=1988.60 (A=023, B=155) +Fout=1988.80 (A=024, B=155) +Fout=1989.00 (A=025, B=155) +Fout=1989.20 (A=026, B=155) +Fout=1989.40 (A=027, B=155) +Fout=1989.60 (A=028, B=155) +Fout=1989.80 (A=029, B=155) +Fout=1990.00 (A=030, B=155) +Fout=1990.20 (A=031, B=155) +Fout=1990.40 (A=032, B=155) +Fout=1990.60 (A=033, B=155) +Fout=1990.80 (A=034, B=155) +Fout=1991.00 (A=035, B=155) +Fout=1991.20 (A=036, B=155) +Fout=1991.40 (A=037, B=155) +Fout=1991.60 (A=038, B=155) +Fout=1991.80 (A=039, B=155) +Fout=1992.00 (A=040, B=155) +Fout=1992.20 (A=041, B=155) +Fout=1992.40 (A=042, B=155) +Fout=1992.60 (A=043, B=155) +Fout=1992.80 (A=044, B=155) +Fout=1993.00 (A=045, B=155) +Fout=1993.20 (A=046, B=155) +Fout=1993.40 (A=047, B=155) +Fout=1993.60 (A=048, B=155) +Fout=1993.80 (A=049, B=155) +Fout=1994.00 (A=050, B=155) +Fout=1994.20 (A=051, B=155) +Fout=1994.40 (A=052, B=155) +Fout=1994.60 (A=053, B=155) +Fout=1994.80 (A=054, B=155) +Fout=1995.00 (A=055, B=155) +Fout=1995.20 (A=056, B=155) +Fout=1995.40 (A=057, B=155) +Fout=1995.60 (A=058, B=155) +Fout=1995.80 (A=059, B=155) +Fout=1996.00 (A=060, B=155) +Fout=1996.20 (A=061, B=155) +Fout=1996.40 (A=062, B=155) +====================================================================== +PLL Tx GSM850_1 +Fout=819.20 (A=000, B=128) +Fout=819.30 (A=001, B=128) +Fout=819.40 (A=002, B=128) +Fout=819.50 (A=003, B=128) +Fout=819.60 (A=004, B=128) +Fout=819.70 (A=005, B=128) +Fout=819.80 (A=006, B=128) +Fout=819.90 (A=007, B=128) +Fout=820.00 (A=008, B=128) +Fout=820.10 (A=009, B=128) +Fout=820.20 (A=010, B=128) +Fout=820.30 (A=011, B=128) +Fout=820.40 (A=012, B=128) +Fout=820.50 (A=013, B=128) +Fout=820.60 (A=014, B=128) +Fout=820.70 (A=015, B=128) +Fout=820.80 (A=016, B=128) +Fout=820.90 (A=017, B=128) +Fout=821.00 (A=018, B=128) +Fout=821.10 (A=019, B=128) +Fout=821.20 (A=020, B=128) +Fout=821.30 (A=021, B=128) +Fout=821.40 (A=022, B=128) +Fout=821.50 (A=023, B=128) +Fout=821.60 (A=024, B=128) +Fout=821.70 (A=025, B=128) +Fout=821.80 (A=026, B=128) +Fout=821.90 (A=027, B=128) +Fout=822.00 (A=028, B=128) +Fout=822.10 (A=029, B=128) +Fout=822.20 (A=030, B=128) +Fout=822.30 (A=031, B=128) +Fout=822.40 (A=032, B=128) +Fout=822.50 (A=033, B=128) +Fout=822.60 (A=034, B=128) +Fout=822.70 (A=035, B=128) +Fout=822.80 (A=036, B=128) +Fout=822.90 (A=037, B=128) +Fout=823.00 (A=038, B=128) +Fout=823.10 (A=039, B=128) +Fout=823.20 (A=040, B=128) +Fout=823.30 (A=041, B=128) +Fout=823.40 (A=042, B=128) +Fout=823.50 (A=043, B=128) +Fout=823.60 (A=044, B=128) +Fout=823.70 (A=045, B=128) +Fout=823.80 (A=046, B=128) +Fout=823.90 (A=047, B=128) +Fout=824.00 (A=048, B=128) +Fout=824.10 (A=049, B=128) +Fout=824.20 (A=050, B=128) +Fout=824.30 (A=051, B=128) +Fout=824.40 (A=052, B=128) +Fout=824.50 (A=053, B=128) +Fout=824.60 (A=054, B=128) +Fout=824.70 (A=055, B=128) +Fout=824.80 (A=056, B=128) +Fout=824.90 (A=057, B=128) +Fout=825.00 (A=058, B=128) +Fout=825.10 (A=059, B=128) +Fout=825.20 (A=060, B=128) +Fout=825.30 (A=061, B=128) +Fout=825.40 (A=062, B=128) +Fout=825.60 (A=000, B=129) +Fout=825.70 (A=001, B=129) +Fout=825.80 (A=002, B=129) +Fout=825.90 (A=003, B=129) +Fout=826.00 (A=004, B=129) +Fout=826.10 (A=005, B=129) +Fout=826.20 (A=006, B=129) +Fout=826.30 (A=007, B=129) +Fout=826.40 (A=008, B=129) +Fout=826.50 (A=009, B=129) +Fout=826.60 (A=010, B=129) +Fout=826.70 (A=011, B=129) +Fout=826.80 (A=012, B=129) +Fout=826.90 (A=013, B=129) +Fout=827.00 (A=014, B=129) +Fout=827.10 (A=015, B=129) +Fout=827.20 (A=016, B=129) +Fout=827.30 (A=017, B=129) +Fout=827.40 (A=018, B=129) +Fout=827.50 (A=019, B=129) +Fout=827.60 (A=020, B=129) +Fout=827.70 (A=021, B=129) +Fout=827.80 (A=022, B=129) +Fout=827.90 (A=023, B=129) +Fout=828.00 (A=024, B=129) +Fout=828.10 (A=025, B=129) +Fout=828.20 (A=026, B=129) +Fout=828.30 (A=027, B=129) +Fout=828.40 (A=028, B=129) +Fout=828.50 (A=029, B=129) +Fout=828.60 (A=030, B=129) +Fout=828.70 (A=031, B=129) +Fout=828.80 (A=032, B=129) +Fout=828.90 (A=033, B=129) +Fout=829.00 (A=034, B=129) +Fout=829.10 (A=035, B=129) +Fout=829.20 (A=036, B=129) +Fout=829.30 (A=037, B=129) +Fout=829.40 (A=038, B=129) +Fout=829.50 (A=039, B=129) +Fout=829.60 (A=040, B=129) +Fout=829.70 (A=041, B=129) +Fout=829.80 (A=042, B=129) +Fout=829.90 (A=043, B=129) +Fout=830.00 (A=044, B=129) +Fout=830.10 (A=045, B=129) +Fout=830.20 (A=046, B=129) +Fout=830.30 (A=047, B=129) +Fout=830.40 (A=048, B=129) +Fout=830.50 (A=049, B=129) +Fout=830.60 (A=050, B=129) +Fout=830.70 (A=051, B=129) +Fout=830.80 (A=052, B=129) +Fout=830.90 (A=053, B=129) +Fout=831.00 (A=054, B=129) +Fout=831.10 (A=055, B=129) +Fout=831.20 (A=056, B=129) +Fout=831.30 (A=057, B=129) +Fout=831.40 (A=058, B=129) +Fout=831.50 (A=059, B=129) +Fout=831.60 (A=060, B=129) +Fout=831.70 (A=061, B=129) +Fout=831.80 (A=062, B=129) +Fout=832.00 (A=000, B=130) +Fout=832.10 (A=001, B=130) +Fout=832.20 (A=002, B=130) +Fout=832.30 (A=003, B=130) +Fout=832.40 (A=004, B=130) +Fout=832.50 (A=005, B=130) +Fout=832.60 (A=006, B=130) +Fout=832.70 (A=007, B=130) +Fout=832.80 (A=008, B=130) +Fout=832.90 (A=009, B=130) +Fout=833.00 (A=010, B=130) +Fout=833.10 (A=011, B=130) +Fout=833.20 (A=012, B=130) +Fout=833.30 (A=013, B=130) +Fout=833.40 (A=014, B=130) +Fout=833.50 (A=015, B=130) +Fout=833.60 (A=016, B=130) +Fout=833.70 (A=017, B=130) +Fout=833.80 (A=018, B=130) +Fout=833.90 (A=019, B=130) +Fout=834.00 (A=020, B=130) +Fout=834.10 (A=021, B=130) +Fout=834.20 (A=022, B=130) +Fout=834.30 (A=023, B=130) +Fout=834.40 (A=024, B=130) +Fout=834.50 (A=025, B=130) +Fout=834.60 (A=026, B=130) +Fout=834.70 (A=027, B=130) +Fout=834.80 (A=028, B=130) +Fout=834.90 (A=029, B=130) +Fout=835.00 (A=030, B=130) +Fout=835.10 (A=031, B=130) +Fout=835.20 (A=032, B=130) +Fout=835.30 (A=033, B=130) +Fout=835.40 (A=034, B=130) +Fout=835.50 (A=035, B=130) +Fout=835.60 (A=036, B=130) +Fout=835.70 (A=037, B=130) +Fout=835.80 (A=038, B=130) +Fout=835.90 (A=039, B=130) +Fout=836.00 (A=040, B=130) +Fout=836.10 (A=041, B=130) +Fout=836.20 (A=042, B=130) +Fout=836.30 (A=043, B=130) +Fout=836.40 (A=044, B=130) +Fout=836.50 (A=045, B=130) +Fout=836.60 (A=046, B=130) +Fout=836.70 (A=047, B=130) +Fout=836.80 (A=048, B=130) +Fout=836.90 (A=049, B=130) +Fout=837.00 (A=050, B=130) +Fout=837.10 (A=051, B=130) +Fout=837.20 (A=052, B=130) +Fout=837.30 (A=053, B=130) +Fout=837.40 (A=054, B=130) +Fout=837.50 (A=055, B=130) +Fout=837.60 (A=056, B=130) +Fout=837.70 (A=057, B=130) +Fout=837.80 (A=058, B=130) +Fout=837.90 (A=059, B=130) +Fout=838.00 (A=060, B=130) +Fout=838.10 (A=061, B=130) +Fout=838.20 (A=062, B=130) +====================================================================== +PLL Tx GSM850_2 +Fout=832.00 (A=000, B=065) +Fout=832.20 (A=001, B=065) +Fout=832.40 (A=002, B=065) +Fout=832.60 (A=003, B=065) +Fout=832.80 (A=004, B=065) +Fout=833.00 (A=005, B=065) +Fout=833.20 (A=006, B=065) +Fout=833.40 (A=007, B=065) +Fout=833.60 (A=008, B=065) +Fout=833.80 (A=009, B=065) +Fout=834.00 (A=010, B=065) +Fout=834.20 (A=011, B=065) +Fout=834.40 (A=012, B=065) +Fout=834.60 (A=013, B=065) +Fout=834.80 (A=014, B=065) +Fout=835.00 (A=015, B=065) +Fout=835.20 (A=016, B=065) +Fout=835.40 (A=017, B=065) +Fout=835.60 (A=018, B=065) +Fout=835.80 (A=019, B=065) +Fout=836.00 (A=020, B=065) +Fout=836.20 (A=021, B=065) +Fout=836.40 (A=022, B=065) +Fout=836.60 (A=023, B=065) +Fout=836.80 (A=024, B=065) +Fout=837.00 (A=025, B=065) +Fout=837.20 (A=026, B=065) +Fout=837.40 (A=027, B=065) +Fout=837.60 (A=028, B=065) +Fout=837.80 (A=029, B=065) +Fout=838.00 (A=030, B=065) +Fout=838.20 (A=031, B=065) +Fout=838.40 (A=032, B=065) +Fout=838.60 (A=033, B=065) +Fout=838.80 (A=034, B=065) +Fout=839.00 (A=035, B=065) +Fout=839.20 (A=036, B=065) +Fout=839.40 (A=037, B=065) +Fout=839.60 (A=038, B=065) +Fout=839.80 (A=039, B=065) +Fout=840.00 (A=040, B=065) +Fout=840.20 (A=041, B=065) +Fout=840.40 (A=042, B=065) +Fout=840.60 (A=043, B=065) +Fout=840.80 (A=044, B=065) +Fout=841.00 (A=045, B=065) +Fout=841.20 (A=046, B=065) +Fout=841.40 (A=047, B=065) +Fout=841.60 (A=048, B=065) +Fout=841.80 (A=049, B=065) +Fout=842.00 (A=050, B=065) +Fout=842.20 (A=051, B=065) +Fout=842.40 (A=052, B=065) +Fout=842.60 (A=053, B=065) +Fout=842.80 (A=054, B=065) +Fout=843.00 (A=055, B=065) +Fout=843.20 (A=056, B=065) +Fout=843.40 (A=057, B=065) +Fout=843.60 (A=058, B=065) +Fout=843.80 (A=059, B=065) +Fout=844.00 (A=060, B=065) +Fout=844.20 (A=061, B=065) +Fout=844.40 (A=062, B=065) +Fout=844.60 (A=063, B=065) +Fout=844.80 (A=000, B=066) +Fout=845.00 (A=001, B=066) +Fout=845.20 (A=002, B=066) +Fout=845.40 (A=003, B=066) +Fout=845.60 (A=004, B=066) +Fout=845.80 (A=005, B=066) +Fout=846.00 (A=006, B=066) +Fout=846.20 (A=007, B=066) +Fout=846.40 (A=008, B=066) +Fout=846.60 (A=009, B=066) +Fout=846.80 (A=010, B=066) +Fout=847.00 (A=011, B=066) +Fout=847.20 (A=012, B=066) +Fout=847.40 (A=013, B=066) +Fout=847.60 (A=014, B=066) +Fout=847.80 (A=015, B=066) +Fout=848.00 (A=016, B=066) +Fout=848.20 (A=017, B=066) +Fout=848.40 (A=018, B=066) +Fout=848.60 (A=019, B=066) +Fout=848.80 (A=020, B=066) +Fout=849.00 (A=021, B=066) +Fout=849.20 (A=022, B=066) +Fout=849.40 (A=023, B=066) +Fout=849.60 (A=024, B=066) +Fout=849.80 (A=025, B=066) +Fout=850.00 (A=026, B=066) +Fout=850.20 (A=027, B=066) +Fout=850.40 (A=028, B=066) +Fout=850.60 (A=029, B=066) +Fout=850.80 (A=030, B=066) +Fout=851.00 (A=031, B=066) +Fout=851.20 (A=032, B=066) +Fout=851.40 (A=033, B=066) +Fout=851.60 (A=034, B=066) +Fout=851.80 (A=035, B=066) +Fout=852.00 (A=036, B=066) +Fout=852.20 (A=037, B=066) +Fout=852.40 (A=038, B=066) +Fout=852.60 (A=039, B=066) +Fout=852.80 (A=040, B=066) +Fout=853.00 (A=041, B=066) +Fout=853.20 (A=042, B=066) +Fout=853.40 (A=043, B=066) +Fout=853.60 (A=044, B=066) +Fout=853.80 (A=045, B=066) +Fout=854.00 (A=046, B=066) +Fout=854.20 (A=047, B=066) +Fout=854.40 (A=048, B=066) +Fout=854.60 (A=049, B=066) +Fout=854.80 (A=050, B=066) +Fout=855.00 (A=051, B=066) +Fout=855.20 (A=052, B=066) +Fout=855.40 (A=053, B=066) +Fout=855.60 (A=054, B=066) +Fout=855.80 (A=055, B=066) +Fout=856.00 (A=056, B=066) +Fout=856.20 (A=057, B=066) +Fout=856.40 (A=058, B=066) +Fout=856.60 (A=059, B=066) +Fout=856.80 (A=060, B=066) +Fout=857.00 (A=061, B=066) +Fout=857.20 (A=062, B=066) +Fout=857.40 (A=063, B=066) +====================================================================== +PLL Tx GSM900 +Fout=870.40 (A=000, B=068) +Fout=870.60 (A=001, B=068) +Fout=870.80 (A=002, B=068) +Fout=871.00 (A=003, B=068) +Fout=871.20 (A=004, B=068) +Fout=871.40 (A=005, B=068) +Fout=871.60 (A=006, B=068) +Fout=871.80 (A=007, B=068) +Fout=872.00 (A=008, B=068) +Fout=872.20 (A=009, B=068) +Fout=872.40 (A=010, B=068) +Fout=872.60 (A=011, B=068) +Fout=872.80 (A=012, B=068) +Fout=873.00 (A=013, B=068) +Fout=873.20 (A=014, B=068) +Fout=873.40 (A=015, B=068) +Fout=873.60 (A=016, B=068) +Fout=873.80 (A=017, B=068) +Fout=874.00 (A=018, B=068) +Fout=874.20 (A=019, B=068) +Fout=874.40 (A=020, B=068) +Fout=874.60 (A=021, B=068) +Fout=874.80 (A=022, B=068) +Fout=875.00 (A=023, B=068) +Fout=875.20 (A=024, B=068) +Fout=875.40 (A=025, B=068) +Fout=875.60 (A=026, B=068) +Fout=875.80 (A=027, B=068) +Fout=876.00 (A=028, B=068) +Fout=876.20 (A=029, B=068) +Fout=876.40 (A=030, B=068) +Fout=876.60 (A=031, B=068) +Fout=876.80 (A=032, B=068) +Fout=877.00 (A=033, B=068) +Fout=877.20 (A=034, B=068) +Fout=877.40 (A=035, B=068) +Fout=877.60 (A=036, B=068) +Fout=877.80 (A=037, B=068) +Fout=878.00 (A=038, B=068) +Fout=878.20 (A=039, B=068) +Fout=878.40 (A=040, B=068) +Fout=878.60 (A=041, B=068) +Fout=878.80 (A=042, B=068) +Fout=879.00 (A=043, B=068) +Fout=879.20 (A=044, B=068) +Fout=879.40 (A=045, B=068) +Fout=879.60 (A=046, B=068) +Fout=879.80 (A=047, B=068) +Fout=880.00 (A=048, B=068) +Fout=880.20 (A=049, B=068) +Fout=880.40 (A=050, B=068) +Fout=880.60 (A=051, B=068) +Fout=880.80 (A=052, B=068) +Fout=881.00 (A=053, B=068) +Fout=881.20 (A=054, B=068) +Fout=881.40 (A=055, B=068) +Fout=881.60 (A=056, B=068) +Fout=881.80 (A=057, B=068) +Fout=882.00 (A=058, B=068) +Fout=882.20 (A=059, B=068) +Fout=882.40 (A=060, B=068) +Fout=882.60 (A=061, B=068) +Fout=882.80 (A=062, B=068) +Fout=883.00 (A=063, B=068) +Fout=883.20 (A=000, B=069) +Fout=883.40 (A=001, B=069) +Fout=883.60 (A=002, B=069) +Fout=883.80 (A=003, B=069) +Fout=884.00 (A=004, B=069) +Fout=884.20 (A=005, B=069) +Fout=884.40 (A=006, B=069) +Fout=884.60 (A=007, B=069) +Fout=884.80 (A=008, B=069) +Fout=885.00 (A=009, B=069) +Fout=885.20 (A=010, B=069) +Fout=885.40 (A=011, B=069) +Fout=885.60 (A=012, B=069) +Fout=885.80 (A=013, B=069) +Fout=886.00 (A=014, B=069) +Fout=886.20 (A=015, B=069) +Fout=886.40 (A=016, B=069) +Fout=886.60 (A=017, B=069) +Fout=886.80 (A=018, B=069) +Fout=887.00 (A=019, B=069) +Fout=887.20 (A=020, B=069) +Fout=887.40 (A=021, B=069) +Fout=887.60 (A=022, B=069) +Fout=887.80 (A=023, B=069) +Fout=888.00 (A=024, B=069) +Fout=888.20 (A=025, B=069) +Fout=888.40 (A=026, B=069) +Fout=888.60 (A=027, B=069) +Fout=888.80 (A=028, B=069) +Fout=889.00 (A=029, B=069) +Fout=889.20 (A=030, B=069) +Fout=889.40 (A=031, B=069) +Fout=889.60 (A=032, B=069) +Fout=889.80 (A=033, B=069) +Fout=890.00 (A=034, B=069) +Fout=890.20 (A=035, B=069) +Fout=890.40 (A=036, B=069) +Fout=890.60 (A=037, B=069) +Fout=890.80 (A=038, B=069) +Fout=891.00 (A=039, B=069) +Fout=891.20 (A=040, B=069) +Fout=891.40 (A=041, B=069) +Fout=891.60 (A=042, B=069) +Fout=891.80 (A=043, B=069) +Fout=892.00 (A=044, B=069) +Fout=892.20 (A=045, B=069) +Fout=892.40 (A=046, B=069) +Fout=892.60 (A=047, B=069) +Fout=892.80 (A=048, B=069) +Fout=893.00 (A=049, B=069) +Fout=893.20 (A=050, B=069) +Fout=893.40 (A=051, B=069) +Fout=893.60 (A=052, B=069) +Fout=893.80 (A=053, B=069) +Fout=894.00 (A=054, B=069) +Fout=894.20 (A=055, B=069) +Fout=894.40 (A=056, B=069) +Fout=894.60 (A=057, B=069) +Fout=894.80 (A=058, B=069) +Fout=895.00 (A=059, B=069) +Fout=895.20 (A=060, B=069) +Fout=895.40 (A=061, B=069) +Fout=895.60 (A=062, B=069) +Fout=895.80 (A=063, B=069) +Fout=896.00 (A=000, B=070) +Fout=896.20 (A=001, B=070) +Fout=896.40 (A=002, B=070) +Fout=896.60 (A=003, B=070) +Fout=896.80 (A=004, B=070) +Fout=897.00 (A=005, B=070) +Fout=897.20 (A=006, B=070) +Fout=897.40 (A=007, B=070) +Fout=897.60 (A=008, B=070) +Fout=897.80 (A=009, B=070) +Fout=898.00 (A=010, B=070) +Fout=898.20 (A=011, B=070) +Fout=898.40 (A=012, B=070) +Fout=898.60 (A=013, B=070) +Fout=898.80 (A=014, B=070) +Fout=899.00 (A=015, B=070) +Fout=899.20 (A=016, B=070) +Fout=899.40 (A=017, B=070) +Fout=899.60 (A=018, B=070) +Fout=899.80 (A=019, B=070) +Fout=900.00 (A=020, B=070) +Fout=900.20 (A=021, B=070) +Fout=900.40 (A=022, B=070) +Fout=900.60 (A=023, B=070) +Fout=900.80 (A=024, B=070) +Fout=901.00 (A=025, B=070) +Fout=901.20 (A=026, B=070) +Fout=901.40 (A=027, B=070) +Fout=901.60 (A=028, B=070) +Fout=901.80 (A=029, B=070) +Fout=902.00 (A=030, B=070) +Fout=902.20 (A=031, B=070) +Fout=902.40 (A=032, B=070) +Fout=902.60 (A=033, B=070) +Fout=902.80 (A=034, B=070) +Fout=903.00 (A=035, B=070) +Fout=903.20 (A=036, B=070) +Fout=903.40 (A=037, B=070) +Fout=903.60 (A=038, B=070) +Fout=903.80 (A=039, B=070) +Fout=904.00 (A=040, B=070) +Fout=904.20 (A=041, B=070) +Fout=904.40 (A=042, B=070) +Fout=904.60 (A=043, B=070) +Fout=904.80 (A=044, B=070) +Fout=905.00 (A=045, B=070) +Fout=905.20 (A=046, B=070) +Fout=905.40 (A=047, B=070) +Fout=905.60 (A=048, B=070) +Fout=905.80 (A=049, B=070) +Fout=906.00 (A=050, B=070) +Fout=906.20 (A=051, B=070) +Fout=906.40 (A=052, B=070) +Fout=906.60 (A=053, B=070) +Fout=906.80 (A=054, B=070) +Fout=907.00 (A=055, B=070) +Fout=907.20 (A=056, B=070) +Fout=907.40 (A=057, B=070) +Fout=907.60 (A=058, B=070) +Fout=907.80 (A=059, B=070) +Fout=908.00 (A=060, B=070) +Fout=908.20 (A=061, B=070) +Fout=908.40 (A=062, B=070) +Fout=908.60 (A=063, B=070) +Fout=908.80 (A=000, B=071) +Fout=909.00 (A=001, B=071) +Fout=909.20 (A=002, B=071) +Fout=909.40 (A=003, B=071) +Fout=909.60 (A=004, B=071) +Fout=909.80 (A=005, B=071) +Fout=910.00 (A=006, B=071) +Fout=910.20 (A=007, B=071) +Fout=910.40 (A=008, B=071) +Fout=910.60 (A=009, B=071) +Fout=910.80 (A=010, B=071) +Fout=911.00 (A=011, B=071) +Fout=911.20 (A=012, B=071) +Fout=911.40 (A=013, B=071) +Fout=911.60 (A=014, B=071) +Fout=911.80 (A=015, B=071) +Fout=912.00 (A=016, B=071) +Fout=912.20 (A=017, B=071) +Fout=912.40 (A=018, B=071) +Fout=912.60 (A=019, B=071) +Fout=912.80 (A=020, B=071) +Fout=913.00 (A=021, B=071) +Fout=913.20 (A=022, B=071) +Fout=913.40 (A=023, B=071) +Fout=913.60 (A=024, B=071) +Fout=913.80 (A=025, B=071) +Fout=914.00 (A=026, B=071) +Fout=914.20 (A=027, B=071) +Fout=914.40 (A=028, B=071) +Fout=914.60 (A=029, B=071) +Fout=914.80 (A=030, B=071) +Fout=915.00 (A=031, B=071) +Fout=915.20 (A=032, B=071) +Fout=915.40 (A=033, B=071) +Fout=915.60 (A=034, B=071) +Fout=915.80 (A=035, B=071) +Fout=916.00 (A=036, B=071) +Fout=916.20 (A=037, B=071) +Fout=916.40 (A=038, B=071) +Fout=916.60 (A=039, B=071) +Fout=916.80 (A=040, B=071) +Fout=917.00 (A=041, B=071) +Fout=917.20 (A=042, B=071) +Fout=917.40 (A=043, B=071) +Fout=917.60 (A=044, B=071) +Fout=917.80 (A=045, B=071) +Fout=918.00 (A=046, B=071) +Fout=918.20 (A=047, B=071) +Fout=918.40 (A=048, B=071) +Fout=918.60 (A=049, B=071) +Fout=918.80 (A=050, B=071) +Fout=919.00 (A=051, B=071) +Fout=919.20 (A=052, B=071) +Fout=919.40 (A=053, B=071) +Fout=919.60 (A=054, B=071) +Fout=919.80 (A=055, B=071) +Fout=920.00 (A=056, B=071) +Fout=920.20 (A=057, B=071) +Fout=920.40 (A=058, B=071) +Fout=920.60 (A=059, B=071) +Fout=920.80 (A=060, B=071) +Fout=921.00 (A=061, B=071) +Fout=921.20 (A=062, B=071) +Fout=921.40 (A=063, B=071) +====================================================================== +PLL Tx GSM1800/1900 +Fout=1702.40 (A=000, B=133) +Fout=1702.60 (A=001, B=133) +Fout=1702.80 (A=002, B=133) +Fout=1703.00 (A=003, B=133) +Fout=1703.20 (A=004, B=133) +Fout=1703.40 (A=005, B=133) +Fout=1703.60 (A=006, B=133) +Fout=1703.80 (A=007, B=133) +Fout=1704.00 (A=008, B=133) +Fout=1704.20 (A=009, B=133) +Fout=1704.40 (A=010, B=133) +Fout=1704.60 (A=011, B=133) +Fout=1704.80 (A=012, B=133) +Fout=1705.00 (A=013, B=133) +Fout=1705.20 (A=014, B=133) +Fout=1705.40 (A=015, B=133) +Fout=1705.60 (A=016, B=133) +Fout=1705.80 (A=017, B=133) +Fout=1706.00 (A=018, B=133) +Fout=1706.20 (A=019, B=133) +Fout=1706.40 (A=020, B=133) +Fout=1706.60 (A=021, B=133) +Fout=1706.80 (A=022, B=133) +Fout=1707.00 (A=023, B=133) +Fout=1707.20 (A=024, B=133) +Fout=1707.40 (A=025, B=133) +Fout=1707.60 (A=026, B=133) +Fout=1707.80 (A=027, B=133) +Fout=1708.00 (A=028, B=133) +Fout=1708.20 (A=029, B=133) +Fout=1708.40 (A=030, B=133) +Fout=1708.60 (A=031, B=133) +Fout=1708.80 (A=032, B=133) +Fout=1709.00 (A=033, B=133) +Fout=1709.20 (A=034, B=133) +Fout=1709.40 (A=035, B=133) +Fout=1709.60 (A=036, B=133) +Fout=1709.80 (A=037, B=133) +Fout=1710.00 (A=038, B=133) +Fout=1710.20 (A=039, B=133) +Fout=1710.40 (A=040, B=133) +Fout=1710.60 (A=041, B=133) +Fout=1710.80 (A=042, B=133) +Fout=1711.00 (A=043, B=133) +Fout=1711.20 (A=044, B=133) +Fout=1711.40 (A=045, B=133) +Fout=1711.60 (A=046, B=133) +Fout=1711.80 (A=047, B=133) +Fout=1712.00 (A=048, B=133) +Fout=1712.20 (A=049, B=133) +Fout=1712.40 (A=050, B=133) +Fout=1712.60 (A=051, B=133) +Fout=1712.80 (A=052, B=133) +Fout=1713.00 (A=053, B=133) +Fout=1713.20 (A=054, B=133) +Fout=1713.40 (A=055, B=133) +Fout=1713.60 (A=056, B=133) +Fout=1713.80 (A=057, B=133) +Fout=1714.00 (A=058, B=133) +Fout=1714.20 (A=059, B=133) +Fout=1714.40 (A=060, B=133) +Fout=1714.60 (A=061, B=133) +Fout=1714.80 (A=062, B=133) +Fout=1715.00 (A=063, B=133) +Fout=1715.20 (A=000, B=134) +Fout=1715.40 (A=001, B=134) +Fout=1715.60 (A=002, B=134) +Fout=1715.80 (A=003, B=134) +Fout=1716.00 (A=004, B=134) +Fout=1716.20 (A=005, B=134) +Fout=1716.40 (A=006, B=134) +Fout=1716.60 (A=007, B=134) +Fout=1716.80 (A=008, B=134) +Fout=1717.00 (A=009, B=134) +Fout=1717.20 (A=010, B=134) +Fout=1717.40 (A=011, B=134) +Fout=1717.60 (A=012, B=134) +Fout=1717.80 (A=013, B=134) +Fout=1718.00 (A=014, B=134) +Fout=1718.20 (A=015, B=134) +Fout=1718.40 (A=016, B=134) +Fout=1718.60 (A=017, B=134) +Fout=1718.80 (A=018, B=134) +Fout=1719.00 (A=019, B=134) +Fout=1719.20 (A=020, B=134) +Fout=1719.40 (A=021, B=134) +Fout=1719.60 (A=022, B=134) +Fout=1719.80 (A=023, B=134) +Fout=1720.00 (A=024, B=134) +Fout=1720.20 (A=025, B=134) +Fout=1720.40 (A=026, B=134) +Fout=1720.60 (A=027, B=134) +Fout=1720.80 (A=028, B=134) +Fout=1721.00 (A=029, B=134) +Fout=1721.20 (A=030, B=134) +Fout=1721.40 (A=031, B=134) +Fout=1721.60 (A=032, B=134) +Fout=1721.80 (A=033, B=134) +Fout=1722.00 (A=034, B=134) +Fout=1722.20 (A=035, B=134) +Fout=1722.40 (A=036, B=134) +Fout=1722.60 (A=037, B=134) +Fout=1722.80 (A=038, B=134) +Fout=1723.00 (A=039, B=134) +Fout=1723.20 (A=040, B=134) +Fout=1723.40 (A=041, B=134) +Fout=1723.60 (A=042, B=134) +Fout=1723.80 (A=043, B=134) +Fout=1724.00 (A=044, B=134) +Fout=1724.20 (A=045, B=134) +Fout=1724.40 (A=046, B=134) +Fout=1724.60 (A=047, B=134) +Fout=1724.80 (A=048, B=134) +Fout=1725.00 (A=049, B=134) +Fout=1725.20 (A=050, B=134) +Fout=1725.40 (A=051, B=134) +Fout=1725.60 (A=052, B=134) +Fout=1725.80 (A=053, B=134) +Fout=1726.00 (A=054, B=134) +Fout=1726.20 (A=055, B=134) +Fout=1726.40 (A=056, B=134) +Fout=1726.60 (A=057, B=134) +Fout=1726.80 (A=058, B=134) +Fout=1727.00 (A=059, B=134) +Fout=1727.20 (A=060, B=134) +Fout=1727.40 (A=061, B=134) +Fout=1727.60 (A=062, B=134) +Fout=1727.80 (A=063, B=134) +Fout=1728.00 (A=000, B=135) +Fout=1728.20 (A=001, B=135) +Fout=1728.40 (A=002, B=135) +Fout=1728.60 (A=003, B=135) +Fout=1728.80 (A=004, B=135) +Fout=1729.00 (A=005, B=135) +Fout=1729.20 (A=006, B=135) +Fout=1729.40 (A=007, B=135) +Fout=1729.60 (A=008, B=135) +Fout=1729.80 (A=009, B=135) +Fout=1730.00 (A=010, B=135) +Fout=1730.20 (A=011, B=135) +Fout=1730.40 (A=012, B=135) +Fout=1730.60 (A=013, B=135) +Fout=1730.80 (A=014, B=135) +Fout=1731.00 (A=015, B=135) +Fout=1731.20 (A=016, B=135) +Fout=1731.40 (A=017, B=135) +Fout=1731.60 (A=018, B=135) +Fout=1731.80 (A=019, B=135) +Fout=1732.00 (A=020, B=135) +Fout=1732.20 (A=021, B=135) +Fout=1732.40 (A=022, B=135) +Fout=1732.60 (A=023, B=135) +Fout=1732.80 (A=024, B=135) +Fout=1733.00 (A=025, B=135) +Fout=1733.20 (A=026, B=135) +Fout=1733.40 (A=027, B=135) +Fout=1733.60 (A=028, B=135) +Fout=1733.80 (A=029, B=135) +Fout=1734.00 (A=030, B=135) +Fout=1734.20 (A=031, B=135) +Fout=1734.40 (A=032, B=135) +Fout=1734.60 (A=033, B=135) +Fout=1734.80 (A=034, B=135) +Fout=1735.00 (A=035, B=135) +Fout=1735.20 (A=036, B=135) +Fout=1735.40 (A=037, B=135) +Fout=1735.60 (A=038, B=135) +Fout=1735.80 (A=039, B=135) +Fout=1736.00 (A=040, B=135) +Fout=1736.20 (A=041, B=135) +Fout=1736.40 (A=042, B=135) +Fout=1736.60 (A=043, B=135) +Fout=1736.80 (A=044, B=135) +Fout=1737.00 (A=045, B=135) +Fout=1737.20 (A=046, B=135) +Fout=1737.40 (A=047, B=135) +Fout=1737.60 (A=048, B=135) +Fout=1737.80 (A=049, B=135) +Fout=1738.00 (A=050, B=135) +Fout=1738.20 (A=051, B=135) +Fout=1738.40 (A=052, B=135) +Fout=1738.60 (A=053, B=135) +Fout=1738.80 (A=054, B=135) +Fout=1739.00 (A=055, B=135) +Fout=1739.20 (A=056, B=135) +Fout=1739.40 (A=057, B=135) +Fout=1739.60 (A=058, B=135) +Fout=1739.80 (A=059, B=135) +Fout=1740.00 (A=060, B=135) +Fout=1740.20 (A=061, B=135) +Fout=1740.40 (A=062, B=135) +Fout=1740.60 (A=063, B=135) +Fout=1740.80 (A=000, B=136) +Fout=1741.00 (A=001, B=136) +Fout=1741.20 (A=002, B=136) +Fout=1741.40 (A=003, B=136) +Fout=1741.60 (A=004, B=136) +Fout=1741.80 (A=005, B=136) +Fout=1742.00 (A=006, B=136) +Fout=1742.20 (A=007, B=136) +Fout=1742.40 (A=008, B=136) +Fout=1742.60 (A=009, B=136) +Fout=1742.80 (A=010, B=136) +Fout=1743.00 (A=011, B=136) +Fout=1743.20 (A=012, B=136) +Fout=1743.40 (A=013, B=136) +Fout=1743.60 (A=014, B=136) +Fout=1743.80 (A=015, B=136) +Fout=1744.00 (A=016, B=136) +Fout=1744.20 (A=017, B=136) +Fout=1744.40 (A=018, B=136) +Fout=1744.60 (A=019, B=136) +Fout=1744.80 (A=020, B=136) +Fout=1745.00 (A=021, B=136) +Fout=1745.20 (A=022, B=136) +Fout=1745.40 (A=023, B=136) +Fout=1745.60 (A=024, B=136) +Fout=1745.80 (A=025, B=136) +Fout=1746.00 (A=026, B=136) +Fout=1746.20 (A=027, B=136) +Fout=1746.40 (A=028, B=136) +Fout=1746.60 (A=029, B=136) +Fout=1746.80 (A=030, B=136) +Fout=1747.00 (A=031, B=136) +Fout=1747.20 (A=032, B=136) +Fout=1747.40 (A=033, B=136) +Fout=1747.60 (A=034, B=136) +Fout=1747.80 (A=035, B=136) +Fout=1748.00 (A=036, B=136) +Fout=1748.20 (A=037, B=136) +Fout=1748.40 (A=038, B=136) +Fout=1748.60 (A=039, B=136) +Fout=1748.80 (A=040, B=136) +Fout=1749.00 (A=041, B=136) +Fout=1749.20 (A=042, B=136) +Fout=1749.40 (A=043, B=136) +Fout=1749.60 (A=044, B=136) +Fout=1749.80 (A=045, B=136) +Fout=1750.00 (A=046, B=136) +Fout=1750.20 (A=047, B=136) +Fout=1750.40 (A=048, B=136) +Fout=1750.60 (A=049, B=136) +Fout=1750.80 (A=050, B=136) +Fout=1751.00 (A=051, B=136) +Fout=1751.20 (A=052, B=136) +Fout=1751.40 (A=053, B=136) +Fout=1751.60 (A=054, B=136) +Fout=1751.80 (A=055, B=136) +Fout=1752.00 (A=056, B=136) +Fout=1752.20 (A=057, B=136) +Fout=1752.40 (A=058, B=136) +Fout=1752.60 (A=059, B=136) +Fout=1752.80 (A=060, B=136) +Fout=1753.00 (A=061, B=136) +Fout=1753.20 (A=062, B=136) +Fout=1753.40 (A=063, B=136) +Fout=1753.60 (A=000, B=137) +Fout=1753.80 (A=001, B=137) +Fout=1754.00 (A=002, B=137) +Fout=1754.20 (A=003, B=137) +Fout=1754.40 (A=004, B=137) +Fout=1754.60 (A=005, B=137) +Fout=1754.80 (A=006, B=137) +Fout=1755.00 (A=007, B=137) +Fout=1755.20 (A=008, B=137) +Fout=1755.40 (A=009, B=137) +Fout=1755.60 (A=010, B=137) +Fout=1755.80 (A=011, B=137) +Fout=1756.00 (A=012, B=137) +Fout=1756.20 (A=013, B=137) +Fout=1756.40 (A=014, B=137) +Fout=1756.60 (A=015, B=137) +Fout=1756.80 (A=016, B=137) +Fout=1757.00 (A=017, B=137) +Fout=1757.20 (A=018, B=137) +Fout=1757.40 (A=019, B=137) +Fout=1757.60 (A=020, B=137) +Fout=1757.80 (A=021, B=137) +Fout=1758.00 (A=022, B=137) +Fout=1758.20 (A=023, B=137) +Fout=1758.40 (A=024, B=137) +Fout=1758.60 (A=025, B=137) +Fout=1758.80 (A=026, B=137) +Fout=1759.00 (A=027, B=137) +Fout=1759.20 (A=028, B=137) +Fout=1759.40 (A=029, B=137) +Fout=1759.60 (A=030, B=137) +Fout=1759.80 (A=031, B=137) +Fout=1760.00 (A=032, B=137) +Fout=1760.20 (A=033, B=137) +Fout=1760.40 (A=034, B=137) +Fout=1760.60 (A=035, B=137) +Fout=1760.80 (A=036, B=137) +Fout=1761.00 (A=037, B=137) +Fout=1761.20 (A=038, B=137) +Fout=1761.40 (A=039, B=137) +Fout=1761.60 (A=040, B=137) +Fout=1761.80 (A=041, B=137) +Fout=1762.00 (A=042, B=137) +Fout=1762.20 (A=043, B=137) +Fout=1762.40 (A=044, B=137) +Fout=1762.60 (A=045, B=137) +Fout=1762.80 (A=046, B=137) +Fout=1763.00 (A=047, B=137) +Fout=1763.20 (A=048, B=137) +Fout=1763.40 (A=049, B=137) +Fout=1763.60 (A=050, B=137) +Fout=1763.80 (A=051, B=137) +Fout=1764.00 (A=052, B=137) +Fout=1764.20 (A=053, B=137) +Fout=1764.40 (A=054, B=137) +Fout=1764.60 (A=055, B=137) +Fout=1764.80 (A=056, B=137) +Fout=1765.00 (A=057, B=137) +Fout=1765.20 (A=058, B=137) +Fout=1765.40 (A=059, B=137) +Fout=1765.60 (A=060, B=137) +Fout=1765.80 (A=061, B=137) +Fout=1766.00 (A=062, B=137) +Fout=1766.20 (A=063, B=137) +Fout=1766.40 (A=000, B=138) +Fout=1766.60 (A=001, B=138) +Fout=1766.80 (A=002, B=138) +Fout=1767.00 (A=003, B=138) +Fout=1767.20 (A=004, B=138) +Fout=1767.40 (A=005, B=138) +Fout=1767.60 (A=006, B=138) +Fout=1767.80 (A=007, B=138) +Fout=1768.00 (A=008, B=138) +Fout=1768.20 (A=009, B=138) +Fout=1768.40 (A=010, B=138) +Fout=1768.60 (A=011, B=138) +Fout=1768.80 (A=012, B=138) +Fout=1769.00 (A=013, B=138) +Fout=1769.20 (A=014, B=138) +Fout=1769.40 (A=015, B=138) +Fout=1769.60 (A=016, B=138) +Fout=1769.80 (A=017, B=138) +Fout=1770.00 (A=018, B=138) +Fout=1770.20 (A=019, B=138) +Fout=1770.40 (A=020, B=138) +Fout=1770.60 (A=021, B=138) +Fout=1770.80 (A=022, B=138) +Fout=1771.00 (A=023, B=138) +Fout=1771.20 (A=024, B=138) +Fout=1771.40 (A=025, B=138) +Fout=1771.60 (A=026, B=138) +Fout=1771.80 (A=027, B=138) +Fout=1772.00 (A=028, B=138) +Fout=1772.20 (A=029, B=138) +Fout=1772.40 (A=030, B=138) +Fout=1772.60 (A=031, B=138) +Fout=1772.80 (A=032, B=138) +Fout=1773.00 (A=033, B=138) +Fout=1773.20 (A=034, B=138) +Fout=1773.40 (A=035, B=138) +Fout=1773.60 (A=036, B=138) +Fout=1773.80 (A=037, B=138) +Fout=1774.00 (A=038, B=138) +Fout=1774.20 (A=039, B=138) +Fout=1774.40 (A=040, B=138) +Fout=1774.60 (A=041, B=138) +Fout=1774.80 (A=042, B=138) +Fout=1775.00 (A=043, B=138) +Fout=1775.20 (A=044, B=138) +Fout=1775.40 (A=045, B=138) +Fout=1775.60 (A=046, B=138) +Fout=1775.80 (A=047, B=138) +Fout=1776.00 (A=048, B=138) +Fout=1776.20 (A=049, B=138) +Fout=1776.40 (A=050, B=138) +Fout=1776.60 (A=051, B=138) +Fout=1776.80 (A=052, B=138) +Fout=1777.00 (A=053, B=138) +Fout=1777.20 (A=054, B=138) +Fout=1777.40 (A=055, B=138) +Fout=1777.60 (A=056, B=138) +Fout=1777.80 (A=057, B=138) +Fout=1778.00 (A=058, B=138) +Fout=1778.20 (A=059, B=138) +Fout=1778.40 (A=060, B=138) +Fout=1778.60 (A=061, B=138) +Fout=1778.80 (A=062, B=138) +Fout=1779.00 (A=063, B=138) +Fout=1779.20 (A=000, B=139) +Fout=1779.40 (A=001, B=139) +Fout=1779.60 (A=002, B=139) +Fout=1779.80 (A=003, B=139) +Fout=1780.00 (A=004, B=139) +Fout=1780.20 (A=005, B=139) +Fout=1780.40 (A=006, B=139) +Fout=1780.60 (A=007, B=139) +Fout=1780.80 (A=008, B=139) +Fout=1781.00 (A=009, B=139) +Fout=1781.20 (A=010, B=139) +Fout=1781.40 (A=011, B=139) +Fout=1781.60 (A=012, B=139) +Fout=1781.80 (A=013, B=139) +Fout=1782.00 (A=014, B=139) +Fout=1782.20 (A=015, B=139) +Fout=1782.40 (A=016, B=139) +Fout=1782.60 (A=017, B=139) +Fout=1782.80 (A=018, B=139) +Fout=1783.00 (A=019, B=139) +Fout=1783.20 (A=020, B=139) +Fout=1783.40 (A=021, B=139) +Fout=1783.60 (A=022, B=139) +Fout=1783.80 (A=023, B=139) +Fout=1784.00 (A=024, B=139) +Fout=1784.20 (A=025, B=139) +Fout=1784.40 (A=026, B=139) +Fout=1784.60 (A=027, B=139) +Fout=1784.80 (A=028, B=139) +Fout=1785.00 (A=029, B=139) +Fout=1785.20 (A=030, B=139) +Fout=1785.40 (A=031, B=139) +Fout=1785.60 (A=032, B=139) +Fout=1785.80 (A=033, B=139) +Fout=1786.00 (A=034, B=139) +Fout=1786.20 (A=035, B=139) +Fout=1786.40 (A=036, B=139) +Fout=1786.60 (A=037, B=139) +Fout=1786.80 (A=038, B=139) +Fout=1787.00 (A=039, B=139) +Fout=1787.20 (A=040, B=139) +Fout=1787.40 (A=041, B=139) +Fout=1787.60 (A=042, B=139) +Fout=1787.80 (A=043, B=139) +Fout=1788.00 (A=044, B=139) +Fout=1788.20 (A=045, B=139) +Fout=1788.40 (A=046, B=139) +Fout=1788.60 (A=047, B=139) +Fout=1788.80 (A=048, B=139) +Fout=1789.00 (A=049, B=139) +Fout=1789.20 (A=050, B=139) +Fout=1789.40 (A=051, B=139) +Fout=1789.60 (A=052, B=139) +Fout=1789.80 (A=053, B=139) +Fout=1790.00 (A=054, B=139) +Fout=1790.20 (A=055, B=139) +Fout=1790.40 (A=056, B=139) +Fout=1790.60 (A=057, B=139) +Fout=1790.80 (A=058, B=139) +Fout=1791.00 (A=059, B=139) +Fout=1791.20 (A=060, B=139) +Fout=1791.40 (A=061, B=139) +Fout=1791.60 (A=062, B=139) +Fout=1791.80 (A=063, B=139) +Fout=1792.00 (A=000, B=140) +Fout=1792.20 (A=001, B=140) +Fout=1792.40 (A=002, B=140) +Fout=1792.60 (A=003, B=140) +Fout=1792.80 (A=004, B=140) +Fout=1793.00 (A=005, B=140) +Fout=1793.20 (A=006, B=140) +Fout=1793.40 (A=007, B=140) +Fout=1793.60 (A=008, B=140) +Fout=1793.80 (A=009, B=140) +Fout=1794.00 (A=010, B=140) +Fout=1794.20 (A=011, B=140) +Fout=1794.40 (A=012, B=140) +Fout=1794.60 (A=013, B=140) +Fout=1794.80 (A=014, B=140) +Fout=1795.00 (A=015, B=140) +Fout=1795.20 (A=016, B=140) +Fout=1795.40 (A=017, B=140) +Fout=1795.60 (A=018, B=140) +Fout=1795.80 (A=019, B=140) +Fout=1796.00 (A=020, B=140) +Fout=1796.20 (A=021, B=140) +Fout=1796.40 (A=022, B=140) +Fout=1796.60 (A=023, B=140) +Fout=1796.80 (A=024, B=140) +Fout=1797.00 (A=025, B=140) +Fout=1797.20 (A=026, B=140) +Fout=1797.40 (A=027, B=140) +Fout=1797.60 (A=028, B=140) +Fout=1797.80 (A=029, B=140) +Fout=1798.00 (A=030, B=140) +Fout=1798.20 (A=031, B=140) +Fout=1798.40 (A=032, B=140) +Fout=1798.60 (A=033, B=140) +Fout=1798.80 (A=034, B=140) +Fout=1799.00 (A=035, B=140) +Fout=1799.20 (A=036, B=140) +Fout=1799.40 (A=037, B=140) +Fout=1799.60 (A=038, B=140) +Fout=1799.80 (A=039, B=140) +Fout=1800.00 (A=040, B=140) +Fout=1800.20 (A=041, B=140) +Fout=1800.40 (A=042, B=140) +Fout=1800.60 (A=043, B=140) +Fout=1800.80 (A=044, B=140) +Fout=1801.00 (A=045, B=140) +Fout=1801.20 (A=046, B=140) +Fout=1801.40 (A=047, B=140) +Fout=1801.60 (A=048, B=140) +Fout=1801.80 (A=049, B=140) +Fout=1802.00 (A=050, B=140) +Fout=1802.20 (A=051, B=140) +Fout=1802.40 (A=052, B=140) +Fout=1802.60 (A=053, B=140) +Fout=1802.80 (A=054, B=140) +Fout=1803.00 (A=055, B=140) +Fout=1803.20 (A=056, B=140) +Fout=1803.40 (A=057, B=140) +Fout=1803.60 (A=058, B=140) +Fout=1803.80 (A=059, B=140) +Fout=1804.00 (A=060, B=140) +Fout=1804.20 (A=061, B=140) +Fout=1804.40 (A=062, B=140) +Fout=1804.60 (A=063, B=140) +Fout=1804.80 (A=000, B=141) +Fout=1805.00 (A=001, B=141) +Fout=1805.20 (A=002, B=141) +Fout=1805.40 (A=003, B=141) +Fout=1805.60 (A=004, B=141) +Fout=1805.80 (A=005, B=141) +Fout=1806.00 (A=006, B=141) +Fout=1806.20 (A=007, B=141) +Fout=1806.40 (A=008, B=141) +Fout=1806.60 (A=009, B=141) +Fout=1806.80 (A=010, B=141) +Fout=1807.00 (A=011, B=141) +Fout=1807.20 (A=012, B=141) +Fout=1807.40 (A=013, B=141) +Fout=1807.60 (A=014, B=141) +Fout=1807.80 (A=015, B=141) +Fout=1808.00 (A=016, B=141) +Fout=1808.20 (A=017, B=141) +Fout=1808.40 (A=018, B=141) +Fout=1808.60 (A=019, B=141) +Fout=1808.80 (A=020, B=141) +Fout=1809.00 (A=021, B=141) +Fout=1809.20 (A=022, B=141) +Fout=1809.40 (A=023, B=141) +Fout=1809.60 (A=024, B=141) +Fout=1809.80 (A=025, B=141) +Fout=1810.00 (A=026, B=141) +Fout=1810.20 (A=027, B=141) +Fout=1810.40 (A=028, B=141) +Fout=1810.60 (A=029, B=141) +Fout=1810.80 (A=030, B=141) +Fout=1811.00 (A=031, B=141) +Fout=1811.20 (A=032, B=141) +Fout=1811.40 (A=033, B=141) +Fout=1811.60 (A=034, B=141) +Fout=1811.80 (A=035, B=141) +Fout=1812.00 (A=036, B=141) +Fout=1812.20 (A=037, B=141) +Fout=1812.40 (A=038, B=141) +Fout=1812.60 (A=039, B=141) +Fout=1812.80 (A=040, B=141) +Fout=1813.00 (A=041, B=141) +Fout=1813.20 (A=042, B=141) +Fout=1813.40 (A=043, B=141) +Fout=1813.60 (A=044, B=141) +Fout=1813.80 (A=045, B=141) +Fout=1814.00 (A=046, B=141) +Fout=1814.20 (A=047, B=141) +Fout=1814.40 (A=048, B=141) +Fout=1814.60 (A=049, B=141) +Fout=1814.80 (A=050, B=141) +Fout=1815.00 (A=051, B=141) +Fout=1815.20 (A=052, B=141) +Fout=1815.40 (A=053, B=141) +Fout=1815.60 (A=054, B=141) +Fout=1815.80 (A=055, B=141) +Fout=1816.00 (A=056, B=141) +Fout=1816.20 (A=057, B=141) +Fout=1816.40 (A=058, B=141) +Fout=1816.60 (A=059, B=141) +Fout=1816.80 (A=060, B=141) +Fout=1817.00 (A=061, B=141) +Fout=1817.20 (A=062, B=141) +Fout=1817.40 (A=063, B=141) +Fout=1817.60 (A=000, B=142) +Fout=1817.80 (A=001, B=142) +Fout=1818.00 (A=002, B=142) +Fout=1818.20 (A=003, B=142) +Fout=1818.40 (A=004, B=142) +Fout=1818.60 (A=005, B=142) +Fout=1818.80 (A=006, B=142) +Fout=1819.00 (A=007, B=142) +Fout=1819.20 (A=008, B=142) +Fout=1819.40 (A=009, B=142) +Fout=1819.60 (A=010, B=142) +Fout=1819.80 (A=011, B=142) +Fout=1820.00 (A=012, B=142) +Fout=1820.20 (A=013, B=142) +Fout=1820.40 (A=014, B=142) +Fout=1820.60 (A=015, B=142) +Fout=1820.80 (A=016, B=142) +Fout=1821.00 (A=017, B=142) +Fout=1821.20 (A=018, B=142) +Fout=1821.40 (A=019, B=142) +Fout=1821.60 (A=020, B=142) +Fout=1821.80 (A=021, B=142) +Fout=1822.00 (A=022, B=142) +Fout=1822.20 (A=023, B=142) +Fout=1822.40 (A=024, B=142) +Fout=1822.60 (A=025, B=142) +Fout=1822.80 (A=026, B=142) +Fout=1823.00 (A=027, B=142) +Fout=1823.20 (A=028, B=142) +Fout=1823.40 (A=029, B=142) +Fout=1823.60 (A=030, B=142) +Fout=1823.80 (A=031, B=142) +Fout=1824.00 (A=032, B=142) +Fout=1824.20 (A=033, B=142) +Fout=1824.40 (A=034, B=142) +Fout=1824.60 (A=035, B=142) +Fout=1824.80 (A=036, B=142) +Fout=1825.00 (A=037, B=142) +Fout=1825.20 (A=038, B=142) +Fout=1825.40 (A=039, B=142) +Fout=1825.60 (A=040, B=142) +Fout=1825.80 (A=041, B=142) +Fout=1826.00 (A=042, B=142) +Fout=1826.20 (A=043, B=142) +Fout=1826.40 (A=044, B=142) +Fout=1826.60 (A=045, B=142) +Fout=1826.80 (A=046, B=142) +Fout=1827.00 (A=047, B=142) +Fout=1827.20 (A=048, B=142) +Fout=1827.40 (A=049, B=142) +Fout=1827.60 (A=050, B=142) +Fout=1827.80 (A=051, B=142) +Fout=1828.00 (A=052, B=142) +Fout=1828.20 (A=053, B=142) +Fout=1828.40 (A=054, B=142) +Fout=1828.60 (A=055, B=142) +Fout=1828.80 (A=056, B=142) +Fout=1829.00 (A=057, B=142) +Fout=1829.20 (A=058, B=142) +Fout=1829.40 (A=059, B=142) +Fout=1829.60 (A=060, B=142) +Fout=1829.80 (A=061, B=142) +Fout=1830.00 (A=062, B=142) +Fout=1830.20 (A=063, B=142) +Fout=1830.40 (A=000, B=143) +Fout=1830.60 (A=001, B=143) +Fout=1830.80 (A=002, B=143) +Fout=1831.00 (A=003, B=143) +Fout=1831.20 (A=004, B=143) +Fout=1831.40 (A=005, B=143) +Fout=1831.60 (A=006, B=143) +Fout=1831.80 (A=007, B=143) +Fout=1832.00 (A=008, B=143) +Fout=1832.20 (A=009, B=143) +Fout=1832.40 (A=010, B=143) +Fout=1832.60 (A=011, B=143) +Fout=1832.80 (A=012, B=143) +Fout=1833.00 (A=013, B=143) +Fout=1833.20 (A=014, B=143) +Fout=1833.40 (A=015, B=143) +Fout=1833.60 (A=016, B=143) +Fout=1833.80 (A=017, B=143) +Fout=1834.00 (A=018, B=143) +Fout=1834.20 (A=019, B=143) +Fout=1834.40 (A=020, B=143) +Fout=1834.60 (A=021, B=143) +Fout=1834.80 (A=022, B=143) +Fout=1835.00 (A=023, B=143) +Fout=1835.20 (A=024, B=143) +Fout=1835.40 (A=025, B=143) +Fout=1835.60 (A=026, B=143) +Fout=1835.80 (A=027, B=143) +Fout=1836.00 (A=028, B=143) +Fout=1836.20 (A=029, B=143) +Fout=1836.40 (A=030, B=143) +Fout=1836.60 (A=031, B=143) +Fout=1836.80 (A=032, B=143) +Fout=1837.00 (A=033, B=143) +Fout=1837.20 (A=034, B=143) +Fout=1837.40 (A=035, B=143) +Fout=1837.60 (A=036, B=143) +Fout=1837.80 (A=037, B=143) +Fout=1838.00 (A=038, B=143) +Fout=1838.20 (A=039, B=143) +Fout=1838.40 (A=040, B=143) +Fout=1838.60 (A=041, B=143) +Fout=1838.80 (A=042, B=143) +Fout=1839.00 (A=043, B=143) +Fout=1839.20 (A=044, B=143) +Fout=1839.40 (A=045, B=143) +Fout=1839.60 (A=046, B=143) +Fout=1839.80 (A=047, B=143) +Fout=1840.00 (A=048, B=143) +Fout=1840.20 (A=049, B=143) +Fout=1840.40 (A=050, B=143) +Fout=1840.60 (A=051, B=143) +Fout=1840.80 (A=052, B=143) +Fout=1841.00 (A=053, B=143) +Fout=1841.20 (A=054, B=143) +Fout=1841.40 (A=055, B=143) +Fout=1841.60 (A=056, B=143) +Fout=1841.80 (A=057, B=143) +Fout=1842.00 (A=058, B=143) +Fout=1842.20 (A=059, B=143) +Fout=1842.40 (A=060, B=143) +Fout=1842.60 (A=061, B=143) +Fout=1842.80 (A=062, B=143) +Fout=1843.00 (A=063, B=143) +Fout=1843.20 (A=000, B=144) +Fout=1843.40 (A=001, B=144) +Fout=1843.60 (A=002, B=144) +Fout=1843.80 (A=003, B=144) +Fout=1844.00 (A=004, B=144) +Fout=1844.20 (A=005, B=144) +Fout=1844.40 (A=006, B=144) +Fout=1844.60 (A=007, B=144) +Fout=1844.80 (A=008, B=144) +Fout=1845.00 (A=009, B=144) +Fout=1845.20 (A=010, B=144) +Fout=1845.40 (A=011, B=144) +Fout=1845.60 (A=012, B=144) +Fout=1845.80 (A=013, B=144) +Fout=1846.00 (A=014, B=144) +Fout=1846.20 (A=015, B=144) +Fout=1846.40 (A=016, B=144) +Fout=1846.60 (A=017, B=144) +Fout=1846.80 (A=018, B=144) +Fout=1847.00 (A=019, B=144) +Fout=1847.20 (A=020, B=144) +Fout=1847.40 (A=021, B=144) +Fout=1847.60 (A=022, B=144) +Fout=1847.80 (A=023, B=144) +Fout=1848.00 (A=024, B=144) +Fout=1848.20 (A=025, B=144) +Fout=1848.40 (A=026, B=144) +Fout=1848.60 (A=027, B=144) +Fout=1848.80 (A=028, B=144) +Fout=1849.00 (A=029, B=144) +Fout=1849.20 (A=030, B=144) +Fout=1849.40 (A=031, B=144) +Fout=1849.60 (A=032, B=144) +Fout=1849.80 (A=033, B=144) +Fout=1850.00 (A=034, B=144) +Fout=1850.20 (A=035, B=144) +Fout=1850.40 (A=036, B=144) +Fout=1850.60 (A=037, B=144) +Fout=1850.80 (A=038, B=144) +Fout=1851.00 (A=039, B=144) +Fout=1851.20 (A=040, B=144) +Fout=1851.40 (A=041, B=144) +Fout=1851.60 (A=042, B=144) +Fout=1851.80 (A=043, B=144) +Fout=1852.00 (A=044, B=144) +Fout=1852.20 (A=045, B=144) +Fout=1852.40 (A=046, B=144) +Fout=1852.60 (A=047, B=144) +Fout=1852.80 (A=048, B=144) +Fout=1853.00 (A=049, B=144) +Fout=1853.20 (A=050, B=144) +Fout=1853.40 (A=051, B=144) +Fout=1853.60 (A=052, B=144) +Fout=1853.80 (A=053, B=144) +Fout=1854.00 (A=054, B=144) +Fout=1854.20 (A=055, B=144) +Fout=1854.40 (A=056, B=144) +Fout=1854.60 (A=057, B=144) +Fout=1854.80 (A=058, B=144) +Fout=1855.00 (A=059, B=144) +Fout=1855.20 (A=060, B=144) +Fout=1855.40 (A=061, B=144) +Fout=1855.60 (A=062, B=144) +Fout=1855.80 (A=063, B=144) +Fout=1856.00 (A=000, B=145) +Fout=1856.20 (A=001, B=145) +Fout=1856.40 (A=002, B=145) +Fout=1856.60 (A=003, B=145) +Fout=1856.80 (A=004, B=145) +Fout=1857.00 (A=005, B=145) +Fout=1857.20 (A=006, B=145) +Fout=1857.40 (A=007, B=145) +Fout=1857.60 (A=008, B=145) +Fout=1857.80 (A=009, B=145) +Fout=1858.00 (A=010, B=145) +Fout=1858.20 (A=011, B=145) +Fout=1858.40 (A=012, B=145) +Fout=1858.60 (A=013, B=145) +Fout=1858.80 (A=014, B=145) +Fout=1859.00 (A=015, B=145) +Fout=1859.20 (A=016, B=145) +Fout=1859.40 (A=017, B=145) +Fout=1859.60 (A=018, B=145) +Fout=1859.80 (A=019, B=145) +Fout=1860.00 (A=020, B=145) +Fout=1860.20 (A=021, B=145) +Fout=1860.40 (A=022, B=145) +Fout=1860.60 (A=023, B=145) +Fout=1860.80 (A=024, B=145) +Fout=1861.00 (A=025, B=145) +Fout=1861.20 (A=026, B=145) +Fout=1861.40 (A=027, B=145) +Fout=1861.60 (A=028, B=145) +Fout=1861.80 (A=029, B=145) +Fout=1862.00 (A=030, B=145) +Fout=1862.20 (A=031, B=145) +Fout=1862.40 (A=032, B=145) +Fout=1862.60 (A=033, B=145) +Fout=1862.80 (A=034, B=145) +Fout=1863.00 (A=035, B=145) +Fout=1863.20 (A=036, B=145) +Fout=1863.40 (A=037, B=145) +Fout=1863.60 (A=038, B=145) +Fout=1863.80 (A=039, B=145) +Fout=1864.00 (A=040, B=145) +Fout=1864.20 (A=041, B=145) +Fout=1864.40 (A=042, B=145) +Fout=1864.60 (A=043, B=145) +Fout=1864.80 (A=044, B=145) +Fout=1865.00 (A=045, B=145) +Fout=1865.20 (A=046, B=145) +Fout=1865.40 (A=047, B=145) +Fout=1865.60 (A=048, B=145) +Fout=1865.80 (A=049, B=145) +Fout=1866.00 (A=050, B=145) +Fout=1866.20 (A=051, B=145) +Fout=1866.40 (A=052, B=145) +Fout=1866.60 (A=053, B=145) +Fout=1866.80 (A=054, B=145) +Fout=1867.00 (A=055, B=145) +Fout=1867.20 (A=056, B=145) +Fout=1867.40 (A=057, B=145) +Fout=1867.60 (A=058, B=145) +Fout=1867.80 (A=059, B=145) +Fout=1868.00 (A=060, B=145) +Fout=1868.20 (A=061, B=145) +Fout=1868.40 (A=062, B=145) +Fout=1868.60 (A=063, B=145) +Fout=1868.80 (A=000, B=146) +Fout=1869.00 (A=001, B=146) +Fout=1869.20 (A=002, B=146) +Fout=1869.40 (A=003, B=146) +Fout=1869.60 (A=004, B=146) +Fout=1869.80 (A=005, B=146) +Fout=1870.00 (A=006, B=146) +Fout=1870.20 (A=007, B=146) +Fout=1870.40 (A=008, B=146) +Fout=1870.60 (A=009, B=146) +Fout=1870.80 (A=010, B=146) +Fout=1871.00 (A=011, B=146) +Fout=1871.20 (A=012, B=146) +Fout=1871.40 (A=013, B=146) +Fout=1871.60 (A=014, B=146) +Fout=1871.80 (A=015, B=146) +Fout=1872.00 (A=016, B=146) +Fout=1872.20 (A=017, B=146) +Fout=1872.40 (A=018, B=146) +Fout=1872.60 (A=019, B=146) +Fout=1872.80 (A=020, B=146) +Fout=1873.00 (A=021, B=146) +Fout=1873.20 (A=022, B=146) +Fout=1873.40 (A=023, B=146) +Fout=1873.60 (A=024, B=146) +Fout=1873.80 (A=025, B=146) +Fout=1874.00 (A=026, B=146) +Fout=1874.20 (A=027, B=146) +Fout=1874.40 (A=028, B=146) +Fout=1874.60 (A=029, B=146) +Fout=1874.80 (A=030, B=146) +Fout=1875.00 (A=031, B=146) +Fout=1875.20 (A=032, B=146) +Fout=1875.40 (A=033, B=146) +Fout=1875.60 (A=034, B=146) +Fout=1875.80 (A=035, B=146) +Fout=1876.00 (A=036, B=146) +Fout=1876.20 (A=037, B=146) +Fout=1876.40 (A=038, B=146) +Fout=1876.60 (A=039, B=146) +Fout=1876.80 (A=040, B=146) +Fout=1877.00 (A=041, B=146) +Fout=1877.20 (A=042, B=146) +Fout=1877.40 (A=043, B=146) +Fout=1877.60 (A=044, B=146) +Fout=1877.80 (A=045, B=146) +Fout=1878.00 (A=046, B=146) +Fout=1878.20 (A=047, B=146) +Fout=1878.40 (A=048, B=146) +Fout=1878.60 (A=049, B=146) +Fout=1878.80 (A=050, B=146) +Fout=1879.00 (A=051, B=146) +Fout=1879.20 (A=052, B=146) +Fout=1879.40 (A=053, B=146) +Fout=1879.60 (A=054, B=146) +Fout=1879.80 (A=055, B=146) +Fout=1880.00 (A=056, B=146) +Fout=1880.20 (A=057, B=146) +Fout=1880.40 (A=058, B=146) +Fout=1880.60 (A=059, B=146) +Fout=1880.80 (A=060, B=146) +Fout=1881.00 (A=061, B=146) +Fout=1881.20 (A=062, B=146) +Fout=1881.40 (A=063, B=146) +Fout=1881.60 (A=000, B=147) +Fout=1881.80 (A=001, B=147) +Fout=1882.00 (A=002, B=147) +Fout=1882.20 (A=003, B=147) +Fout=1882.40 (A=004, B=147) +Fout=1882.60 (A=005, B=147) +Fout=1882.80 (A=006, B=147) +Fout=1883.00 (A=007, B=147) +Fout=1883.20 (A=008, B=147) +Fout=1883.40 (A=009, B=147) +Fout=1883.60 (A=010, B=147) +Fout=1883.80 (A=011, B=147) +Fout=1884.00 (A=012, B=147) +Fout=1884.20 (A=013, B=147) +Fout=1884.40 (A=014, B=147) +Fout=1884.60 (A=015, B=147) +Fout=1884.80 (A=016, B=147) +Fout=1885.00 (A=017, B=147) +Fout=1885.20 (A=018, B=147) +Fout=1885.40 (A=019, B=147) +Fout=1885.60 (A=020, B=147) +Fout=1885.80 (A=021, B=147) +Fout=1886.00 (A=022, B=147) +Fout=1886.20 (A=023, B=147) +Fout=1886.40 (A=024, B=147) +Fout=1886.60 (A=025, B=147) +Fout=1886.80 (A=026, B=147) +Fout=1887.00 (A=027, B=147) +Fout=1887.20 (A=028, B=147) +Fout=1887.40 (A=029, B=147) +Fout=1887.60 (A=030, B=147) +Fout=1887.80 (A=031, B=147) +Fout=1888.00 (A=032, B=147) +Fout=1888.20 (A=033, B=147) +Fout=1888.40 (A=034, B=147) +Fout=1888.60 (A=035, B=147) +Fout=1888.80 (A=036, B=147) +Fout=1889.00 (A=037, B=147) +Fout=1889.20 (A=038, B=147) +Fout=1889.40 (A=039, B=147) +Fout=1889.60 (A=040, B=147) +Fout=1889.80 (A=041, B=147) +Fout=1890.00 (A=042, B=147) +Fout=1890.20 (A=043, B=147) +Fout=1890.40 (A=044, B=147) +Fout=1890.60 (A=045, B=147) +Fout=1890.80 (A=046, B=147) +Fout=1891.00 (A=047, B=147) +Fout=1891.20 (A=048, B=147) +Fout=1891.40 (A=049, B=147) +Fout=1891.60 (A=050, B=147) +Fout=1891.80 (A=051, B=147) +Fout=1892.00 (A=052, B=147) +Fout=1892.20 (A=053, B=147) +Fout=1892.40 (A=054, B=147) +Fout=1892.60 (A=055, B=147) +Fout=1892.80 (A=056, B=147) +Fout=1893.00 (A=057, B=147) +Fout=1893.20 (A=058, B=147) +Fout=1893.40 (A=059, B=147) +Fout=1893.60 (A=060, B=147) +Fout=1893.80 (A=061, B=147) +Fout=1894.00 (A=062, B=147) +Fout=1894.20 (A=063, B=147) +Fout=1894.40 (A=000, B=148) +Fout=1894.60 (A=001, B=148) +Fout=1894.80 (A=002, B=148) +Fout=1895.00 (A=003, B=148) +Fout=1895.20 (A=004, B=148) +Fout=1895.40 (A=005, B=148) +Fout=1895.60 (A=006, B=148) +Fout=1895.80 (A=007, B=148) +Fout=1896.00 (A=008, B=148) +Fout=1896.20 (A=009, B=148) +Fout=1896.40 (A=010, B=148) +Fout=1896.60 (A=011, B=148) +Fout=1896.80 (A=012, B=148) +Fout=1897.00 (A=013, B=148) +Fout=1897.20 (A=014, B=148) +Fout=1897.40 (A=015, B=148) +Fout=1897.60 (A=016, B=148) +Fout=1897.80 (A=017, B=148) +Fout=1898.00 (A=018, B=148) +Fout=1898.20 (A=019, B=148) +Fout=1898.40 (A=020, B=148) +Fout=1898.60 (A=021, B=148) +Fout=1898.80 (A=022, B=148) +Fout=1899.00 (A=023, B=148) +Fout=1899.20 (A=024, B=148) +Fout=1899.40 (A=025, B=148) +Fout=1899.60 (A=026, B=148) +Fout=1899.80 (A=027, B=148) +Fout=1900.00 (A=028, B=148) +Fout=1900.20 (A=029, B=148) +Fout=1900.40 (A=030, B=148) +Fout=1900.60 (A=031, B=148) +Fout=1900.80 (A=032, B=148) +Fout=1901.00 (A=033, B=148) +Fout=1901.20 (A=034, B=148) +Fout=1901.40 (A=035, B=148) +Fout=1901.60 (A=036, B=148) +Fout=1901.80 (A=037, B=148) +Fout=1902.00 (A=038, B=148) +Fout=1902.20 (A=039, B=148) +Fout=1902.40 (A=040, B=148) +Fout=1902.60 (A=041, B=148) +Fout=1902.80 (A=042, B=148) +Fout=1903.00 (A=043, B=148) +Fout=1903.20 (A=044, B=148) +Fout=1903.40 (A=045, B=148) +Fout=1903.60 (A=046, B=148) +Fout=1903.80 (A=047, B=148) +Fout=1904.00 (A=048, B=148) +Fout=1904.20 (A=049, B=148) +Fout=1904.40 (A=050, B=148) +Fout=1904.60 (A=051, B=148) +Fout=1904.80 (A=052, B=148) +Fout=1905.00 (A=053, B=148) +Fout=1905.20 (A=054, B=148) +Fout=1905.40 (A=055, B=148) +Fout=1905.60 (A=056, B=148) +Fout=1905.80 (A=057, B=148) +Fout=1906.00 (A=058, B=148) +Fout=1906.20 (A=059, B=148) +Fout=1906.40 (A=060, B=148) +Fout=1906.60 (A=061, B=148) +Fout=1906.80 (A=062, B=148) +Fout=1907.00 (A=063, B=148) +Fout=1907.20 (A=000, B=149) +Fout=1907.40 (A=001, B=149) +Fout=1907.60 (A=002, B=149) +Fout=1907.80 (A=003, B=149) +Fout=1908.00 (A=004, B=149) +Fout=1908.20 (A=005, B=149) +Fout=1908.40 (A=006, B=149) +Fout=1908.60 (A=007, B=149) +Fout=1908.80 (A=008, B=149) +Fout=1909.00 (A=009, B=149) +Fout=1909.20 (A=010, B=149) +Fout=1909.40 (A=011, B=149) +Fout=1909.60 (A=012, B=149) +Fout=1909.80 (A=013, B=149) +Fout=1910.00 (A=014, B=149) +Fout=1910.20 (A=015, B=149) +Fout=1910.40 (A=016, B=149) +Fout=1910.60 (A=017, B=149) +Fout=1910.80 (A=018, B=149) +Fout=1911.00 (A=019, B=149) +Fout=1911.20 (A=020, B=149) +Fout=1911.40 (A=021, B=149) +Fout=1911.60 (A=022, B=149) +Fout=1911.80 (A=023, B=149) +Fout=1912.00 (A=024, B=149) +Fout=1912.20 (A=025, B=149) +Fout=1912.40 (A=026, B=149) +Fout=1912.60 (A=027, B=149) +Fout=1912.80 (A=028, B=149) +Fout=1913.00 (A=029, B=149) +Fout=1913.20 (A=030, B=149) +Fout=1913.40 (A=031, B=149) +Fout=1913.60 (A=032, B=149) +Fout=1913.80 (A=033, B=149) +Fout=1914.00 (A=034, B=149) +Fout=1914.20 (A=035, B=149) +Fout=1914.40 (A=036, B=149) +Fout=1914.60 (A=037, B=149) +Fout=1914.80 (A=038, B=149) +Fout=1915.00 (A=039, B=149) +Fout=1915.20 (A=040, B=149) +Fout=1915.40 (A=041, B=149) +Fout=1915.60 (A=042, B=149) +Fout=1915.80 (A=043, B=149) +Fout=1916.00 (A=044, B=149) +Fout=1916.20 (A=045, B=149) +Fout=1916.40 (A=046, B=149) +Fout=1916.60 (A=047, B=149) +Fout=1916.80 (A=048, B=149) +Fout=1917.00 (A=049, B=149) +Fout=1917.20 (A=050, B=149) +Fout=1917.40 (A=051, B=149) +Fout=1917.60 (A=052, B=149) +Fout=1917.80 (A=053, B=149) +Fout=1918.00 (A=054, B=149) +Fout=1918.20 (A=055, B=149) +Fout=1918.40 (A=056, B=149) +Fout=1918.60 (A=057, B=149) +Fout=1918.80 (A=058, B=149) +Fout=1919.00 (A=059, B=149) +Fout=1919.20 (A=060, B=149) +Fout=1919.40 (A=061, B=149) +Fout=1919.60 (A=062, B=149) +Fout=1919.80 (A=063, B=149) diff --git a/src/host/rita_pll/rita_pll_notes.txt b/src/host/rita_pll/rita_pll_notes.txt new file mode 100644 index 00000000..7df03f5e --- /dev/null +++ b/src/host/rita_pll/rita_pll_notes.txt @@ -0,0 +1,19 @@ + +Regular Operation as per DS GSM SPEC + +GSM900 Tx: 870.4 ... 921.4 MHz 880.0 ... 914.8 +GSM900 Rx: 864.4 ... 966.2 MHz 925.0 ... 959.8 + +GSM1800 Tx: 1702.4 ... 1919.8 MHz 1710.2 ... 1784.8 +GSM1800 Rx: 1804.8 ... 1996.4 MHz 1805.2 ... 1879.8 + +GSM850 Tx: + +[824.2 ~ 837.0 MHz] +[837.2 ~ 848.8 MHz] + + +GSM810 Tx: 806.0 ... 821.0 +* modify B value to go down to 125 (A = 60) +GSM810 Rx: 851.0 ... 866.0 +* modify B value to go down to 132 (A = 62) diff --git a/.gitignore b/src/shared/libosmocore/.gitignore index d39ae4a8..d39ae4a8 100644 --- a/.gitignore +++ b/src/shared/libosmocore/.gitignore diff --git a/src/shared/libosmocore/COPYING b/src/shared/libosmocore/COPYING new file mode 100644 index 00000000..d511905c --- /dev/null +++ b/src/shared/libosmocore/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile.am b/src/shared/libosmocore/Makefile.am index 93254e81..93254e81 100644 --- a/Makefile.am +++ b/src/shared/libosmocore/Makefile.am diff --git a/configure.in b/src/shared/libosmocore/configure.in index 309aa034..309aa034 100644 --- a/configure.in +++ b/src/shared/libosmocore/configure.in diff --git a/debian/changelog b/src/shared/libosmocore/debian/changelog index 5783127f..5783127f 100644 --- a/debian/changelog +++ b/src/shared/libosmocore/debian/changelog diff --git a/debian/compat b/src/shared/libosmocore/debian/compat index 7f8f011e..7f8f011e 100644 --- a/debian/compat +++ b/src/shared/libosmocore/debian/compat diff --git a/debian/control b/src/shared/libosmocore/debian/control index 1348ec30..1348ec30 100644 --- a/debian/control +++ b/src/shared/libosmocore/debian/control diff --git a/debian/copyright b/src/shared/libosmocore/debian/copyright index c450be58..c450be58 100644 --- a/debian/copyright +++ b/src/shared/libosmocore/debian/copyright diff --git a/debian/docs b/src/shared/libosmocore/debian/docs index e69de29b..e69de29b 100644 --- a/debian/docs +++ b/src/shared/libosmocore/debian/docs diff --git a/debian/libosmocore-dbg.debhelper.log b/src/shared/libosmocore/debian/libosmocore-dbg.debhelper.log index 2742cb47..2742cb47 100644 --- a/debian/libosmocore-dbg.debhelper.log +++ b/src/shared/libosmocore/debian/libosmocore-dbg.debhelper.log diff --git a/debian/libosmocore-dbg.dirs b/src/shared/libosmocore/debian/libosmocore-dbg.dirs index af59b0a9..af59b0a9 100644 --- a/debian/libosmocore-dbg.dirs +++ b/src/shared/libosmocore/debian/libosmocore-dbg.dirs diff --git a/debian/libosmocore-dbg.install b/src/shared/libosmocore/debian/libosmocore-dbg.install index 7ce02127..7ce02127 100644 --- a/debian/libosmocore-dbg.install +++ b/src/shared/libosmocore/debian/libosmocore-dbg.install diff --git a/debian/libosmocore-dev.dirs b/src/shared/libosmocore/debian/libosmocore-dev.dirs index e168dc66..e168dc66 100644 --- a/debian/libosmocore-dev.dirs +++ b/src/shared/libosmocore/debian/libosmocore-dev.dirs diff --git a/debian/libosmocore-dev.install b/src/shared/libosmocore/debian/libosmocore-dev.install index eec0e15e..eec0e15e 100644 --- a/debian/libosmocore-dev.install +++ b/src/shared/libosmocore/debian/libosmocore-dev.install diff --git a/debian/libosmocore.dirs b/src/shared/libosmocore/debian/libosmocore.dirs index e168dc66..e168dc66 100644 --- a/debian/libosmocore.dirs +++ b/src/shared/libosmocore/debian/libosmocore.dirs diff --git a/debian/libosmocore.install b/src/shared/libosmocore/debian/libosmocore.install index d0dbfd18..d0dbfd18 100644 --- a/debian/libosmocore.install +++ b/src/shared/libosmocore/debian/libosmocore.install diff --git a/debian/patches/debian-changes-0.1.17-1 b/src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1 index c0a54bd7..c0a54bd7 100644 --- a/debian/patches/debian-changes-0.1.17-1 +++ b/src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1 diff --git a/debian/patches/series b/src/shared/libosmocore/debian/patches/series index 0ca407b1..0ca407b1 100644 --- a/debian/patches/series +++ b/src/shared/libosmocore/debian/patches/series diff --git a/debian/rules b/src/shared/libosmocore/debian/rules index a81850a2..a81850a2 100755 --- a/debian/rules +++ b/src/shared/libosmocore/debian/rules diff --git a/debian/source/format b/src/shared/libosmocore/debian/source/format index 163aaf8d..163aaf8d 100644 --- a/debian/source/format +++ b/src/shared/libosmocore/debian/source/format diff --git a/git-version-gen b/src/shared/libosmocore/git-version-gen index 42cf3d2b..42cf3d2b 100755 --- a/git-version-gen +++ b/src/shared/libosmocore/git-version-gen diff --git a/include/Makefile.am b/src/shared/libosmocore/include/Makefile.am index 185c6968..185c6968 100644 --- a/include/Makefile.am +++ b/src/shared/libosmocore/include/Makefile.am diff --git a/include/osmocom/Makefile.am b/src/shared/libosmocore/include/osmocom/Makefile.am index ec548fbd..ec548fbd 100644 --- a/include/osmocom/Makefile.am +++ b/src/shared/libosmocore/include/osmocom/Makefile.am diff --git a/include/osmocom/codec/Makefile.am b/src/shared/libosmocore/include/osmocom/codec/Makefile.am index c2136023..c2136023 100644 --- a/include/osmocom/codec/Makefile.am +++ b/src/shared/libosmocore/include/osmocom/codec/Makefile.am diff --git a/include/osmocom/codec/codec.h b/src/shared/libosmocore/include/osmocom/codec/codec.h index 6f9ffea5..6f9ffea5 100644 --- a/include/osmocom/codec/codec.h +++ b/src/shared/libosmocore/include/osmocom/codec/codec.h diff --git a/include/osmocom/crypt/Makefile.am b/src/shared/libosmocore/include/osmocom/crypt/Makefile.am index 7ce69fdd..7ce69fdd 100644 --- a/include/osmocom/crypt/Makefile.am +++ b/src/shared/libosmocore/include/osmocom/crypt/Makefile.am diff --git a/include/osmocom/crypt/gprs_cipher.h b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h index 3e514ec7..3e514ec7 100644 --- a/include/osmocom/crypt/gprs_cipher.h +++ b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h diff --git a/include/osmocom/vty/Makefile.am b/src/shared/libosmocore/include/osmocom/vty/Makefile.am index d2f0616d..d2f0616d 100644 --- a/include/osmocom/vty/Makefile.am +++ b/src/shared/libosmocore/include/osmocom/vty/Makefile.am diff --git a/include/osmocom/vty/buffer.h b/src/shared/libosmocore/include/osmocom/vty/buffer.h index c9467a91..c9467a91 100644 --- a/include/osmocom/vty/buffer.h +++ b/src/shared/libosmocore/include/osmocom/vty/buffer.h diff --git a/include/osmocom/vty/command.h b/src/shared/libosmocore/include/osmocom/vty/command.h index 69e9e772..69e9e772 100644 --- a/include/osmocom/vty/command.h +++ b/src/shared/libosmocore/include/osmocom/vty/command.h diff --git a/include/osmocom/vty/logging.h b/src/shared/libosmocore/include/osmocom/vty/logging.h index 45d74fdf..45d74fdf 100644 --- a/include/osmocom/vty/logging.h +++ b/src/shared/libosmocore/include/osmocom/vty/logging.h diff --git a/include/osmocom/vty/telnet_interface.h b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h index 444e6497..444e6497 100644 --- a/include/osmocom/vty/telnet_interface.h +++ b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h diff --git a/include/osmocom/vty/vector.h b/src/shared/libosmocore/include/osmocom/vty/vector.h index 22a184d6..22a184d6 100644 --- a/include/osmocom/vty/vector.h +++ b/src/shared/libosmocore/include/osmocom/vty/vector.h diff --git a/include/osmocom/vty/vty.h b/src/shared/libosmocore/include/osmocom/vty/vty.h index 8035585d..8035585d 100644 --- a/include/osmocom/vty/vty.h +++ b/src/shared/libosmocore/include/osmocom/vty/vty.h diff --git a/include/osmocore/Makefile.am b/src/shared/libosmocore/include/osmocore/Makefile.am index 2efaa96b..2efaa96b 100644 --- a/include/osmocore/Makefile.am +++ b/src/shared/libosmocore/include/osmocore/Makefile.am diff --git a/include/osmocore/bitvec.h b/src/shared/libosmocore/include/osmocore/bitvec.h index 42977fb2..42977fb2 100644 --- a/include/osmocore/bitvec.h +++ b/src/shared/libosmocore/include/osmocore/bitvec.h diff --git a/include/osmocore/comp128.h b/src/shared/libosmocore/include/osmocore/comp128.h index c37808f0..c37808f0 100644 --- a/include/osmocore/comp128.h +++ b/src/shared/libosmocore/include/osmocore/comp128.h diff --git a/include/osmocore/crc16.h b/src/shared/libosmocore/include/osmocore/crc16.h index 7a512490..7a512490 100644 --- a/include/osmocore/crc16.h +++ b/src/shared/libosmocore/include/osmocore/crc16.h diff --git a/include/osmocore/gsm0480.h b/src/shared/libosmocore/include/osmocore/gsm0480.h index b504332e..b504332e 100644 --- a/include/osmocore/gsm0480.h +++ b/src/shared/libosmocore/include/osmocore/gsm0480.h diff --git a/include/osmocore/gsm0808.h b/src/shared/libosmocore/include/osmocore/gsm0808.h index 1d853775..1d853775 100644 --- a/include/osmocore/gsm0808.h +++ b/src/shared/libosmocore/include/osmocore/gsm0808.h diff --git a/include/osmocore/gsm48.h b/src/shared/libosmocore/include/osmocore/gsm48.h index ffe0399b..ffe0399b 100644 --- a/include/osmocore/gsm48.h +++ b/src/shared/libosmocore/include/osmocore/gsm48.h diff --git a/include/osmocore/gsm48_ie.h b/src/shared/libosmocore/include/osmocore/gsm48_ie.h index fa66159f..fa66159f 100644 --- a/include/osmocore/gsm48_ie.h +++ b/src/shared/libosmocore/include/osmocore/gsm48_ie.h diff --git a/include/osmocore/gsm_utils.h b/src/shared/libosmocore/include/osmocore/gsm_utils.h index 0aadd2e4..0aadd2e4 100644 --- a/include/osmocore/gsm_utils.h +++ b/src/shared/libosmocore/include/osmocore/gsm_utils.h diff --git a/include/osmocore/gsmtap.h b/src/shared/libosmocore/include/osmocore/gsmtap.h index 3f384a4f..3f384a4f 100644 --- a/include/osmocore/gsmtap.h +++ b/src/shared/libosmocore/include/osmocore/gsmtap.h diff --git a/include/osmocore/gsmtap_util.h b/src/shared/libosmocore/include/osmocore/gsmtap_util.h index 96449443..96449443 100644 --- a/include/osmocore/gsmtap_util.h +++ b/src/shared/libosmocore/include/osmocore/gsmtap_util.h diff --git a/include/osmocore/linuxlist.h b/src/shared/libosmocore/include/osmocore/linuxlist.h index fb99c5ec..fb99c5ec 100644 --- a/include/osmocore/linuxlist.h +++ b/src/shared/libosmocore/include/osmocore/linuxlist.h diff --git a/include/osmocore/logging.h b/src/shared/libosmocore/include/osmocore/logging.h index 7f33155a..7f33155a 100644 --- a/include/osmocore/logging.h +++ b/src/shared/libosmocore/include/osmocore/logging.h diff --git a/include/osmocore/mncc.h b/src/shared/libosmocore/include/osmocore/mncc.h index a094bb9b..a094bb9b 100644 --- a/include/osmocore/mncc.h +++ b/src/shared/libosmocore/include/osmocore/mncc.h diff --git a/include/osmocore/msgb.h b/src/shared/libosmocore/include/osmocore/msgb.h index 6fd24c7f..6fd24c7f 100644 --- a/include/osmocore/msgb.h +++ b/src/shared/libosmocore/include/osmocore/msgb.h diff --git a/include/osmocore/msgfile.h b/src/shared/libosmocore/include/osmocore/msgfile.h index 92caa9fc..92caa9fc 100644 --- a/include/osmocore/msgfile.h +++ b/src/shared/libosmocore/include/osmocore/msgfile.h diff --git a/include/osmocore/panic.h b/src/shared/libosmocore/include/osmocore/panic.h index c5a83778..c5a83778 100644 --- a/include/osmocore/panic.h +++ b/src/shared/libosmocore/include/osmocore/panic.h diff --git a/include/osmocore/plugin.h b/src/shared/libosmocore/include/osmocore/plugin.h index 98f9b56d..98f9b56d 100644 --- a/include/osmocore/plugin.h +++ b/src/shared/libosmocore/include/osmocore/plugin.h diff --git a/include/osmocore/process.h b/src/shared/libosmocore/include/osmocore/process.h index 2d663828..2d663828 100644 --- a/include/osmocore/process.h +++ b/src/shared/libosmocore/include/osmocore/process.h diff --git a/include/osmocore/protocol/Makefile.am b/src/shared/libosmocore/include/osmocore/protocol/Makefile.am index 03a4849d..03a4849d 100644 --- a/include/osmocore/protocol/Makefile.am +++ b/src/shared/libosmocore/include/osmocore/protocol/Makefile.am diff --git a/include/osmocore/protocol/gsm_03_41.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_03_41.h index 3b1b7c9f..3b1b7c9f 100644 --- a/include/osmocore/protocol/gsm_03_41.h +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_03_41.h diff --git a/include/osmocore/protocol/gsm_04_08.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h index 3ad7dfdc..3ad7dfdc 100644 --- a/include/osmocore/protocol/gsm_04_08.h +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h diff --git a/include/osmocore/protocol/gsm_04_11.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h index c6a2b193..c6a2b193 100644 --- a/include/osmocore/protocol/gsm_04_11.h +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h diff --git a/include/osmocore/protocol/gsm_04_12.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_12.h index 9b1538a5..9b1538a5 100644 --- a/include/osmocore/protocol/gsm_04_12.h +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_12.h diff --git a/include/osmocore/protocol/gsm_04_80.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_80.h index fa5c9451..fa5c9451 100644 --- a/include/osmocore/protocol/gsm_04_80.h +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_80.h diff --git a/include/osmocore/protocol/gsm_08_08.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_08.h index 6b8f9359..6b8f9359 100644 --- a/include/osmocore/protocol/gsm_08_08.h +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_08.h diff --git a/include/osmocore/protocol/gsm_08_58.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h index 74a4083b..74a4083b 100644 --- a/include/osmocore/protocol/gsm_08_58.h +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h diff --git a/include/osmocore/protocol/gsm_12_21.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_12_21.h index 9cae45da..9cae45da 100644 --- a/include/osmocore/protocol/gsm_12_21.h +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_12_21.h diff --git a/include/osmocore/rate_ctr.h b/src/shared/libosmocore/include/osmocore/rate_ctr.h index f887d9a7..f887d9a7 100644 --- a/include/osmocore/rate_ctr.h +++ b/src/shared/libosmocore/include/osmocore/rate_ctr.h diff --git a/src/shared/libosmocore/include/osmocore/rsl.h b/src/shared/libosmocore/include/osmocore/rsl.h new file mode 100644 index 00000000..54d67032 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/rsl.h @@ -0,0 +1,41 @@ +#ifndef _OSMOCORE_RSL_H +#define _OSMOCORE_RSL_H + +#include <stdint.h> +#include <osmocore/utils.h> +#include <osmocore/protocol/gsm_08_58.h> + +void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type); + +void rsl_init_cchan_hdr(struct abis_rsl_cchan_hdr *ch, uint8_t msg_type); + +extern const struct tlv_definition rsl_att_tlvdef; +#define rsl_tlv_parse(dec, buf, len) \ + tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0) + +/* encode channel number as per Section 9.3.1 */ +uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot); +/* decode channel number as per Section 9.3.1 */ +int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot); +/* Turns channel number into a string */ +const char *rsl_chan_nr_str(uint8_t chan_nr); + + +const char *rsl_err_name(uint8_t err); +const char *rsl_rlm_cause_name(uint8_t err); + +/* Section 3.3.2.3 TS 05.02. I think this looks like a table */ +int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf); + +/* Push a RSL RLL header */ +void rsl_rll_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, + uint8_t link_id, int transparent); + +/* Push a RSL RLL header with L3_INFO IE */ +void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, + uint8_t link_id, int transparent); + +/* Allocate msgb and fill with simple RSL RLL header */ +struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr, + uint8_t link_id, int transparent); +#endif /* _OSMOCORE_RSL_H */ diff --git a/include/osmocore/rxlev_stat.h b/src/shared/libosmocore/include/osmocore/rxlev_stat.h index 415509dc..415509dc 100644 --- a/include/osmocore/rxlev_stat.h +++ b/src/shared/libosmocore/include/osmocore/rxlev_stat.h diff --git a/include/osmocore/select.h b/src/shared/libosmocore/include/osmocore/select.h index 2d8b3ec0..2d8b3ec0 100644 --- a/include/osmocore/select.h +++ b/src/shared/libosmocore/include/osmocore/select.h diff --git a/include/osmocore/signal.h b/src/shared/libosmocore/include/osmocore/signal.h index 02d83d2e..02d83d2e 100644 --- a/include/osmocore/signal.h +++ b/src/shared/libosmocore/include/osmocore/signal.h diff --git a/include/osmocore/statistics.h b/src/shared/libosmocore/include/osmocore/statistics.h index 1d56054a..1d56054a 100644 --- a/include/osmocore/statistics.h +++ b/src/shared/libosmocore/include/osmocore/statistics.h diff --git a/include/osmocore/talloc.h b/src/shared/libosmocore/include/osmocore/talloc.h index f7f7643b..f7f7643b 100644 --- a/include/osmocore/talloc.h +++ b/src/shared/libosmocore/include/osmocore/talloc.h diff --git a/include/osmocore/timer.h b/src/shared/libosmocore/include/osmocore/timer.h index fee888bf..fee888bf 100644 --- a/include/osmocore/timer.h +++ b/src/shared/libosmocore/include/osmocore/timer.h diff --git a/include/osmocore/tlv.h b/src/shared/libosmocore/include/osmocore/tlv.h index 4cfce872..4cfce872 100644 --- a/include/osmocore/tlv.h +++ b/src/shared/libosmocore/include/osmocore/tlv.h diff --git a/src/shared/libosmocore/include/osmocore/utils.h b/src/shared/libosmocore/include/osmocore/utils.h new file mode 100644 index 00000000..3574f7f8 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/utils.h @@ -0,0 +1,24 @@ +#ifndef OSMOCORE_UTIL_H +#define OSMOCORE_UTIL_H + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#include <stdint.h> + +struct value_string { + unsigned int value; + const char *str; +}; + +const char *get_value_string(const struct value_string *vs, uint32_t val); +int get_string_value(const struct value_string *vs, const char *str); + +char bcd2char(uint8_t bcd); +/* only works for numbers in ascci */ +uint8_t char2bcd(char c); + +int hexparse(const char *str, uint8_t *b, int max_len); +char *hexdump(const unsigned char *buf, int len); +char *hexdump_nospc(const unsigned char *buf, int len); + +#endif diff --git a/include/osmocore/write_queue.h b/src/shared/libosmocore/include/osmocore/write_queue.h index ef244c32..ef244c32 100644 --- a/include/osmocore/write_queue.h +++ b/src/shared/libosmocore/include/osmocore/write_queue.h diff --git a/libosmocodec.pc.in b/src/shared/libosmocore/libosmocodec.pc.in index 3030230b..3030230b 100644 --- a/libosmocodec.pc.in +++ b/src/shared/libosmocore/libosmocodec.pc.in diff --git a/libosmocore.pc.in b/src/shared/libosmocore/libosmocore.pc.in index 7c298693..7c298693 100644 --- a/libosmocore.pc.in +++ b/src/shared/libosmocore/libosmocore.pc.in diff --git a/libosmovty.pc.in b/src/shared/libosmocore/libosmovty.pc.in index 2cc0b5f8..2cc0b5f8 100644 --- a/libosmovty.pc.in +++ b/src/shared/libosmocore/libosmovty.pc.in diff --git a/m4/DUMMY b/src/shared/libosmocore/m4/DUMMY index fda557ad..fda557ad 100644 --- a/m4/DUMMY +++ b/src/shared/libosmocore/m4/DUMMY diff --git a/src/Makefile.am b/src/shared/libosmocore/src/Makefile.am index 64310e0f..64310e0f 100644 --- a/src/Makefile.am +++ b/src/shared/libosmocore/src/Makefile.am diff --git a/src/bitvec.c b/src/shared/libosmocore/src/bitvec.c index 95d78a5c..95d78a5c 100644 --- a/src/bitvec.c +++ b/src/shared/libosmocore/src/bitvec.c diff --git a/src/codec/Makefile.am b/src/shared/libosmocore/src/codec/Makefile.am index f8a06012..f8a06012 100644 --- a/src/codec/Makefile.am +++ b/src/shared/libosmocore/src/codec/Makefile.am diff --git a/src/codec/gsm610.c b/src/shared/libosmocore/src/codec/gsm610.c index 09fbeb5a..09fbeb5a 100644 --- a/src/codec/gsm610.c +++ b/src/shared/libosmocore/src/codec/gsm610.c diff --git a/src/codec/gsm620.c b/src/shared/libosmocore/src/codec/gsm620.c index 09aca502..09aca502 100644 --- a/src/codec/gsm620.c +++ b/src/shared/libosmocore/src/codec/gsm620.c diff --git a/src/codec/gsm660.c b/src/shared/libosmocore/src/codec/gsm660.c index 4fff5ffc..4fff5ffc 100644 --- a/src/codec/gsm660.c +++ b/src/shared/libosmocore/src/codec/gsm660.c diff --git a/src/codec/gsm690.c b/src/shared/libosmocore/src/codec/gsm690.c index e5b9bd49..e5b9bd49 100644 --- a/src/codec/gsm690.c +++ b/src/shared/libosmocore/src/codec/gsm690.c diff --git a/src/comp128.c b/src/shared/libosmocore/src/comp128.c index 5d5680c7..5d5680c7 100644 --- a/src/comp128.c +++ b/src/shared/libosmocore/src/comp128.c diff --git a/src/crc16.c b/src/shared/libosmocore/src/crc16.c index 8ac8031e..8ac8031e 100644 --- a/src/crc16.c +++ b/src/shared/libosmocore/src/crc16.c diff --git a/src/gprs_cipher_core.c b/src/shared/libosmocore/src/gprs_cipher_core.c index 6174bd72..6174bd72 100644 --- a/src/gprs_cipher_core.c +++ b/src/shared/libosmocore/src/gprs_cipher_core.c diff --git a/src/gsm0480.c b/src/shared/libosmocore/src/gsm0480.c index b6b345cb..b6b345cb 100644 --- a/src/gsm0480.c +++ b/src/shared/libosmocore/src/gsm0480.c diff --git a/src/gsm0808.c b/src/shared/libosmocore/src/gsm0808.c index dc450cc4..dc450cc4 100644 --- a/src/gsm0808.c +++ b/src/shared/libosmocore/src/gsm0808.c diff --git a/src/gsm48.c b/src/shared/libosmocore/src/gsm48.c index daec4f39..daec4f39 100644 --- a/src/gsm48.c +++ b/src/shared/libosmocore/src/gsm48.c diff --git a/src/gsm48_ie.c b/src/shared/libosmocore/src/gsm48_ie.c index 0e270881..0e270881 100644 --- a/src/gsm48_ie.c +++ b/src/shared/libosmocore/src/gsm48_ie.c diff --git a/src/gsm_utils.c b/src/shared/libosmocore/src/gsm_utils.c index 31e3cd69..31e3cd69 100644 --- a/src/gsm_utils.c +++ b/src/shared/libosmocore/src/gsm_utils.c diff --git a/src/gsmtap_util.c b/src/shared/libosmocore/src/gsmtap_util.c index abee4dac..abee4dac 100644 --- a/src/gsmtap_util.c +++ b/src/shared/libosmocore/src/gsmtap_util.c diff --git a/src/logging.c b/src/shared/libosmocore/src/logging.c index b3b5cb69..b3b5cb69 100644 --- a/src/logging.c +++ b/src/shared/libosmocore/src/logging.c diff --git a/src/msgb.c b/src/shared/libosmocore/src/msgb.c index 12a839a9..12a839a9 100644 --- a/src/msgb.c +++ b/src/shared/libosmocore/src/msgb.c diff --git a/src/msgfile.c b/src/shared/libosmocore/src/msgfile.c index 68f05813..68f05813 100644 --- a/src/msgfile.c +++ b/src/shared/libosmocore/src/msgfile.c diff --git a/src/panic.c b/src/shared/libosmocore/src/panic.c index 5fb7b565..5fb7b565 100644 --- a/src/panic.c +++ b/src/shared/libosmocore/src/panic.c diff --git a/src/plugin.c b/src/shared/libosmocore/src/plugin.c index 3ba2d431..3ba2d431 100644 --- a/src/plugin.c +++ b/src/shared/libosmocore/src/plugin.c diff --git a/src/process.c b/src/shared/libosmocore/src/process.c index 180efa51..180efa51 100644 --- a/src/process.c +++ b/src/shared/libosmocore/src/process.c diff --git a/src/rate_ctr.c b/src/shared/libosmocore/src/rate_ctr.c index 80ef55b2..80ef55b2 100644 --- a/src/rate_ctr.c +++ b/src/shared/libosmocore/src/rate_ctr.c diff --git a/src/rsl.c b/src/shared/libosmocore/src/rsl.c index 3bfeffb9..3bfeffb9 100644 --- a/src/rsl.c +++ b/src/shared/libosmocore/src/rsl.c diff --git a/src/rxlev_stat.c b/src/shared/libosmocore/src/rxlev_stat.c index 1bfd6795..1bfd6795 100644 --- a/src/rxlev_stat.c +++ b/src/shared/libosmocore/src/rxlev_stat.c diff --git a/src/select.c b/src/shared/libosmocore/src/select.c index 5aa2beb5..5aa2beb5 100644 --- a/src/select.c +++ b/src/shared/libosmocore/src/select.c diff --git a/src/signal.c b/src/shared/libosmocore/src/signal.c index c7ca86c4..c7ca86c4 100644 --- a/src/signal.c +++ b/src/shared/libosmocore/src/signal.c diff --git a/src/statistics.c b/src/shared/libosmocore/src/statistics.c index 34e6a408..34e6a408 100644 --- a/src/statistics.c +++ b/src/shared/libosmocore/src/statistics.c diff --git a/src/talloc.c b/src/shared/libosmocore/src/talloc.c index 98c2ee09..98c2ee09 100644 --- a/src/talloc.c +++ b/src/shared/libosmocore/src/talloc.c diff --git a/src/timer.c b/src/shared/libosmocore/src/timer.c index 37d7d166..37d7d166 100644 --- a/src/timer.c +++ b/src/shared/libosmocore/src/timer.c diff --git a/src/tlv_parser.c b/src/shared/libosmocore/src/tlv_parser.c index bbef7a9a..bbef7a9a 100644 --- a/src/tlv_parser.c +++ b/src/shared/libosmocore/src/tlv_parser.c diff --git a/src/utils.c b/src/shared/libosmocore/src/utils.c index 405039f6..405039f6 100644 --- a/src/utils.c +++ b/src/shared/libosmocore/src/utils.c diff --git a/src/vty/Makefile.am b/src/shared/libosmocore/src/vty/Makefile.am index 7353ab84..7353ab84 100644 --- a/src/vty/Makefile.am +++ b/src/shared/libosmocore/src/vty/Makefile.am diff --git a/src/vty/buffer.c b/src/shared/libosmocore/src/vty/buffer.c index a5655b93..a5655b93 100644 --- a/src/vty/buffer.c +++ b/src/shared/libosmocore/src/vty/buffer.c diff --git a/src/vty/command.c b/src/shared/libosmocore/src/vty/command.c index 7525df65..7525df65 100644 --- a/src/vty/command.c +++ b/src/shared/libosmocore/src/vty/command.c diff --git a/src/vty/logging_vty.c b/src/shared/libosmocore/src/vty/logging_vty.c index 896d79a9..896d79a9 100644 --- a/src/vty/logging_vty.c +++ b/src/shared/libosmocore/src/vty/logging_vty.c diff --git a/src/vty/telnet_interface.c b/src/shared/libosmocore/src/vty/telnet_interface.c index 098fa2e6..098fa2e6 100644 --- a/src/vty/telnet_interface.c +++ b/src/shared/libosmocore/src/vty/telnet_interface.c diff --git a/src/vty/utils.c b/src/shared/libosmocore/src/vty/utils.c index e163526e..e163526e 100644 --- a/src/vty/utils.c +++ b/src/shared/libosmocore/src/vty/utils.c diff --git a/src/vty/vector.c b/src/shared/libosmocore/src/vty/vector.c index 0343163f..0343163f 100644 --- a/src/vty/vector.c +++ b/src/shared/libosmocore/src/vty/vector.c diff --git a/src/vty/vty.c b/src/shared/libosmocore/src/vty/vty.c index a5b16dce..a5b16dce 100644 --- a/src/vty/vty.c +++ b/src/shared/libosmocore/src/vty/vty.c diff --git a/src/write_queue.c b/src/shared/libosmocore/src/write_queue.c index 618a8c0b..618a8c0b 100644 --- a/src/write_queue.c +++ b/src/shared/libosmocore/src/write_queue.c diff --git a/tests/Makefile.am b/src/shared/libosmocore/tests/Makefile.am index 2b4ac6e6..2b4ac6e6 100644 --- a/tests/Makefile.am +++ b/src/shared/libosmocore/tests/Makefile.am diff --git a/tests/msgfile/Makefile.am b/src/shared/libosmocore/tests/msgfile/Makefile.am index c9f4bec2..c9f4bec2 100644 --- a/tests/msgfile/Makefile.am +++ b/src/shared/libosmocore/tests/msgfile/Makefile.am diff --git a/tests/msgfile/msgconfig.cfg b/src/shared/libosmocore/tests/msgfile/msgconfig.cfg index 28d74326..28d74326 100644 --- a/tests/msgfile/msgconfig.cfg +++ b/src/shared/libosmocore/tests/msgfile/msgconfig.cfg diff --git a/tests/msgfile/msgfile_test.c b/src/shared/libosmocore/tests/msgfile/msgfile_test.c index a82ac516..a82ac516 100644 --- a/tests/msgfile/msgfile_test.c +++ b/src/shared/libosmocore/tests/msgfile/msgfile_test.c diff --git a/tests/sms/Makefile.am b/src/shared/libosmocore/tests/sms/Makefile.am index a8f1ff6a..a8f1ff6a 100644 --- a/tests/sms/Makefile.am +++ b/src/shared/libosmocore/tests/sms/Makefile.am diff --git a/tests/sms/sms_test.c b/src/shared/libosmocore/tests/sms/sms_test.c index 9d87b5bc..9d87b5bc 100644 --- a/tests/sms/sms_test.c +++ b/src/shared/libosmocore/tests/sms/sms_test.c diff --git a/tests/smscb/Makefile.am b/src/shared/libosmocore/tests/smscb/Makefile.am index 1d0e5384..1d0e5384 100644 --- a/tests/smscb/Makefile.am +++ b/src/shared/libosmocore/tests/smscb/Makefile.am diff --git a/tests/smscb/smscb_test.c b/src/shared/libosmocore/tests/smscb/smscb_test.c index 627d5a13..627d5a13 100644 --- a/tests/smscb/smscb_test.c +++ b/src/shared/libosmocore/tests/smscb/smscb_test.c diff --git a/tests/timer/Makefile.am b/src/shared/libosmocore/tests/timer/Makefile.am index d3decf55..d3decf55 100644 --- a/tests/timer/Makefile.am +++ b/src/shared/libosmocore/tests/timer/Makefile.am diff --git a/tests/timer/timer_test.c b/src/shared/libosmocore/tests/timer/timer_test.c index 1b458d81..1b458d81 100644 --- a/tests/timer/timer_test.c +++ b/src/shared/libosmocore/tests/timer/timer_test.c diff --git a/tests/ussd/Makefile.am b/src/shared/libosmocore/tests/ussd/Makefile.am index d29506cc..d29506cc 100644 --- a/tests/ussd/Makefile.am +++ b/src/shared/libosmocore/tests/ussd/Makefile.am diff --git a/tests/ussd/ussd_test.c b/src/shared/libosmocore/tests/ussd/ussd_test.c index bddbbcb7..bddbbcb7 100644 --- a/tests/ussd/ussd_test.c +++ b/src/shared/libosmocore/tests/ussd/ussd_test.c diff --git a/src/shared/update-libosmocore.sh b/src/shared/update-libosmocore.sh new file mode 100755 index 00000000..e7940c3d --- /dev/null +++ b/src/shared/update-libosmocore.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +(cd ../.. && git-subtree pull --prefix=src/shared/libosmocore git://git.osmocom.org/libosmocore.git master) diff --git a/src/target/firmware/.gitignore b/src/target/firmware/.gitignore new file mode 100644 index 00000000..79e98dfc --- /dev/null +++ b/src/target/firmware/.gitignore @@ -0,0 +1,9 @@ +*.o +*.p +*.a +*.lst +*.bin +*.elf +*.map +*.size +*~ diff --git a/src/target/firmware/COPYING b/src/target/firmware/COPYING new file mode 100644 index 00000000..d511905c --- /dev/null +++ b/src/target/firmware/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile new file mode 100644 index 00000000..56e0068e --- /dev/null +++ b/src/target/firmware/Makefile @@ -0,0 +1,82 @@ + + +# List of all supported boards (meant to be overridden on command line) +BOARDS?=compal_e88 compal_e99 gta0x + +# List of all applications (meant to be overridden on command line) +APPLICATIONS?=hello_world compal_dsp_dump layer1 loader simtest chainload + + +# TI Calypso + +calypso_COMMON_OBJS=board/common/calypso_uart.o board/common/calypso_pwl.o + +# OpenMoko GTA0x + +gta0x_COMMON_OBJS=$(calypso_COMMON_OBJS) board/common/rffe_gta0x_triband.o +gta0x_COMMON_ENVIRONMENTS=highram +gta0x_OBJS=$(gta0x_COMMON_OBJS) board/gta0x/init.o board/gta0x/rf_power.o +gta0x_ENVIRONMENTS=$(gta0x_COMMON_ENVIRONMENTS) + +# Compal Generic + +compal_COMMON_OBJS=$(calypso_COMMON_OBJS) board/compal/rffe_dualband.o board/compal/rf_power.o +compal_COMMON_ENVIRONMENTS=compalram highram + +compalram_LDS=board/compal/ram.lds +compalram_OBJS=board/compal/start.ram.o board/compal/exceptions_redirected.o board/compal/handlers.o + +highram_LDS=board/compal/highram.lds +highram_OBJS=board/compal/start.ram.o board/compal/exceptions_redirected.o board/compal/handlers.o + +# Compal E88 + +compal_e88_OBJS=$(compal_COMMON_OBJS) board/compal_e88/init.o +compal_e88_ENVIRONMENTS=$(compal_COMMON_ENVIRONMENTS) e88loader e88flash + +e88loader_LDS=board/compal_e88/loader.lds +e88loader_OBJS=board/compal/start.rom.o board/compal/header.o board/compal/exceptions_redirect.o + +e88flash_LDS=board/compal_e88/flash.lds +e88flash_OBJS=board/compal/start.rom.o board/compal/header.o board/compal/exceptions_redirected.o board/compal/handlers.o + +# Compal E99 + +compal_e99_OBJS=$(compal_COMMON_OBJS) board/compal_e99/init.o +compal_e99_ENVIRONMENTS=$(compal_COMMON_ENVIRONMENTS) + +e99loader_LDS=board/compal_e99/loader.lds +e99loader_OBJS=board/compal/header.o +e99flash_LDS=board/compal_e99/flash.lds + +# Global include path +INCLUDES=-Iinclude/ -I../../../include -I../../shared/libosmocore/include + +# Various objects that are currently linked into all applications +FLASH_OBJS=flash/cfi_flash.o +DISPLAY_OBJS=display/font_r8x8.o display/font_r8x8_horiz.o display/st7558.o display/td014.o display/ssd1783.o display/display.o +ABB_OBJS=abb/twl3025.o +RF_OBJS=rf/trf6151.o + +# Objects that go in all applications +ANY_APP_OBJS+=$(ABB_OBJS) $(RF_OBJS) $(DISPLAY_OBJS) $(FLASH_OBJS) +ANY_APP_LIBS+=calypso/libcalypso.a layer1/liblayer1.a lib/libmini.a comm/libcomm.a ../../shared/libosmocore/build-target/src/.libs/libosmocore.a + + +# Libraries are defined in subdirectories +-include calypso/Makefile +-include layer1/Makefile +-include comm/Makefile +-include lib/Makefile + +# Include rules +-include Makefile.inc + +# Uncomment this line if you want to enable Tx (Transmit) Support. +#CFLAGS += -DCONFIG_TX_ENABLE + +# Uncomment this line if you want to write to flash. +#CFLAGS += -DCONFIG_FLASH_WRITE + +# Uncomment this line if you want to write to flash, including the bootloader. +#CFLAGS += -DCONFIG_FLASH_WRITE_LOADER diff --git a/src/target/firmware/Makefile.inc b/src/target/firmware/Makefile.inc new file mode 100644 index 00000000..11c63ec8 --- /dev/null +++ b/src/target/firmware/Makefile.inc @@ -0,0 +1,197 @@ + +#### TOOLCHAIN CONFIGURATION #### + +CROSS_COMPILE?=arm-elf- + +CC=gcc +LD=ld +AR=ar +SIZE=size +OBJCOPY=objcopy + +DEBUGF=dwarf-2 + +CFLAGS=-mcpu=arm7tdmi $(INCLUDES) +CFLAGS += -Wall -Wextra -Wcast-align -Wimplicit -Wunused +CFLAGS += -Wswitch -Wredundant-decls -Wreturn-type -Wshadow -Wnested-externs +CFLAGS += -Wbad-function-cast -Wsign-compare -Waggregate-return +CFLAGS += -Os -ffunction-sections +CFLAGS += -g$(DEBUGF) + +# some older toolchains don't support this, ignore it for now +#ASFLAGS=--g$(DEBUGF) $(INCLUDES) -D__ASSEMBLY__ +ASFLAGS=$(INCLUDES) -D__ASSEMBLY__ + +LDFLAGS = -nostartfiles -nostdlib -nodefaultlibs --gc-sections --cref + +#### GIT VERSION #### + +GIT_COMMIT:=$(shell git describe --always) +GIT_MODIFIED:=$(shell (git status | grep "modified:\|added:\|deleted:" -q) && echo "-modified") + +GIT_REVISION:=$(GIT_COMMIT)$(GIT_MODIFIED) + +ASFLAGS += -DGIT_REVISION=\"$(GIT_REVISION)\" +CFLAGS += -DGIT_REVISION=\"$(GIT_REVISION)\" + +#### GLOBAL DATA #### + +ALL_OBJS= + +ALL_LSTS=$(ALL_OBJS:.o=.lst) +ALL_DEPS=$(ALL_OBJS:.o=.p) + +#### APPLICATION DATA #### + +ALL_APPS= + +ALL_APP_TARGETS=$(ALL_APPS:.elf=.bin) $(ALL_APPS:.elf=.size) $(ALL_APPS) $(ALL_APPS:.elf=.map) + +#### LIBRARY DATA #### + +ALL_LIBS= + +ALL_LIB_TARGETS=$(ALL_LIBS) + + +#### DEFAULT RULE #### + +.PHONY: default +default: all + + +#### APPLICATION RULES #### + +# template for application rules +define APPLICATION_BOARD_ENVIRONMENT_template + +# define set of objects for this binary +$(1)_$(2)_$(3)_OBJS := apps/$(1)/main.o $(ANY_APP_OBJS) $$($(2)_OBJS) +$(1)_$(2)_$(3)_LIBS := $(ANY_APP_LIBS) + +# define manifest compilation +board/$(2)/$(1).$(3).manifest.o: board/manifest.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -DAPPLICATION=\"$(1)\" -DBOARD=\"$(2)\" -DENVIRONMENT=\"$(3)\" -c -o $$@ $$< + +# generate dummy dependencies for manifest +board/$(2)/$(1).$(3).manifest.p: board/manifest.c + @touch board/$(2)/$(1).$(3).manifest.p + +# add manifest object to object list +$(1)_$(2)_$(3)_OBJS+=board/$(2)/$(1).$(3).manifest.o $$($(3)_OBJS) + +# define compilation rule, also generates map file +board/$(2)/$(1).$(3).elf board/$(2)/$(1).$(3).map: $$($(1)_$(2)_$(3)_OBJS) $$($(1)_$(2)_$(3)_LIBS) $$($(3)_LDS) + $(CROSS_COMPILE)$(LD) $(LDFLAGS) -T $$($(3)_LDS) -Bstatic \ + -Map board/$(2)/$(1).$(3).map -o board/$(2)/$(1).$(3).elf \ + --start-group $$($(1)_$(2)_$(3)_OBJS) $$($(1)_$(2)_$(3)_LIBS) --end-group + +# define size rule +board/$(2)/$(1).$(3).size: board/$(2)/$(1).$(3).elf + $(CROSS_COMPILE)$(SIZE) board/$(2)/$(1).$(3).elf | tee board/$(2)/$(1).$(3).size + +ALL_APPS+=board/$(2)/$(1).$(3).elf +ALL_OBJS+=board/$(2)/$(1).$(3).manifest.o + +endef + +define BOARD_template +ALL_OBJS+=$$($(1)_OBJS) +endef + +define BOARD_ENVIRONMENT_template +ALL_OBJS+=$$($(1)_OBJS) +endef + +define APPLICATION_template +$(1)_SRCS_REL=$$(patsubst %,$$($(1)_DIR)/%,$$($(1)_SRCS)) +$(1)_OBJS:=$$($(1)_SRCS_REL:.c=.o) +$(1)_OBJS:=$$($(1)_OBJS:.S=.o) + +ALL_OBJS+=$$($(1)_OBJS) apps/$(1)/main.o +endef + +# define rules for all defined applications +$(foreach app,$(APPLICATIONS), \ + $(foreach brd,$(BOARDS), \ + $(foreach env,$($(brd)_ENVIRONMENTS), \ + $(eval $(call APPLICATION_BOARD_ENVIRONMENT_template,$(app),$(brd),$(env)))))) + +$(foreach brd,$(BOARDS), \ + $(eval $(call BOARD_template,$(brd)) \ + $(foreach env,$($(brd)_ENVIRONMENTS), \ + $(eval $(call BOARD_ENVIRONMENT_template,$(env)))))) + +$(foreach app,$(APPLICATIONS), \ + $(eval $(call APPLICATION_template,$(app)))) + + +# add common things to global lists +ALL_OBJS+=$(ANY_APP_OBJS) + +#### LIBRARY RULES #### + +# template for library rules +define LIBRARY_template + +$(1)_SRCS_REL=$$(patsubst %,$$($(1)_DIR)/%,$$($(1)_SRCS)) +$(1)_OBJS:=$$($(1)_SRCS_REL:.c=.o) +$(1)_OBJS:=$$($(1)_OBJS:.S=.o) + +$$($(1)_DIR)/lib$(1).a: $$($(1)_OBJS) + $(CROSS_COMPILE)$(AR) cru $$($(1)_DIR)/lib$(1).a $$($(1)_OBJS) + +ALL_LIBS+=$$($(1)_DIR)/lib$(1).a + +ALL_OBJS+=$$($(1)_OBJS) + +endef + +# define rules for all defined libraries +$(foreach lbr,$(LIBRARIES),$(eval $(call LIBRARY_template,$(lbr)))) + + +#### TOPLEVEL RULES #### + +.PHONY: all +all: $(ALL_DEPS) $(ALL_APPS:.elf=.bin) $(ALL_APPS:.elf=.size) + +.PHONY: depend +depend: $(ALL_DEPS) + + +#### COMPILATION RULES #### + +%.p: %.c + @$(CROSS_COMPILE)$(CC) $(CFLAGS) -M -o $(*).d $(<) + @sed 's|.*\.o:|$(@:.p=.o): |g' < $*.d > $@; rm -f $*.d; [ -s $@ ] || rm -f $@ + +%.p: %.S + @$(CROSS_COMPILE)$(CC) $(ASFLAGS) -M -o $(*).d $(<) + @sed 's|.*\.o:|$(@:.p=.o): |g' < $*.d > $@; rm -f $*.d; [ -s $@ ] || rm -f $@ + +%.o: %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -Wa,-adhlns=$(@:.o=.lst) -c -o $@ $< + +%.o: %.S + $(CROSS_COMPILE)$(CC) $(ASFLAGS) -Wa,-adhlns=$(@:.o=.lst) -c -o $@ $< + + +%.bin: %.elf + $(CROSS_COMPILE)objcopy --gap-fill=0xff -O binary $^ $@ + + +#### CLEANUP RULES #### + +.PHONY: clean +clean: + rm -f $(ALL_APP_TARGETS) $(ALL_LIB_TARGETS) $(ALL_OBJS) $(ALL_DEPS) $(ALL_LSTS) + +.PHONY: distclean +distclean: clean + find . -name '*.o' -or -name '*.bin' -or -name '*.map' -or -name '*.lst' -or -name '*.p' -exec rm '{}' ';' + + +#### DEPENDENCY LOAD #### + +-include $(ALL_DEPS) diff --git a/src/target/firmware/abb/twl3025.c b/src/target/firmware/abb/twl3025.c new file mode 100644 index 00000000..01817130 --- /dev/null +++ b/src/target/firmware/abb/twl3025.c @@ -0,0 +1,356 @@ +/* Driver for Analog Baseband Circuit (TWL3025) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <spi.h> +#include <calypso/irq.h> +#include <calypso/tsp.h> +#include <calypso/tpu.h> +#include <abb/twl3025.h> + +/* TWL3025 */ +#define REG_PAGE(n) (n >> 7) +#define REG_ADDR(n) (n & 0x3f) + +#define TWL3025_DEV_IDX 0 /* On the SPI bus */ +#define TWL3025_TSP_DEV_IDX 0 /* On the TSP bus */ + +/* values encountered on a GTA-02 for GSM900 (the same for GSM1800!?) */ +const uint16_t twl3025_default_ramp[16] = { + ABB_RAMP_VAL( 0, 0), + ABB_RAMP_VAL( 0, 11), + ABB_RAMP_VAL( 0, 31), + ABB_RAMP_VAL( 0, 31), + ABB_RAMP_VAL( 0, 31), + ABB_RAMP_VAL( 0, 24), + ABB_RAMP_VAL( 0, 0), + ABB_RAMP_VAL( 0, 0), + ABB_RAMP_VAL( 9, 0), + ABB_RAMP_VAL(18, 0), + ABB_RAMP_VAL(25, 0), + ABB_RAMP_VAL(31, 0), + ABB_RAMP_VAL(30, 0), + ABB_RAMP_VAL(15, 0), + ABB_RAMP_VAL( 0, 0), + ABB_RAMP_VAL( 0, 0), +}; + +struct twl3025 { + uint8_t page; +}; +static struct twl3025 twl3025_state; + +/* Switch the register page of the TWL3025 */ +static void twl3025_switch_page(uint8_t page) +{ + if (page == 0) + twl3025_reg_write(PAGEREG, 1 << 0); + else + twl3025_reg_write(PAGEREG, 1 << 1); + + twl3025_state.page = page; +} + +static void handle_charger(void) +{ + uint16_t status; + printd("handle_charger();"); + + status = twl3025_reg_read(VRPCSTS); +// printd("\nvrpcsts: 0x%02x", status); + + if (status & 0x40) { + printd(" inserted\n"); + } else { + printd(" removed\n"); + } + +// twl3025_dump_madc(); +} + +static void handle_adc_done(void) +{ + printd("handle_adc_done();"); +} + +static void twl3025_irq(enum irq_nr nr) +{ + uint16_t src; + printd("twl3025_irq: 0x%02x\n",nr); + switch (nr){ + case IRQ_EXTERNAL: // charger in/out, pwrbtn, adc done + src = twl3025_reg_read(ITSTATREG); +// printd("itstatreg 0x%02x\n", src); + if (src & 0x08) + handle_charger(); + if (src & 0x20) + handle_adc_done(); + break; + case IRQ_EXTERNAL_FIQ: // vcc <2.8V emergency power off + puts("\nBROWNOUT!1!"); + twl3025_power_off(); + break; + default: + return; + } +} + +void twl3025_init(void) +{ + spi_init(); + twl3025_switch_page(0); + twl3025_clk13m(1); + twl3025_reg_write(AFCCTLADD, 0x01); /* AFCCK(1:0) must not be zero! */ + twl3025_unit_enable(TWL3025_UNIT_AFC, 1); + + irq_register_handler(IRQ_EXTERNAL, &twl3025_irq); + irq_config(IRQ_EXTERNAL, 0, 0, 0); + irq_enable(IRQ_EXTERNAL); + + irq_register_handler(IRQ_EXTERNAL_FIQ, &twl3025_irq); + irq_config(IRQ_EXTERNAL_FIQ, 1, 0, 0); + irq_enable(IRQ_EXTERNAL_FIQ); +} + +void twl3025_reg_write(uint8_t reg, uint16_t data) +{ + uint16_t tx; + + printd("tw3025_reg_write(%u,%u)=0x%04x\n", REG_PAGE(reg), + REG_ADDR(reg), data); + + if (reg != PAGEREG && REG_PAGE(reg) != twl3025_state.page) + twl3025_switch_page(REG_PAGE(reg)); + + tx = ((data & 0x3ff) << 6) | (REG_ADDR(reg) << 1); + + spi_xfer(TWL3025_DEV_IDX, 16, &tx, NULL); +} + +void twl3025_tsp_write(uint8_t data) +{ + tsp_write(TWL3025_TSP_DEV_IDX, 7, data); +} + +uint16_t twl3025_reg_read(uint8_t reg) +{ + uint16_t tx, rx; + + if (REG_PAGE(reg) != twl3025_state.page) + twl3025_switch_page(REG_PAGE(reg)); + + tx = (REG_ADDR(reg) << 1) | 1; + + /* A read cycle contains two SPI transfers */ + spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx); + delay_ms(1); + spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx); + + rx >>= 6; + + printd("tw3025_reg_read(%u,%u)=0x%04x\n", REG_PAGE(reg), + REG_ADDR(reg), rx); + + return rx; +} + +static void twl3025_wait_ibic_access(void) +{ + /* Wait 6 * 32kHz clock cycles for first IBIC access (187us + 10% = 210us) */ + delay_ms(1); +} + +void twl3025_power_off(void) +{ + twl3025_reg_write(VRPCDEV, 0x01); +} + +void twl3025_clk13m(int enable) +{ + if (enable) { + twl3025_reg_write(TOGBR2, TOGBR2_ACTS); + twl3025_wait_ibic_access(); + /* for whatever reason we need to do this twice */ + twl3025_reg_write(TOGBR2, TOGBR2_ACTS); + twl3025_wait_ibic_access(); + } else { + twl3025_reg_write(TOGBR2, TOGBR2_ACTR); + twl3025_wait_ibic_access(); + } +} + +#define TSP_DELAY 6 /* 13* Tclk6M5 = ~ 3 GSM Qbits + 3 TPU instructions */ +#define BDLON_TO_BDLCAL 6 +#define BDLCAL_DURATION 66 +#define BDLON_TO_BDLENA 7 +#define BULON_TO_BULENA 16 +#define BULON_TO_BULCAL 17 +#define BULCAL_DURATION 143 /* really that long? */ + +/* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY - BDLON_TO_BDLCAL - TSP_DELAY */ +#define DOWNLINK_DELAY (3 * TSP_DELAY + BDLCAL_DURATION + BDLON_TO_BDLCAL) + +/* Enqueue a series of TSP commands in the TPU to (de)activate the downlink path */ +void twl3025_downlink(int on, int16_t at) +{ + int16_t bdl_ena = at - TSP_DELAY - 6; + + if (on) { + if (bdl_ena < 0) + printf("BDLENA time negative (%d)\n", bdl_ena); + /* calibration should be done just before BDLENA */ + tpu_enq_at(bdl_ena - DOWNLINK_DELAY); + /* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY - BDLON_TO_BDLCAL - TSP_DELAY */ + twl3025_tsp_write(BDLON); + /* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY - BDLON_TO_BDLCAL */ + tpu_enq_wait(BDLON_TO_BDLCAL - TSP_DELAY); + /* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY */ + twl3025_tsp_write(BDLON | BDLCAL); + /* bdl_ena - TSP_DELAY - BDLCAL_DURATION */ + tpu_enq_wait(BDLCAL_DURATION - TSP_DELAY); + /* bdl_ena - TSP_DELAY */ + twl3025_tsp_write(BDLON); + //tpu_enq_wait(BDLCAL_TO_BDLENA) this is only 3.7us == 4 qbits, i.e. less than the TSP_DELAY + tpu_enq_at(bdl_ena); + twl3025_tsp_write(BDLON | BDLENA); + } else { + tpu_enq_at(bdl_ena); + twl3025_tsp_write(BDLON); + //tpu_enq_wait(nBDLENA_TO_nBDLON) this is only 3.7us == 4 qbits, i.e. less than the TSP_DELAY + twl3025_tsp_write(0); + } +} + +/* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY - BULON_TO_BULCAL - TSP_DELAY */ +#define UPLINK_DELAY (3 * TSP_DELAY + BULCAL_DURATION + BULON_TO_BULCAL + 35) + +void twl3025_uplink(int on, int16_t at) +{ + int16_t bul_ena = at - TSP_DELAY - 6; + + if (bul_ena < 0) + printf("BULENA time negative (%d)\n", bul_ena); + if (on) { + /* calibration should be done just before BULENA */ + tpu_enq_at(bul_ena - UPLINK_DELAY); + /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY - BULON_TO_BULCAL - TSP_DELAY */ + twl3025_tsp_write(BULON); + /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY - BULON_TO_BULCAL */ + tpu_enq_wait(BULON_TO_BULCAL - TSP_DELAY); + /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY */ + twl3025_tsp_write(BULON | BULCAL); + /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION */ + tpu_enq_wait(BULCAL_DURATION - TSP_DELAY); + /* bdl_ena - 35 - TSP_DELAY */ + twl3025_tsp_write(BULON); + /* bdl_ena - 35 */ + tpu_enq_wait(35); /* minimum time required to bring the ramp up (really needed?) */ + tpu_enq_at(bul_ena); + twl3025_tsp_write(BULON | BULENA); + } else { + tpu_enq_at(bul_ena); + twl3025_tsp_write(BULON); + tpu_enq_wait(35); /* minimum time required to bring the ramp down (needed!) */ + twl3025_tsp_write(0); + } +} + +void twl3025_afc_set(int16_t val) +{ + printf("twl3025_afc_set(%d)\n", val); + + if (val > 4095) + val = 4095; + else if (val <= -4096) + val = -4096; + + /* FIXME: we currently write from the USP rather than BSP */ + twl3025_reg_write(AUXAFC2, val >> 10); + twl3025_reg_write(AUXAFC1, val & 0x3ff); +} + +int16_t twl3025_afc_get(void) +{ + int16_t val; + + val = (twl3025_reg_read(AUXAFC2) & 0x7); + val = val << 10; + val = val | (twl3025_reg_read(AUXAFC1) & 0x3ff); + + if (val > 4095) + val = -(8192 - val); + return val; +} + +void twl3025_unit_enable(enum twl3025_unit unit, int on) +{ + uint16_t togbr1 = 0; + + switch (unit) { + case TWL3025_UNIT_AFC: + if (on) + togbr1 = (1 << 7); + else + togbr1 = (1 << 6); + break; + case TWL3025_UNIT_MAD: + if (on) + togbr1 = (1 << 9); + else + togbr1 = (1 << 8); + break; + case TWL3025_UNIT_ADA: + if (on) + togbr1 = (1 << 5); + else + togbr1 = (1 << 4); + case TWL3025_UNIT_VDL: + if (on) + togbr1 = (1 << 3); + else + togbr1 = (1 << 2); + break; + case TWL3025_UNIT_VUL: + if (on) + togbr1 = (1 << 1); + else + togbr1 = (1 << 0); + break; + } + twl3025_reg_write(TOGBR1, togbr1); +} + +uint8_t twl3025_afcout_get(void) +{ + return twl3025_reg_read(AFCOUT) & 0xff; +} + +void twl3025_afcout_set(uint8_t val) +{ + twl3025_reg_write(AFCCTLADD, 0x05); + twl3025_reg_write(AFCOUT, val); +} diff --git a/src/target/firmware/apps/chainload/main.c b/src/target/firmware/apps/chainload/main.c new file mode 100644 index 00000000..5121837f --- /dev/null +++ b/src/target/firmware/apps/chainload/main.c @@ -0,0 +1,53 @@ +/* Compal ramloader -> Calypso romloader Chainloading application */ + +/* (C) 2010 by Steve Markgraf <steve@steve-m.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <debug.h> +#include <memory.h> +#include <delay.h> + +#include <calypso/clock.h> + +/* Main Program */ + +static void device_enter_loader(unsigned char bootrom) { + calypso_bootrom(bootrom); + void (*entry)( void ) = (void (*)(void))0; + entry(); +} + +int main(void) +{ + /* Always disable wdt (some platforms enable it on boot) */ + wdog_enable(0); + + /* enable Calypso romloader mapping and jump there */ + delay_ms(200); + device_enter_loader(1); + + /* Not reached */ + while(1) { + } +} diff --git a/src/target/firmware/apps/compal_dsp_dump/main.c b/src/target/firmware/apps/compal_dsp_dump/main.c new file mode 100644 index 00000000..c823d0ae --- /dev/null +++ b/src/target/firmware/apps/compal_dsp_dump/main.c @@ -0,0 +1,62 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 Harald Welte <laforge@gnumonks.org> + * (C) 2010 Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <memory.h> +#include <delay.h> +#include <stdio.h> +#include <stdint.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> +#include <abb/twl3025.h> +#include <rf/trf6151.h> +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> +#include <comm/timer.h> + +/* Main Program */ +const char *hr = "======================================================================\n"; + +int main(void) +{ + board_init(); + + puts("\n\nOSMOCOM Compal DSP Dumper (revision " GIT_REVISION ")\n"); + puts(hr); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + /* Dump DSP content */ + dsp_dump(); + + while (1) { + update_timers(); + } +} + diff --git a/src/target/firmware/apps/hello_world/main.c b/src/target/firmware/apps/hello_world/main.c new file mode 100644 index 00000000..14b09b73 --- /dev/null +++ b/src/target/firmware/apps/hello_world/main.c @@ -0,0 +1,149 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <debug.h> +#include <memory.h> +#include <delay.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> +#include <abb/twl3025.h> +#include <display.h> +#include <rf/trf6151.h> +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> +#include <comm/sercomm.h> +#include <comm/timer.h> + +/* Main Program */ +const char *hr = "======================================================================\n"; + +void key_handler(enum key_codes code, enum key_states state); + +static void console_rx_cb(uint8_t dlci, struct msgb *msg) +{ + if (dlci != SC_DLCI_CONSOLE) { + printf("Message for unknown DLCI %u\n", dlci); + return; + } + + printf("Message on console DLCI: '%s'\n", msg->data); + display_puts((char *) msg->data); + msgb_free(msg); +} + +static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg) +{ + int i; + puts("l1a_l23_rx_cb: "); + for (i = 0; i < msg->len; i++) + printf("%02x ", msg->data[i]); + puts("\n"); +} + +int main(void) +{ + board_init(); + + puts("\n\nOSMOCOM Hello World (revision " GIT_REVISION ")\n"); + puts(hr); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + /* Dump clock config before PLL set */ + calypso_clk_dump(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + /* Dump all memory */ + //dump_mem(); +#if 0 + /* Dump Bootloader */ + memdump_range((void *)0x00000000, 0x2000); + puts(hr); +#endif + + display_set_attr(DISP_ATTR_INVERT); + display_puts("Hello World"); + + sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb); + sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb); + + /* beyond this point we only react to interrupts */ + puts("entering interrupt loop\n"); + while (1) { + update_timers(); + } + + twl3025_power_off(); + + while (1) {} +} + +void key_handler(enum key_codes code, enum key_states state) +{ + char test[16]; + + if (state != PRESSED) + return; + + switch (code) { + case KEY_0: + case KEY_1: + case KEY_2: + case KEY_3: + case KEY_4: + case KEY_5: + case KEY_6: + case KEY_7: + case KEY_8: + case KEY_9: + sprintf(test, "%d", code - KEY_0); + display_puts(test); + break; + case KEY_STAR: + sprintf(test, "*", 0); + display_puts(test); + break; + case KEY_HASH: + sprintf(test, "#", 0); + display_puts(test); + break; + default: + break; + } +} diff --git a/src/target/firmware/apps/l1test/main.c b/src/target/firmware/apps/l1test/main.c new file mode 100644 index 00000000..a800161a --- /dev/null +++ b/src/target/firmware/apps/l1test/main.c @@ -0,0 +1,276 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <delay.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> + +#include <abb/twl3025.h> +#include <display.h> +#include <rf/trf6151.h> + +#include <comm/sercomm.h> +#include <comm/timer.h> + +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> + +#include <layer1/sync.h> +#include <layer1/tpu_window.h> + +#define SCAN + +#ifdef SCAN +/* if scanning is enabled, scan from 0 ... 124 */ +#define BASE_ARFCN 0 +#else +/* fixed ARFCN in GSM1800 at which Harald has his GSM test license */ +#define BASE_ARFCN 871 +#endif + +/* Main Program */ +const char *hr = "======================================================================\n"; + +/* Best ARFCN MAP ************************************************************/ + +struct arfcn_map { + uint16_t arfcn; + int16_t dbm8; +}; + +static struct arfcn_map best_arfcn_map[10]; +static void best_arfcn_update(uint16_t arfcn, int16_t dbm8) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(best_arfcn_map); i++) { + if (best_arfcn_map[i].dbm8 < dbm8 || + best_arfcn_map[i].dbm8 == 0) { + best_arfcn_map[i].dbm8 = dbm8; + best_arfcn_map[i].arfcn = arfcn; + return; + } + } +} + +static void best_arfcn_dump(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(best_arfcn_map); i++) { + if (best_arfcn_map[i].dbm8 == 0) + continue; + printf("ARFCN %3d: %d dBm\n", + best_arfcn_map[i].arfcn, + best_arfcn_map[i].dbm8/8); + } +} + + +/* MAIN program **************************************************************/ + +enum l1test_state { + STATE_NONE, + STATE_PM, + STATE_FB, +}; + +static void l1test_state_change(enum l1test_state new_state) +{ + switch (new_state) { + case STATE_PM: + puts("Performing power measurement over GSM900\n"); + l1s_pm_test(1, BASE_ARFCN); + break; + case STATE_FB: + puts("Starting FCCH Recognition\n"); + l1s_fb_test(1, 0); + break; + case STATE_NONE: + /* disable frame interrupts */ + tpu_frame_irq_en(0, 0); + break; + } +} + +/* completion call-back for the L1 Sync Pwer Measurement */ +static void l1s_signal_cb(struct l1_signal *sig) +{ + uint16_t i, next_arfcn; + + switch (sig->signum) { + case L1_SIG_PM: + best_arfcn_update(sig->arfcn, sig->pm.dbm8[0]); + next_arfcn = sig->arfcn + 1; + + if (next_arfcn >= 124) { + puts("ARFCN Top 10 Rx Level\n"); + best_arfcn_dump(); + + trf6151_rx_window(0, best_arfcn_map[0].arfcn, 40, 0); + tpu_end_scenario(); + + /* PM phase completed, do FB det */ + l1test_state_change(STATE_FB); + + break; + } + + /* restart Power Measurement */ + l1s_pm_test(1, next_arfcn); + break; + case L1_SIG_NB: + puts("NB SNR "); + for (i = 0; i < 4; i++) { + uint16_t snr = sig->nb.meas[i].snr; + printf("%d.%03u ", l1s_snr_int(snr), l1s_snr_fract(snr)); + } + putchar('\n'); + printf("--> Frame %d %d 0x%04X ", sig->nb.fire, sig->nb.crc, sig->nb.num_biterr); + for (i = 0; i < ARRAY_SIZE(sig->nb.frame); i++) + printf("%02X ", sig->nb.frame[i]); + putchar('\n'); + break; + } +} + +static void key_handler(enum key_codes code, enum key_states state); + +int main(void) +{ + board_init(); + + puts("\n\nHello World from " __FILE__ " program code\n"); + + puts(hr); + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + display_set_attr(DISP_ATTR_INVERT); + display_puts("l1test.bin"); + + layer1_init(); + l1s_set_handler(&l1s_signal_cb); + + //dsp_checksum_task(); +#ifdef SCAN + l1test_state_change(STATE_PM); +#else + l1test_state_change(STATE_FB); +#endif + tpu_frame_irq_en(1, 1); + + while (1) { + update_timers(); + } + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static int8_t vga_gain = 40; +static int high_gain = 0; +static int afcout = 0; + +static void update_vga_gain(void) +{ + printf("VGA Gain: %u %s\n", vga_gain, high_gain ? "HIGH" : "LOW"); + trf6151_set_gain(vga_gain, high_gain); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void tspact_toggle(uint8_t num) +{ + printf("TSPACT%u toggle\n", num); + tsp_act_toggle((1 << num)); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + case KEY_1: /* VGA gain decrement */ + vga_gain -= 2; + if (vga_gain < 14) + vga_gain = 14; + update_vga_gain(); + break; + case KEY_2: /* High/Low Rx gain */ + high_gain ^= 1; + update_vga_gain(); + break; + case KEY_3: /* VGA gain increment */ + vga_gain += 2; + if (vga_gain > 40) + vga_gain = 40; + update_vga_gain(); + break; + case KEY_4: + tspact_toggle(6); /* TRENA (RFFE) */ + break; + case KEY_5: + tspact_toggle(8); /* GSM_TXEN (RFFE) */ + break; + case KEY_6: + tspact_toggle(1); /* PAENA (RFFE) */ + break; + case KEY_7: /* decrement AFC OUT */ + afcout -= 100; + if (afcout < -4096) + afcout = -4096; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + case KEY_9: /* increase AFC OUT */ + afcout += 100; + if (afcout > 4095) + afcout = 4095; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + default: + break; + } +} diff --git a/src/target/firmware/apps/layer1/main.c b/src/target/firmware/apps/layer1/main.c new file mode 100644 index 00000000..393f69e2 --- /dev/null +++ b/src/target/firmware/apps/layer1/main.c @@ -0,0 +1,161 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <delay.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> + +#include <abb/twl3025.h> +#include <display.h> +#include <rf/trf6151.h> + +#include <comm/sercomm.h> +#include <comm/timer.h> + +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> + +#include <layer1/sync.h> +#include <layer1/tpu_window.h> + +const char *hr = "======================================================================\n"; + +/* MAIN program **************************************************************/ + +static void key_handler(enum key_codes code, enum key_states state); + +int main(void) +{ + board_init(); + + puts("\n\nOSMOCOM Layer 1 (revision " GIT_REVISION ")\n"); + puts(hr); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + display_set_attr(DISP_ATTR_INVERT); + display_puts("layer1.bin"); + + layer1_init(); + + tpu_frame_irq_en(1, 1); + + while (1) { + l1a_compl_execute(); + update_timers(); + } + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static int8_t vga_gain = 40; +static int high_gain = 0; +static int afcout = 0; + +static void update_vga_gain(void) +{ + printf("VGA Gain: %u %s\n", vga_gain, high_gain ? "HIGH" : "LOW"); + trf6151_set_gain(vga_gain, high_gain); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void tspact_toggle(uint8_t num) +{ + printf("TSPACT%u toggle\n", num); + tsp_act_toggle((1 << num)); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + case KEY_1: /* VGA gain decrement */ + vga_gain -= 2; + if (vga_gain < 14) + vga_gain = 14; + update_vga_gain(); + break; + case KEY_2: /* High/Low Rx gain */ + high_gain ^= 1; + update_vga_gain(); + break; + case KEY_3: /* VGA gain increment */ + vga_gain += 2; + if (vga_gain > 40) + vga_gain = 40; + update_vga_gain(); + break; + case KEY_4: + tspact_toggle(6); /* TRENA (RFFE) */ + break; + case KEY_5: + tspact_toggle(8); /* GSM_TXEN (RFFE) */ + break; + case KEY_6: + tspact_toggle(1); /* PAENA (RFFE) */ + break; + case KEY_7: /* decrement AFC OUT */ + afcout -= 100; + if (afcout < -4096) + afcout = -4096; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + case KEY_9: /* increase AFC OUT */ + afcout += 100; + if (afcout > 4095) + afcout = 4095; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + default: + break; + } +} + + diff --git a/src/target/firmware/apps/loader/main.c b/src/target/firmware/apps/loader/main.c new file mode 100644 index 00000000..fd10b192 --- /dev/null +++ b/src/target/firmware/apps/loader/main.c @@ -0,0 +1,441 @@ +/* boot loader for Calypso phones */ + +/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <debug.h> +#include <memory.h> +#include <delay.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> +#include <console.h> +#include <manifest.h> + +#include <osmocore/crc16.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> + +#include <comm/sercomm.h> + +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> +#include <calypso/uart.h> +#include <calypso/timer.h> + +#include <flash/cfi_flash.h> + +#include "protocol.h" + +/* Main Program */ +const char *hr = + "======================================================================\n"; + +static void key_handler(enum key_codes code, enum key_states state); +static void cmd_handler(uint8_t dlci, struct msgb *msg); + +int flag = 0; + +static void flush_uart(void) +{ + unsigned i; + for (i = 0; i < 500; i++) { + uart_poll(SERCOMM_UART_NR); + delay_ms(1); + } +} + +static void device_poweroff(void) +{ + flush_uart(); + twl3025_power_off(); +} + +static void device_reset(void) +{ + flush_uart(); + wdog_reset(); +} + +static void device_enter_loader(unsigned char bootrom) +{ + flush_uart(); + + calypso_bootrom(bootrom); + void (*entry) (void) = (void (*)(void))0; + entry(); +} + +static void device_jump(void *entry) +{ + flush_uart(); + + void (*f) (void) = (void (*)(void))entry; + f(); +} + +static void loader_send_simple(struct msgb *msg, uint8_t dlci, uint8_t command) +{ + msgb_put_u8(msg, command); + sercomm_sendmsg(dlci, msg); +} + +extern unsigned char _start; + +static void loader_send_init(uint8_t dlci) +{ + struct msgb *msg = sercomm_alloc_msgb(9); + msgb_put_u8(msg, LOADER_INIT); + msgb_put_u32(msg, 0); + msgb_put_u32(msg, &_start); + sercomm_sendmsg(dlci, msg); +} + +flash_t the_flash; + +extern void putchar_asm(uint32_t c); + +static const uint8_t phone_ack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x42 }; + +int main(void) +{ + /* Simulate a compal loader saying "ACK" */ + int i = 0; + for (i = 0; i < sizeof(phone_ack); i++) { + putchar_asm(phone_ack[i]); + } + + /* Always disable wdt (some platforms enable it on boot) */ + wdog_enable(0); + + /* Initialize TWL3025 for power control */ + twl3025_init(); + + /* Backlight */ + bl_mode_pwl(1); + bl_level(50); + + /* Initialize UART without interrupts */ + uart_init(SERCOMM_UART_NR, 0); + uart_baudrate(SERCOMM_UART_NR, UART_115200); + + /* Initialize HDLC subsystem */ + sercomm_init(); + + /* Say hi */ + puts("\n\nOSMOCOM Loader (revision " GIT_REVISION ")\n"); + puts(hr); + + /* Identify environment */ + printf("Running on %s in environment %s\n", manifest_board, + manifest_environment); + + /* Initialize flash driver */ + if (flash_init(&the_flash, 0)) { + puts("Failed to initialize flash!\n"); + } else { + printf("Found flash of %d bytes at 0x%x with %d regions\n", + the_flash.f_size, the_flash.f_base, + the_flash.f_nregions); + + int i; + for (i = 0; i < the_flash.f_nregions; i++) { + printf(" Region %d of %d pages with %d bytes each.\n", + i, + the_flash.f_regions[i].fr_bnum, + the_flash.f_regions[i].fr_bsize); + } + + } + + /* Set up a key handler for powering off */ + keypad_set_handler(&key_handler); + + /* Set up loader communications */ + sercomm_register_rx_cb(SC_DLCI_LOADER, &cmd_handler); + + /* Notify any running osmoload about our startup */ + loader_send_init(SC_DLCI_LOADER); + + /* Wait for events */ + while (1) { + keypad_poll(); + uart_poll(SERCOMM_UART_NR); + } + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static void cmd_handler(uint8_t dlci, struct msgb *msg) +{ + if (msg->data_len < 1) { + return; + } + + uint8_t command = msgb_get_u8(msg); + + int res; + + flash_lock_t lock; + + void *data; + + uint8_t chip; + uint8_t nbytes; + uint16_t crc, mycrc; + uint32_t address; + + struct msgb *reply = sercomm_alloc_msgb(256); // XXX + + if (!reply) { + printf("Failed to allocate reply buffer!\n"); + goto out; + } + + switch (command) { + + case LOADER_PING: + loader_send_simple(reply, dlci, LOADER_PING); + break; + + case LOADER_RESET: + loader_send_simple(reply, dlci, LOADER_RESET); + device_reset(); + break; + + case LOADER_POWEROFF: + loader_send_simple(reply, dlci, LOADER_POWEROFF); + device_poweroff(); + break; + + case LOADER_ENTER_ROM_LOADER: + loader_send_simple(reply, dlci, LOADER_ENTER_ROM_LOADER); + device_enter_loader(1); + break; + + case LOADER_ENTER_FLASH_LOADER: + loader_send_simple(reply, dlci, LOADER_ENTER_FLASH_LOADER); + device_enter_loader(0); + break; + + case LOADER_MEM_READ: + + nbytes = msgb_get_u8(msg); + address = msgb_get_u32(msg); + + crc = crc16(0, (void *)address, nbytes); + + msgb_put_u8(reply, LOADER_MEM_READ); + msgb_put_u8(reply, nbytes); + msgb_put_u16(reply, crc); + msgb_put_u32(reply, address); + + memcpy(msgb_put(reply, nbytes), (void *)address, nbytes); + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_MEM_WRITE: + + nbytes = msgb_get_u8(msg); + crc = msgb_get_u16(msg); + address = msgb_get_u32(msg); + + data = msgb_get(msg, nbytes); + + mycrc = crc16(0, data, nbytes); + + if (mycrc == crc) { + memcpy((void *)address, data, nbytes); + } + + msgb_put_u8(reply, LOADER_MEM_WRITE); + msgb_put_u8(reply, nbytes); + msgb_put_u16(reply, mycrc); + msgb_put_u32(reply, address); + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_JUMP: + + address = msgb_get_u32(msg); + + msgb_put_u8(reply, LOADER_JUMP); + msgb_put_u32(reply, address); + + sercomm_sendmsg(dlci, reply); + + device_jump((void *)address); + + break; + + case LOADER_FLASH_INFO: + + msgb_put_u8(reply, LOADER_FLASH_INFO); + msgb_put_u8(reply, 1); // nchips + + // chip 1 + msgb_put_u32(reply, the_flash.f_base); + msgb_put_u32(reply, the_flash.f_size); + msgb_put_u8(reply, the_flash.f_nregions); + + int i; + for (i = 0; i < the_flash.f_nregions; i++) { + msgb_put_u32(reply, the_flash.f_regions[i].fr_bnum); + msgb_put_u32(reply, the_flash.f_regions[i].fr_bsize); + } + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_FLASH_ERASE: + case LOADER_FLASH_UNLOCK: + case LOADER_FLASH_LOCK: + case LOADER_FLASH_LOCKDOWN: + + chip = msgb_get_u8(msg); + address = msgb_get_u32(msg); + + if (command == LOADER_FLASH_ERASE) { + res = flash_block_erase(&the_flash, address); + } + if (command == LOADER_FLASH_UNLOCK) { + res = flash_block_unlock(&the_flash, address); + } + if (command == LOADER_FLASH_LOCK) { + res = flash_block_lock(&the_flash, address); + } + if (command == LOADER_FLASH_LOCKDOWN) { + res = flash_block_lockdown(&the_flash, address); + } + + msgb_put_u8(reply, command); + msgb_put_u8(reply, chip); + msgb_put_u32(reply, address); + msgb_put_u32(reply, (res != 0)); + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_FLASH_GETLOCK: + + chip = msgb_get_u8(msg); + address = msgb_get_u32(msg); + + lock = flash_block_getlock(&the_flash, address); + + msgb_put_u8(reply, command); + msgb_put_u8(reply, chip); + msgb_put_u32(reply, address); + + switch (lock) { + case FLASH_UNLOCKED: + msgb_put_u32(reply, LOADER_FLASH_UNLOCKED); + break; + case FLASH_LOCKED: + msgb_put_u32(reply, LOADER_FLASH_LOCKED); + break; + case FLASH_LOCKED_DOWN: + msgb_put_u32(reply, LOADER_FLASH_LOCKED_DOWN); + break; + default: + msgb_put_u32(reply, 0xFFFFFFFF); + break; + } + + sercomm_sendmsg(dlci, reply); + + break; + + case LOADER_FLASH_PROGRAM: + + nbytes = msgb_get_u8(msg); + crc = msgb_get_u16(msg); + msgb_get_u8(msg); // XXX align + chip = msgb_get_u8(msg); + address = msgb_get_u32(msg); + + data = msgb_get(msg, nbytes); + + mycrc = crc16(0, data, nbytes); + + if (mycrc == crc) { + res = flash_program(&the_flash, address, data, nbytes); + } + + msgb_put_u8(reply, LOADER_FLASH_PROGRAM); + msgb_put_u8(reply, nbytes); + msgb_put_u16(reply, mycrc); + msgb_put_u8(reply, 0); // XXX align + msgb_put_u8(reply, chip); + msgb_put_u32(reply, address); + + msgb_put_u32(reply, (uint32_t) res); // XXX + + sercomm_sendmsg(dlci, reply); + + break; + + default: + printf("unknown command %d\n", command); + + msgb_free(reply); + + break; + } + + out: + + msgb_free(msg); +} + +static void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + case KEY_POWER: + puts("Powering off due to keypress.\n"); + device_poweroff(); + break; + case KEY_OK: + puts("Resetting due to keypress.\n"); + device_reset(); + break; + default: + break; + } +} diff --git a/src/target/firmware/apps/loader/protocol.h b/src/target/firmware/apps/loader/protocol.h new file mode 100644 index 00000000..0a61c89e --- /dev/null +++ b/src/target/firmware/apps/loader/protocol.h @@ -0,0 +1,37 @@ + +enum loader_command { + /* init message from loader */ + LOADER_INIT, + + /* ping / pong */ + LOADER_PING, + + /* lifecycle requests */ + LOADER_RESET, + LOADER_POWEROFF, + + /* jumps */ + LOADER_JUMP, + LOADER_ENTER_ROM_LOADER, + LOADER_ENTER_FLASH_LOADER, + + /* generic memory ops */ + LOADER_MEM_READ, + LOADER_MEM_WRITE, + + /* flash operations */ + LOADER_FLASH_INFO, + LOADER_FLASH_ERASE, + LOADER_FLASH_UNLOCK, + LOADER_FLASH_LOCK, + LOADER_FLASH_LOCKDOWN, + LOADER_FLASH_GETLOCK, + LOADER_FLASH_PROGRAM, + +}; + +enum loader_flash_lock { + LOADER_FLASH_UNLOCKED = 0, + LOADER_FLASH_LOCKED, + LOADER_FLASH_LOCKED_DOWN, +}; diff --git a/src/target/firmware/apps/simtest/main.c b/src/target/firmware/apps/simtest/main.c new file mode 100755 index 00000000..f3fa89f3 --- /dev/null +++ b/src/target/firmware/apps/simtest/main.c @@ -0,0 +1,328 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <debug.h> +#include <memory.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> +#include <abb/twl3025.h> +#include <display.h> +#include <rf/trf6151.h> +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> +#include <comm/sercomm.h> +#include <comm/timer.h> + + +#include <calypso/sim.h> + + +#define DEBUG + +/* Dump bytes in hex on the console */ +static void myHexdump(uint8_t *data, int len) +{ + int i; + + for(i=0;i<len;i++) + printf("%x ",data[i]); + + printf("(%i bytes)\n", len); + + return; +} + +/* SIM instructions + All instructions a standard sim card must feature: */ +#define SIM_CLASS 0xA0 /* Class that contains the following instructions */ +#define SIM_SELECT 0xA4 /* Select a file on the card */ +#define SIM_STATUS 0xF2 /* Get the status of the currently selected file */ +#define SIM_READ_BINARY 0xB0 /* Read file in binary mode */ +#define SIM_UPDATE_BINARY 0xD6 /* Write file in binary mode */ +#define SIM_READ_RECORD 0xB2 /* Read record of a record based file */ +#define SIM_UPDATE_RECORD 0xDC /* Write record of a record based file */ +#define SIM_SEEK 0xA2 /* Seek in a record based file */ +#define SIM_INCREASE 0x32 /* Increase a record in a record based file */ +#define SIM_VERIFY_CHV 0x20 /* Authenicate with card (enter pin) */ +#define SIM_CHANGE_CHV 0x24 /* Change pin */ +#define SIM_DISABLE_CHV 0x26 /* Disable pin so that no authentication is needed anymore */ +#define SIM_ENABLE_CHV 0x28 /* Enable pin, authentication is now needed again */ +#define SIM_UNBLOCK_CHV 0x2C /* Unblock pin when it is blocked by entering a wrong pin three times */ +#define SIM_INVALIDATE 0x04 /* Invalidate the current elementry file (file in a subdirectory) */ +#define SIM_REHABILITATE 0x44 /* Rehabilitate the current elementry file (file in a subdirectory) */ +#define SIM_RUN_GSM_ALGORITHM 0x88 /* Run the GSM A3 authentication algorithm in the card */ +#define SIM_SLEEP 0xFA /* Sleep command (only used in Phase 1 GSM) */ +#define SIM_GET_RESPONSE 0xC0 /* Get the response of a command from the card */ + +/* File identifiers (filenames) + The file identifiers are the standartized file identfiers mentiond in the + GSM-11-11 specification. */ +#define SIM_MF 0x3F00 +#define SIM_EF_ICCID 0x2FE2 +#define SIM_DF_TELECOM 0x7F10 +#define SIM_EF_ADN 0x6F3A +#define SIM_EF_FDN 0x6F3B +#define SIM_EF_SMS 0x6F3C +#define SIM_EF_CCP 0x6F3D +#define SIM_EF_MSISDN 0x6F40 +#define SIM_EF_SMSP 0x6F42 +#define SIM_EF_SMSS 0x6F43 +#define SIM_EF_LND 0x6F44 +#define SIM_EF_EXT1 0x6F4A +#define SIM_EF_EXT2 0x6F4B +#define SIM_DF_GSM 0x7F20 +#define SIM_EF_LP 0x6F05 +#define SIM_EF_IMSI 0x6F07 +#define SIM_EF_KC 0x6F20 +#define SIM_EF_PLMNsel 0x6F30 +#define SIM_EF_HPLMN 0x6F31 +#define SIM_EF_ACMmax 0x6F37 +#define SIM_EF_SST 0x6F38 +#define SIM_EF_ACM 0x6F39 +#define SIM_EF_GID1 0x6F3E +#define SIM_EF_GID2 0x6F3F +#define SIM_EF_PUCT 0x6F41 +#define SIM_EF_CBMI 0x6F45 +#define SIM_EF_SPN 0x6F46 +#define SIM_EF_BCCH 0x6F74 +#define SIM_EF_ACC 0x6F78 +#define SIM_EF_FPLMN 0x6F7B +#define SIM_EF_LOCI 0x6F7E +#define SIM_EF_AD 0x6FAD +#define SIM_EF_PHASE 0x6FAE + +/* Select a file on the card */ +uint16_t sim_select(uint16_t fid) +{ + uint8_t txBuffer[2]; + uint8_t status_word[2]; + + txBuffer[1] = (uint8_t) fid; + txBuffer[0] = (uint8_t) (fid >> 8); + + if(calypso_sim_transceive(SIM_CLASS, SIM_SELECT, 0x00, 0x00, 0x02,txBuffer,status_word, SIM_APDU_PUT) != 0) + return 0xFFFF; + + return (status_word[0] << 8) | status_word[1]; +} + +/* Get the status of the currently selected file */ +uint16_t sim_status(void) +{ + uint8_t status_word[2]; + + if(calypso_sim_transceive(SIM_CLASS, SIM_STATUS, 0x00, 0x00, 0x00,0,status_word, SIM_APDU_PUT) != 0) + return 0xFFFF; + + return (status_word[0] << 8) | status_word[1]; +} + +/* Read file in binary mode */ +uint16_t sim_readbinary(uint8_t offset_high, uint8_t offset_low, uint8_t length, uint8_t *data) +{ + uint8_t status_word[2]; + if(calypso_sim_transceive(SIM_CLASS, SIM_READ_BINARY, offset_high, offset_low, length, data ,status_word, SIM_APDU_GET) != 0) + return 0xFFFF; + + return (status_word[0] << 8) | status_word[1]; +} + + + + + +/* FIXME: We need proper calibrated delay loops at some point! */ +void delay_us(unsigned int us) +{ + volatile unsigned int i; + + for (i= 0; i < us*4; i++) { i; } +} + +void delay_ms(unsigned int ms) +{ + volatile unsigned int i; + for (i= 0; i < ms*1300; i++) { i; } +} + + + + +/* Execute my (dexter's) personal test */ +void do_sim_test(void) +{ + uint8_t testBuffer[20]; + uint8_t testtxBuffer[20]; + + uint8_t testDataBody[257]; + uint8_t testStatusWord[2]; + int recivedChars; + int i; + + uint8_t atr[20]; + uint8_t atrLength = 0; + + memset(atr,0,sizeof(atr)); + + + + uint8_t buffer[20]; + + + memset(testtxBuffer,0,sizeof(testtxBuffer)); + + puts("----------------SIMTEST----8<-----------------\n"); + + /* Initalize Sim-Controller driver */ + puts("Initalizing driver:\n"); + calypso_sim_init(); + + /* Power up sim and display ATR */ + puts("Power up simcard:\n"); + memset(atr,0,sizeof(atr)); + atrLength = calypso_sim_powerup(atr); + myHexdump(atr,atrLength); + + /* Reset sim and display ATR */ + puts("Reset simcard:\n"); + memset(atr,0,sizeof(atr)); + atrLength = calypso_sim_reset(atr); + myHexdump(atr,atrLength); + + + + testDataBody[0] = 0x3F; + testDataBody[1] = 0x00; + calypso_sim_transceive(0xA0, 0xA4, 0x00, 0x00, 0x02, testDataBody,0, SIM_APDU_PUT); + calypso_sim_transceive(0xA0, 0xC0, 0x00, 0x00, 0x0f, testDataBody,0, SIM_APDU_GET); + myHexdump(testDataBody,0x0F); + + puts("Test Phase 1: Testing bare sim commands...\n"); + + puts(" * Testing SELECT: Selecting MF\n"); + printf(" ==> Status word: %x\n", sim_select(SIM_MF)); + + puts(" * Testing SELECT: Selecting DF_GSM\n"); + printf(" ==> Status word: %x\n", sim_select(SIM_DF_GSM)); + + puts(" * Testing SELECT: Selecting EF_IMSI\n"); + printf(" ==> Status word: %x\n", sim_select(SIM_EF_IMSI)); + + puts(" * Testing STATUS:\n"); + printf(" ==> Status word: %x\n", sim_status()); + + memset(buffer,0,sizeof(buffer)); + puts(" * Testing READ BINARY:\n"); + printf(" ==> Status word: %x\n", sim_readbinary(0,0,9,buffer)); + printf(" Data: "); + myHexdump(buffer,9); + + delay_ms(5000); + + calypso_sim_powerdown(); + + puts("------------END SIMTEST----8<-----------------\n"); +} + + + + +/* Main Program */ +const char *hr = "======================================================================\n"; + +void key_handler(enum key_codes code, enum key_states state); + +static void *console_rx_cb(uint8_t dlci, struct msgb *msg) +{ + if (dlci != SC_DLCI_CONSOLE) { + printf("Message for unknown DLCI %u\n", dlci); + return; + } + + printf("Message on console DLCI: '%s'\n", msg->data); + msgb_free(msg); +} + +int main(void) +{ + board_init(); + + puts("\n\nOSMOCOM SIM Test (revision " GIT_REVISION ")\n"); + puts(hr); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + /* Dump clock config before PLL set */ + calypso_clk_dump(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + /* Dump all memory */ + //dump_mem(); +#if 0 + /* Dump Bootloader */ + memdump_range((void *)0x00000000, 0x2000); + puts(hr); +#endif + + display_set_attr(DISP_ATTR_INVERT); + display_puts("SIM-TEST"); + + sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb); + + do_sim_test(); + + /* beyond this point we only react to interrupts */ + puts("entering interrupt loop\n"); + while (1) { + } + + twl3025_power_off(); + while (1) {} +} + +void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + default: + break; + } +} diff --git a/src/target/firmware/board/common/calypso_pwl.S b/src/target/firmware/board/common/calypso_pwl.S new file mode 100644 index 00000000..90e29bff --- /dev/null +++ b/src/target/firmware/board/common/calypso_pwl.S @@ -0,0 +1,21 @@ + +/* Calypso PWL driver */ + +#define ASIC_CONF_REG 0xfffef008 +#define BA_PWL 0xfffe8000 + +.globl pwl_init +pwl_init: ldr r1, =ASIC_CONF_REG + ldr r2, [r1] + orr r2, r2, #0x10 @ set light output to PWL + str r2, [r1] + ldr r1, =BA_PWL + mov r0, #1 + strb r0, [r1, #1] @ enable clock of PWL unut + mov pc, lr + +.globl pwl_set_level +pwl_set_level: ldr r1, =BA_PWL + strb r0, [r1] + mov pc, lr + diff --git a/src/target/firmware/board/common/calypso_uart.S b/src/target/firmware/board/common/calypso_uart.S new file mode 100644 index 00000000..808cb051 --- /dev/null +++ b/src/target/firmware/board/common/calypso_uart.S @@ -0,0 +1,92 @@ +/* uart routines for early assembly code */ + +#define BA_UART_MODEM 0xFFFF5800 + +.macro senduart, rd, rx + strb \rd, [\rx, #0] +.endm + +.macro busyuart, rd, rx +1001: + @busy waiting until THR is empty + ldrb \rd, [\rx, #5] @ read LSR register + mov \rd, \rd, lsr #6 + tst \rd, #1 + beq 1001b +.endm + +.macro loadsp, rd + ldr \rd, =BA_UART_MODEM +.endm + +.section .text + + .align 2 + .type phexbuf, #object +phexbuf: .space 12 + .size phexubf, . - phexbuf + +.globl phex +phex: adr r3, phexbuf + mov r2, #0 + strb r2, [r3, r1] +1: subs r1, r1, #1 + movmi r0, r3 + bmi puts_asm + and r2, r0, #15 + mov r0, r0, lsr #4 + cmp r2, #10 + addge r2, r2, #7 + add r2, r2, #'0' + strb r2, [r3, r1] + b 1b + +.globl puts_asm +puts_asm: loadsp r3 +1: ldrb r2, [r0], #1 + teq r2, #0 + moveq pc, lr +2: senduart r2, r3 + busyuart r1, r3 + teq r2, #'\n' + moveq r2, #'\r' + beq 2b + teq r0, #0 + bne 1b + mov pc, lr + +.globl putchar_asm +putchar_asm: + mov r2, r0 + mov r0, #0 + loadsp r3 + b 2b + +.globl memdump_asm +memdump_asm: mov r12, r0 + mov r10, lr + mov r11, #0 +2: mov r0, r11, lsl #2 + add r0, r0, r12 + mov r1, #8 + bl phex + mov r0, #':' + bl putchar_asm +1: mov r0, #' ' + bl putchar_asm + ldr r0, [r12, r11, lsl #2] + mov r1, #8 + bl phex + and r0, r11, #7 + teq r0, #3 + moveq r0, #' ' + bleq putchar_asm + and r0, r11, #7 + add r11, r11, #1 + teq r0, #7 + bne 1b + mov r0, #'\n' + bl putchar_asm + cmp r11, #64 + blt 2b + mov pc, r10 diff --git a/src/target/firmware/board/common/rffe_gta0x_triband.c b/src/target/firmware/board/common/rffe_gta0x_triband.c new file mode 100644 index 00000000..a21cc612 --- /dev/null +++ b/src/target/firmware/board/common/rffe_gta0x_triband.c @@ -0,0 +1,96 @@ +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <rffe.h> +#include <calypso/tsp.h> +#include <rf/trf6151.h> + +/* This is a value that has been measured on the C123 by Harald: 71dBm, + it is the difference between the input level at the antenna and what + the DSP reports, subtracted by the total gain of the TRF6151 */ +#define SYSTEM_INHERENT_GAIN 71 + +/* describe how the RF frontend is wired on the Openmoko GTA0x boards */ + +#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151 */ +#define PA_ENABLE TSPACT(9) /* Enable the Power Amplifier */ +#define GSM_TXEN TSPACT(3) /* PA GSM switch, low-active */ + +/* All VCn controls are low-active */ +#define ASM_VC1 TSPACT(2) /* Antenna switch VC1 */ +#define ASM_VC2 TSPACT(1) /* Antenna switch VC2 */ +#define ASM_VC3 TSPACT(4) /* Antenna switch VC3 */ + +#define IOTA_STROBE TSPEN0 /* Strobe for the Iota TSP */ +#define RITA_STROBE TSPEN2 /* Strobe for the Rita TSP */ + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx) +{ + uint16_t tspact = tsp_act_state(); + + /* First we mask off all bits from the state cache */ + tspact &= ~PA_ENABLE; + tspact |= GSM_TXEN; /* low-active */ + tspact |= ASM_VC1 | ASM_VC2 | ASM_VC3; /* low-active */ + + switch (band) { + case GSM_BAND_850: + case GSM_BAND_900: + case GSM_BAND_1800: + break; + case GSM_BAND_1900: + tspact &= ~ASM_VC2; + break; + default: + /* TODO return/signal error here */ + break; + } + +#ifdef CONFIG_TX_ENABLE + /* Then we selectively set the bits on, if required */ + if (tx) { + // TODO: Implement tx + } +#endif /* TRANSMIT_SUPPORT */ + + tsp_act_update(tspact); +} + +#define MCU_SW_TRACE 0xfffef00e +#define ARM_CONF_REG 0xfffef006 + +void rffe_init(void) +{ + uint16_t reg; + + reg = readw(ARM_CONF_REG); + reg &= ~ (1 << 7); /* TSPACT4 I/O function, not nRDYMEM */ + writew(reg, ARM_CONF_REG); + + reg = readw(MCU_SW_TRACE); + reg &= ~(1 << 1); /* TSPACT9 I/O function, not MAS(1) */ + writew(reg, MCU_SW_TRACE); +} + +uint8_t rffe_get_gain(void) +{ + return trf6151_get_gain(); +} + +const uint8_t system_inherent_gain = SYSTEM_INHERENT_GAIN; + +#define to_dbm8(x) ((x)*8) +/* Given the expected input level of exp_inp dBm/8 and the target of target_bb + * dBm8, configure the RF Frontend with the respective gain */ +void rffe_set_gain(int16_t exp_inp, int16_t target_bb) +{ + trf6151_compute_gain(exp_inp, target_bb); +} + +void rffe_rx_win_ctrl(int16_t exp_inp, int16_t target_bb) +{ + /* FIXME */ +} diff --git a/src/target/firmware/board/compal/LINKAGE.txt b/src/target/firmware/board/compal/LINKAGE.txt new file mode 100644 index 00000000..1ae06fb3 --- /dev/null +++ b/src/target/firmware/board/compal/LINKAGE.txt @@ -0,0 +1,12 @@ + +We provide the following common RAM linkages for all Compal phones: + +(both use the Calypso ROM loader for interrupt redirect, if required) + + compalram: + Image for the Compal ramloader. Starts at a weird address and + contains various ramloader specifics. + + highram: + Image linked to 0x820000, used for various special purposes. + This image is completely independent of the compal loader. diff --git a/src/target/firmware/board/compal/exceptions_redirect.S b/src/target/firmware/board/compal/exceptions_redirect.S new file mode 100644 index 00000000..a216e601 --- /dev/null +++ b/src/target/firmware/board/compal/exceptions_redirect.S @@ -0,0 +1,24 @@ + +.section .text.exceptions +_undef_instr: + ldr pc, _vec_undef_instr +_sw_interr: + ldr pc, _vec_sw_interr +_prefetch_abort: + ldr pc, _vec_prefetch_abort +_data_abort: + ldr pc, _vec_data_abort +_reserved: + ldr pc, _vec_reserved +_irq: + ldr pc, _vec_irq +_fiq: + ldr pc, _vec_fiq + +_vec_undef_instr: .word(0x80001c) +_vec_sw_interr: .word(0x800020) +_vec_prefetch_abort: .word(0x800024) +_vec_data_abort: .word(0x800028) +_vec_reserved: .word(0x80002c) +_vec_irq: .word(0x800030) +_vec_fiq: .word(0x800034) diff --git a/src/target/firmware/board/compal/exceptions_redirected.S b/src/target/firmware/board/compal/exceptions_redirected.S new file mode 100644 index 00000000..69083962 --- /dev/null +++ b/src/target/firmware/board/compal/exceptions_redirected.S @@ -0,0 +1,20 @@ + +/* Exception Vectors like they are needed for the exception vector + indirection of the internal boot ROM. The following section must be liked + to appear at 0x80001c */ +.section .text.exceptions +_undef_instr: + b handle_abort +_sw_interr: + b _sw_interr +_prefetch_abort: + b handle_abort +_data_abort: + b handle_abort +_reserved: + b _reserved +_irq: + b irq_entry +_fiq: + b fiq_entry + diff --git a/src/target/firmware/board/compal/handlers.S b/src/target/firmware/board/compal/handlers.S new file mode 100644 index 00000000..ef044e3f --- /dev/null +++ b/src/target/firmware/board/compal/handlers.S @@ -0,0 +1,79 @@ + + .EQU I_BIT, 0x80 + .EQU F_BIT, 0x40 + +.section .text + +/* handler for all kinds of aborts */ +.global handle_abort +handle_abort: + @ print the PC we would jump back to... + sub lr, lr, #4 @ we assume to be ARM32 + + mov r0, lr + mov r1, #8 + bl phex + + @ print abort message + mov r0, #'A' + bl putchar_asm + mov r0, #'B' + bl putchar_asm + mov r0, #'O' + bl putchar_asm + mov r0, #'R' + bl putchar_asm + mov r0, #'T' + bl putchar_asm + + @ disable IRQ and FIQ + msr CPSR_c, #I_BIT | F_BIT + +0: @ dead + b 0b + +/* entry point for IRQs */ +.global irq_entry +irq_entry: + /* Adjust and save LR_irq in IRQ stack */ + sub lr, lr, #4 + stmfd sp!, {lr} + + /* Save SPSR for nested interrupt */ + mrs r14, SPSR + stmfd sp!, {r14} + + /* Call the interrupt handler C function */ + stmfd sp!, {r0-r4, r12} + bl irq + ldmfd sp!, {r0-r4, r12} + + /* Restore SPSR_irq from IRQ stack */ + ldmia sp!, {r14} + msr SPSR_cxsf, r14 + + /* Restore adjusted LR_irq from IRQ stack directly in the PC */ + ldmia sp!, {pc}^ + +/* entry point for FIQs */ +.global fiq_entry +fiq_entry: + /* Adjust and save LR_irq in IRQ stack */ + sub lr, lr, #4 + stmfd sp!, {lr} + + /* Save SPSR for nested interrupt */ + mrs r14, SPSR + stmfd sp!, {r14} + + /* Call the interrupt handler C function */ + stmfd sp!, {r0-r4, r12} + bl fiq + ldmfd sp!, {r0-r4, r12} + + /* Restore SPSR_irq from IRQ stack */ + ldmia sp!, {r14} + msr SPSR_cxsf, r14 + + /* Restore adjusted LR_irq from IRQ stack directly in the PC */ + ldmia sp!, {pc}^ diff --git a/src/target/firmware/board/compal/header.S b/src/target/firmware/board/compal/header.S new file mode 100644 index 00000000..747f6806 --- /dev/null +++ b/src/target/firmware/board/compal/header.S @@ -0,0 +1,11 @@ +/* + * This is a textual header that is prepended to images where appropriate. + * + * It is meant to ease identification of our firmwares in dumps as well + * as filling some space that is used for the same purpose by the vendor. + * + */ +.section .compal.header +.ascii "OSMOCOM" +. = 0x20 +.ascii GIT_REVISION diff --git a/src/target/firmware/board/compal/highram.lds b/src/target/firmware/board/compal/highram.lds new file mode 100644 index 00000000..1f0a5a67 --- /dev/null +++ b/src/target/firmware/board/compal/highram.lds @@ -0,0 +1,121 @@ +/* + * Linker script for running from upper internal RAM on the TI Calypso + * + * This script creates a binary that can be loaded into high ram on + * all Calypso devices. It can be jumped into directly at the load + * address. + * + * This is used for debugging the loader and for general hacking purposes. + * + */ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +MEMORY +{ + /* lowram: could be anything, we place exception vectors here */ + XRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00020000 + /* highram binary: our text, initialized data */ + LRAM (rw) : ORIGIN = 0x00820000, LENGTH = 0x00010000 + /* highram binary: our unitialized data, stacks, heap */ + IRAM (rw) : ORIGIN = 0x00830000, LENGTH = 0x00010000 +} +SECTIONS +{ + . = 0x820000; + + /* initialization code */ + .text.start : { + PROVIDE(_start = .); + KEEP(*(.text.start)) + *(.text.start) + } > LRAM + + /* exception vectors linked for 0x80001c to 0x800034 */ + .text.exceptions 0x80001c : AT (LOADADDR(.text.start) + SIZEOF(.text.start)) { + KEEP(*(.text.exceptions)) + * (.text.exceptions) + . = ALIGN(4); + } > XRAM + PROVIDE(_exceptions = LOADADDR(.text.exceptions)); + + /* code */ + . = ALIGN(4); + .text (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) : + AT (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) { + /* regular code */ + *(.text*) + /* always-in-ram code */ + *(.ramtext*) + /* gcc voodoo */ + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx) + . = ALIGN(4); + } > LRAM + PROVIDE(_text_start = LOADADDR(.text)); + PROVIDE(_text_end = LOADADDR(.text) + SIZEOF(.text)); + + /* constructor pointers */ + .ctors : { + /* ctor count */ + LONG(SIZEOF(.ctors) / 4 - 2) + /* ctor pointers */ + KEEP(*(SORT(.ctors))) + /* end of list */ + LONG(0) + } > LRAM + PROVIDE(_ctor_start = LOADADDR(.ctors)); + PROVIDE(_ctor_end = LOADADDR(.ctors) + SIZEOF(.ctors)); + + /* destructor pointers */ + .dtors : { + /* dtor count */ + LONG(SIZEOF(.dtors) / 4 - 2) + /* dtor pointers */ + KEEP(*(SORT(.dtors))) + /* end of list */ + LONG(0) + } > LRAM + PROVIDE(_dtor_start = LOADADDR(.dtors)); + PROVIDE(_dtor_end = LOADADDR(.dtors) + SIZEOF(.dtors)); + + /* read-only data */ + . = ALIGN(4); + .rodata : { + *(.rodata*) + } > LRAM + PROVIDE(_rodata_start = LOADADDR(.rodata)); + PROVIDE(_rodata_end = LOADADDR(.rodata) + SIZEOF(.rodata)); + + /* initialized data */ + . = ALIGN(4); + .data : { + *(.data) + } > LRAM + PROVIDE(_data_start = LOADADDR(.data)); + PROVIDE(_data_end = LOADADDR(.data) + SIZEOF(.data)); + + /* pic offset tables */ + . = ALIGN(4); + .got : { + *(.got) + *(.got.plt) *(.igot.plt) *(.got) *(.igot) + } > LRAM + PROVIDE(_got_start = LOADADDR(.got)); + PROVIDE(_got_end = LOADADDR(.got) + SIZEOF(.got)); + + /* uninitialized data */ + .bss (NOLOAD) : { + . = ALIGN(4); + __bss_start = .; + *(.bss) + } > IRAM + . = ALIGN(4); + __bss_end = .; + PROVIDE(_bss_start = __bss_start); + PROVIDE(_bss_end = __bss_end); + + /* end of image */ + . = ALIGN(4); + _end = .; + PROVIDE(end = .); +} diff --git a/src/target/firmware/board/compal/macros.S b/src/target/firmware/board/compal/macros.S new file mode 100644 index 00000000..613e6bda --- /dev/null +++ b/src/target/firmware/board/compal/macros.S @@ -0,0 +1,76 @@ + +.macro clear_bss + mov r0, #0 + ldr r1, =__bss_start + ldr r2, =__bss_end +loop_bss: + cmp r1, r2 + strlo r0, [r1], #4 + blo loop_bss +.endm + +.macro copy_data + ldr r0, =__data_start + ldr r1, =_data_start + ldr r2, =__data_end + cmp r0, r2 + beq done_data +loop_data: + ldrb r4, [r0], #1 + strb r4, [r1], #1 + cmp r0, r2 + bne loop_data +done_data: +.endm + +.macro copy_ramtext + ldr r0, =__ramtext_start + ldr r1, =_ramtext_start + ldr r2, =__ramtext_end + cmp r0, r2 + beq done_ramtext +loop_ramtext: + ldrb r4, [r0], #1 + strb r4, [r1], #1 + cmp r0, r2 + bne loop_ramtext +done_ramtext: +.endm + + .EQU ARM_MODE_FIQ, 0x11 + .EQU ARM_MODE_IRQ, 0x12 + .EQU ARM_MODE_SVC, 0x13 + + .EQU I_BIT, 0x80 + .EQU F_BIT, 0x40 + +#define TOP_OF_RAM 0x083fff0 +#define FIQ_STACK_SIZE 1024 +#define IRQ_STACK_SIZE 1024 + +.macro init_stacks + /* initialize stacks, starting at TOP_OF_RAM */ + ldr r0, =TOP_OF_RAM + + /* initialize FIQ stack */ + msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT + mov r13, r0 + sub r0, r0, #FIQ_STACK_SIZE + + /* initialize IRQ stack */ + msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT + mov r13, r0 + sub r0, r0, #IRQ_STACK_SIZE + + /* initialize supervisor stack */ + msr CPSR_c, #ARM_MODE_SVC | I_BIT | F_BIT + mov r13, r0 +.endm + +.macro call_ctors + /* call constructor functions */ + ldr r0, =_ctor_start + ldr r1, =_ctor_end + bl do_global_ctors +.endm + diff --git a/src/target/firmware/board/compal/ram.lds b/src/target/firmware/board/compal/ram.lds new file mode 100644 index 00000000..342870dc --- /dev/null +++ b/src/target/firmware/board/compal/ram.lds @@ -0,0 +1,123 @@ +/* + * Linker script for running from internal SRAM on Compal phones + * + * This script is tailored specifically to the requirements imposed + * on us by the Compal bootloader. + * + */ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +MEMORY +{ + /* compal-loaded binary: our text, initialized data */ + LRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00010000 + /* compal-loaded binary: our unitialized data, stacks, heap */ + IRAM (rw) : ORIGIN = 0x00810000, LENGTH = 0x00010000 +} +SECTIONS +{ + . = 0x800000; + + /* romloader data section, contains passthru interrupt vectors */ + .compal.loader (NOLOAD) : { . = 0x100; } > LRAM + + /* image signature (prepended by osmocon according to phone type) */ + .compal.header (NOLOAD) : { . = 4; } > LRAM + + /* initialization code */ + . = ALIGN(4); + .text.start : { + PROVIDE(_start = .); + KEEP(*(.text.start)) + *(.text.start) + } > LRAM + + /* exception vectors from 0x80001c to 0x800034 */ + .text.exceptions 0x80001c : AT (LOADADDR(.text.start) + SIZEOF(.text.start)) { + KEEP(*(.text.exceptions)) + * (.text.exceptions) + . = ALIGN(4); + } > LRAM + PROVIDE(_exceptions = LOADADDR(.text.exceptions)); + + /* code */ + . = ALIGN(4); + .text (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) : + AT (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) { + /* regular code */ + *(.text*) + /* always-in-ram code */ + *(.ramtext*) + /* gcc voodoo */ + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx) + . = ALIGN(4); + } > LRAM + PROVIDE(_text_start = LOADADDR(.text)); + PROVIDE(_text_end = LOADADDR(.text) + SIZEOF(.text)); + + /* constructor pointers */ + .ctors : { + /* ctor count */ + LONG(SIZEOF(.ctors) / 4 - 2) + /* ctor pointers */ + KEEP(*(SORT(.ctors))) + /* end of list */ + LONG(0) + } > LRAM + PROVIDE(_ctor_start = LOADADDR(.ctors)); + PROVIDE(_ctor_end = LOADADDR(.ctors) + SIZEOF(.ctors)); + + /* destructor pointers */ + .dtors : { + /* dtor count */ + LONG(SIZEOF(.dtors) / 4 - 2) + /* dtor pointers */ + KEEP(*(SORT(.dtors))) + /* end of list */ + LONG(0) + } > LRAM + PROVIDE(_dtor_start = LOADADDR(.dtors)); + PROVIDE(_dtor_end = LOADADDR(.dtors) + SIZEOF(.dtors)); + + /* read-only data */ + . = ALIGN(4); + .rodata : { + *(.rodata*) + } > LRAM + PROVIDE(_rodata_start = LOADADDR(.rodata)); + PROVIDE(_rodata_end = LOADADDR(.rodata) + SIZEOF(.rodata)); + + /* initialized data */ + . = ALIGN(4); + .data : { + *(.data) + } > LRAM + PROVIDE(_data_start = LOADADDR(.data)); + PROVIDE(_data_end = LOADADDR(.data) + SIZEOF(.data)); + + /* pic offset tables */ + . = ALIGN(4); + .got : { + *(.got) + *(.got.plt) *(.igot.plt) *(.got) *(.igot) + } > LRAM + PROVIDE(_got_start = LOADADDR(.got)); + PROVIDE(_got_end = LOADADDR(.got) + SIZEOF(.got)); + + /* uninitialized data */ + .bss (NOLOAD) : { + . = ALIGN(4); + __bss_start = .; + *(.bss) + } > IRAM + . = ALIGN(4); + __bss_end = .; + PROVIDE(_bss_start = __bss_start); + PROVIDE(_bss_end = __bss_end); + + /* end of image */ + . = ALIGN(4); + _end = .; + PROVIDE(end = .); +} diff --git a/src/target/firmware/board/compal/rf_power.c b/src/target/firmware/board/compal/rf_power.c new file mode 100644 index 00000000..71033aea --- /dev/null +++ b/src/target/firmware/board/compal/rf_power.c @@ -0,0 +1,62 @@ +/* Tx RF power calibration for the Compal/Motorola dualband phones */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <osmocore/utils.h> + +/* GSM900 ARFCN 33, Measurements by Steve Markgraf / May 2010 */ +const int16_t dbm2apc_gsm900[] = { + [0] = 151, + [1] = 152, + [2] = 153, + [3] = 155, + [4] = 156, + [5] = 158, + [6] = 160, + [7] = 162, + [8] = 164, + [9] = 167, + [10] = 170, + [11] = 173, + [12] = 177, + [13] = 182, + [14] = 187, + [15] = 192, + [16] = 199, + [17] = 206, + [18] = 214, + [19] = 223, + [20] = 233, + [21] = 244, + [22] = 260, + [23] = 271, + [24] = 288, + [25] = 307, + [26] = 327, + [27] = 350, + [28] = 376, + [29] = 407, + [30] = 456, + [31] = 575, +}; + +const int dbm2apc_gsm900_max = ARRAY_SIZE(dbm2apc_gsm900) - 1; diff --git a/src/target/firmware/board/compal/rffe_dualband.c b/src/target/firmware/board/compal/rffe_dualband.c new file mode 100644 index 00000000..bfd3d98e --- /dev/null +++ b/src/target/firmware/board/compal/rffe_dualband.c @@ -0,0 +1,80 @@ +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <rffe.h> +#include <calypso/tsp.h> +#include <rf/trf6151.h> + +/* This is a value that has been measured on the C123 by Harald: 71dBm, + it is the difference between the input level at the antenna and what + the DSP reports, subtracted by the total gain of the TRF6151 */ +#define SYSTEM_INHERENT_GAIN 71 + +/* describe how the RF frontend is wired on the Motorola E88 board (C117/C118/C121/C123) */ + +#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151 */ +#define PA_ENABLE TSPACT(1) /* Enable the Power Amplifier */ +#define TRENA TSPACT(6) /* Transmit Enable (Antenna Switch) */ +#define GSM_TXEN TSPACT(8) /* GSM (as opposed to DCS) Transmit */ + +#define IOTA_STROBE TSPEN0 /* Strobe for the Iota TSP */ +#define RITA_STROBE TSPEN2 /* Strobe for the Rita TSP */ + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx) +{ + uint16_t tspact = tsp_act_state(); + + /* First we mask off all bits from the state cache */ + tspact &= ~PA_ENABLE; + tspact |= TRENA | GSM_TXEN; /* low-active */ + +#ifdef CONFIG_TX_ENABLE + /* Then we selectively set the bits on, if required */ + if (tx) { + tspact &= ~TRENA; + if (band == GSM_BAND_900) + tspact &= ~GSM_TXEN; + tspact |= PA_ENABLE; /* Dieter: TODO */ + } +#endif /* TRANSMIT_SUPPORT */ + + tsp_act_update(tspact); +} + +#define MCU_SW_TRACE 0xfffef00e +#define ARM_CONF_REG 0xfffef006 + +void rffe_init(void) +{ + uint16_t reg; + + reg = readw(ARM_CONF_REG); + reg &= ~ (1 << 5); /* TSPACT6 I/O function, not nCS6 */ + writew(reg, ARM_CONF_REG); + + reg = readw(MCU_SW_TRACE); + reg &= ~(1 << 5); /* TSPACT8 I/O function, not nMREQ */ + writew(reg, MCU_SW_TRACE); +} + +uint8_t rffe_get_gain(void) +{ + return trf6151_get_gain(); +} + +const uint8_t system_inherent_gain = SYSTEM_INHERENT_GAIN; + +/* Given the expected input level of exp_inp dBm/8 and the target of target_bb + * dBm8, configure the RF Frontend with the respective gain */ +void rffe_set_gain(int16_t exp_inp, int16_t target_bb) +{ + trf6151_compute_gain(exp_inp, target_bb); +} + +void rffe_rx_win_ctrl(int16_t exp_inp, int16_t target_bb) +{ + /* FIXME */ +} diff --git a/src/target/firmware/board/compal/start.ram.S b/src/target/firmware/board/compal/start.ram.S new file mode 100644 index 00000000..c8f242c0 --- /dev/null +++ b/src/target/firmware/board/compal/start.ram.S @@ -0,0 +1,26 @@ + +.section .text.start + +#include "macros.S" + +.globl _start +_start: + /* clear bss section */ + clear_bss + + /* initialize all stacks */ + init_stacks + + /* call constructors */ + call_ctors + + /* jump to main */ + ldr pc, _jump_main + + /* endless loop at end of program */ +_loop: + b _loop + b _start + +_jump_main: + .word main diff --git a/src/target/firmware/board/compal/start.rom.S b/src/target/firmware/board/compal/start.rom.S new file mode 100644 index 00000000..211bea86 --- /dev/null +++ b/src/target/firmware/board/compal/start.rom.S @@ -0,0 +1,32 @@ + +.section .text.start + +#include "macros.S" + +.globl _start +_start: + /* clear bss section */ + clear_bss + + /* copy data to ram */ + copy_data + + /* copy alway-in-ram code */ + copy_ramtext + + /* initialize all stacks */ + init_stacks + + /* call constructors */ + call_ctors + + /* jump to main */ + ldr pc, _jump_main + + /* endless loop at end of program */ +_loop: + b _loop + b _start + +_jump_main: + .word main diff --git a/src/target/firmware/board/compal_e88/LINKAGE.txt b/src/target/firmware/board/compal_e88/LINKAGE.txt new file mode 100644 index 00000000..8adaf86e --- /dev/null +++ b/src/target/firmware/board/compal_e88/LINKAGE.txt @@ -0,0 +1,33 @@ + +The Compal E88 supports the common Compal RAM linkages. +These operate entirely from the Calypso internal RAM. + +Flash linkages are structured as follows: + + e88loader: + Linked at address of original compal application (0x2000). + Provides interrupt vectors as expected by compal loader. + Allows interrupt redirection (XXX to where?). + + We introduce this for the following reasons: + 1. We want to start our app at 0x10000, because that + is the first flash page after the loader page, allowing + us a higher degree of "unbrickability" by never reflashing + the bootloader. + 2. We want to keep the compal loader so we do not need even + more boot options and to allow recovery of original firmware. + 3. When there is a custom app in flash at 0xFFFF, just turning + the phone on with the compal loader would jump into an incomplete + motorola app. That might not even allow turning the phone off. + The loader provides this functionality. + 4. We do not want to patch the compal loader for interrupt + redirect and entry vectors. So we need to place something between + 0x2000 and 0x10000 anyway. And since there is space, why not put + the whole loader in there. + 5. This loader has a good chance of being able to read crash buffers. + and examining RAM without it being clobbered by a ram upload. + + e88flash: + Our main application linkage, starting at 0x10000 and going through + all of flash. Data storage locations are still to be determined. + diff --git a/src/target/firmware/board/compal_e88/MEMORY_MAP.txt b/src/target/firmware/board/compal_e88/MEMORY_MAP.txt new file mode 100644 index 00000000..6094aa9b --- /dev/null +++ b/src/target/firmware/board/compal_e88/MEMORY_MAP.txt @@ -0,0 +1,21 @@ +The Compal E88 has the following physical memory map: + + /* 2 MBytes of external flash memory */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x200000 + /* 256 kBytes of internal zero-waitstate sram */ + IRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x040000 + /* 256 kBytes of external slow sram */ + ERAM (rw) : ORIGIN = 0x01000000, LENGTH = 0x040000 + +The flash layout, as distributed, is: + + 0x00000000 0x2000 Compal loader + 0x00002000 >>>>>> Compal application and storage + +Our flash layout is: + + 0x00000000 0x2000 Compal loader + 0x00002000 0xE000 OSMOCOM loader (see LINKAGE.txt for reasoning) + 0x00010000 >>>>>> OSMOCOM application and storage + +(XXX: determine storage location / storage descriptor location) diff --git a/src/target/firmware/board/compal_e88/flash.lds b/src/target/firmware/board/compal_e88/flash.lds new file mode 100644 index 00000000..cf0f6a41 --- /dev/null +++ b/src/target/firmware/board/compal_e88/flash.lds @@ -0,0 +1,134 @@ +/* + * Linker script for flashed applications on the Compal E88 + * + * This script creates a binary that can be linked at 0xFFFF, starting + * with the second flash page. This is what a phone application or + * pure layer1 device uses. + * + * XXX: interrupts? + * + */ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +MEMORY +{ + LOADR (rx) : ORIGIN = 0x00000000, LENGTH = 0x10000 + /* 2 MBytes of external flash memory (minus loader) */ + FLASH (rx) : ORIGIN = 0x00010000, LENGTH = 0x1F0000 + /* 256 kBytes of internal zero-waitstate sram */ + IRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x040000 + /* 256 kBytes of external slow sram */ + ERAM (rw) : ORIGIN = 0x01000000, LENGTH = 0x040000 +} +SECTIONS +{ + /* entrypoint */ + .text.start : { + PROVIDE(_start = .); + KEEP(*(.text.start)) + *(.text.start) + } > FLASH + + /* exception vectors from 0x80001c to 0x800034 */ + .text.exceptions 0x80001c : { + KEEP(*(.text.exceptions)) + * (.text.exceptions) + . = ALIGN(4); + } > IRAM AT> FLASH + PROVIDE(_exceptions = LOADADDR(.text.exceptions)); + + /* code */ + .text : { + /* regular code */ + *(.text*) + /* gcc voodoo */ + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx) + } > FLASH + PROVIDE(_text_start = ADDR(.text)); + PROVIDE(_text_end = ADDR(.text) + SIZEOF(.text)); + + /* constructor pointers */ + .ctors : { + /* ctor count */ + LONG(SIZEOF(.ctors) / 4 - 2) + /* ctor pointers */ + KEEP(*(SORT(.ctors))) + /* end of list */ + LONG(0) + } > FLASH + PROVIDE(_ctor_start = LOADADDR(.ctors)); + PROVIDE(_ctor_end = LOADADDR(.ctors) + SIZEOF(.ctors)); + + /* destructor pointers */ + .dtors : { + /* dtor count */ + LONG(SIZEOF(.dtors) / 4 - 2) + /* dtor pointers */ + KEEP(*(SORT(.dtors))) + /* end of list */ + LONG(0) + } > FLASH + PROVIDE(_dtor_start = LOADADDR(.dtors)); + PROVIDE(_dtor_end = LOADADDR(.dtors) + SIZEOF(.dtors)); + + /* read-only data */ + .rodata : { + *(.rodata*) + } > FLASH + PROVIDE(_rodata_start = ADDR(.rodata)); + PROVIDE(_rodata_end = ADDR(.rodata) + SIZEOF(.rodata)); + + /* pic offset tables */ + .got : { + . = ALIGN(4); + *(.got) + *(.got.plt) *(.igot.plt) *(.got) *(.igot) + . = ALIGN(4); + } > FLASH + PROVIDE(_got_start = ADDR(.got)); + PROVIDE(_got_end = ADDR(.got) + SIZEOF(.got)); + + /* reserved ram */ + .compal.reservedram 0x800000 (NOLOAD) : { + . = 0xff; + } > IRAM + + /* initialized data */ + .data : AT (LOADADDR(.got) + SIZEOF(.got)) { + . = ALIGN(4); + *(.data) + . = ALIGN(4); + } > IRAM + PROVIDE(__data_start = LOADADDR(.data)); + PROVIDE(__data_end = LOADADDR(.data) + SIZEOF(.data)); + PROVIDE(_data_start = ADDR(.data)); + PROVIDE(_data_end = ADDR(.data) + SIZEOF(.data)); + + /* ram code */ + .ramtext : AT (LOADADDR(.data) + SIZEOF(.data)) { + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + } > IRAM + PROVIDE(__ramtext_start = LOADADDR(.ramtext)); + PROVIDE(__ramtext_end = LOADADDR(.ramtext) + SIZEOF(.ramtext)); + PROVIDE(_ramtext_start = ADDR(.ramtext)); + PROVIDE(_ramtext_end = ADDR(.ramtext) + SIZEOF(.ramtext)); + + /* uninitialized data */ + .bss (NOLOAD) : { + . = ALIGN(4); + *(.bss) + . = ALIGN(4); + } > IRAM + PROVIDE(__bss_start = ADDR(.bss)); + PROVIDE(__bss_end = ADDR(.bss) + SIZEOF(.bss)); + PROVIDE(_bss_start = __bss_start); + PROVIDE(_bss_end = __bss_end); + + /* end of image */ + . = ALIGN(4); + _end = .; + PROVIDE(end = .); +} diff --git a/src/target/firmware/board/compal_e88/init.c b/src/target/firmware/board/compal_e88/init.c new file mode 100644 index 00000000..5a4f882f --- /dev/null +++ b/src/target/firmware/board/compal_e88/init.c @@ -0,0 +1,136 @@ +/* Initialization for the Compal E88 (Motorola C115...C123) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <ctors.h> +#include <memory.h> +#include <board.h> +#include <keypad.h> +#include <console.h> +#include <flash/cfi_flash.h> + +#include <calypso/irq.h> +#include <calypso/clock.h> +#include <calypso/dma.h> +#include <calypso/rtc.h> +#include <calypso/timer.h> +#include <calypso/uart.h> +#include <calypso/backlight.h> + +#include <comm/sercomm.h> +#include <comm/timer.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> +#include <display.h> + +#define ARMIO_LATCH_OUT 0xfffe4802 +#define ARMIO_CNTL_REG 0xfffe4804 +#define ASIC_CONF_REG 0xfffef008 + +static void board_io_init(void) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + /* LCD Set I/O(3) / SA0 to I/O(3) mode */ + reg &= ~(1 << 10); + /* Set function pins to I2C Mode */ + reg |= 0x1080; /* SCL / SDA */ + /* TWL3025: Set SPI+RIF RX clock to rising edge */ + reg |= (1 << 13) | (1 << 14); + writew(reg, ASIC_CONF_REG); + + /* LCD Set I/O(3) to output mode */ + reg = readw(ARMIO_CNTL_REG); + reg &= ~(1 << 3); + writew(reg, ARMIO_CNTL_REG); + + /* LCD Set I/O(3) output low */ + reg = readw(ARMIO_LATCH_OUT); + reg &= ~(1 << 3); + writew(reg, ARMIO_LATCH_OUT); +} + +void board_init(void) +{ + /* Configure the memory interface */ + calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1); + calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1); + calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0); + + /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */ + calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2); + + /* Configure the RHEA bridge with some sane default values */ + calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0); + + /* Initialize board-specific GPIO */ + board_io_init(); + + /* Enable bootrom mapping to route exception vectors to RAM */ + calypso_bootrom(1); + calypso_exceptions_install(); + + /* Initialize interrupt controller */ + irq_init(); + + /* initialize MODEM UART to be used for sercomm*/ + uart_init(SERCOMM_UART_NR, 1); + uart_baudrate(SERCOMM_UART_NR, UART_115200); + + /* Initialize IRDA UART to be used for old-school console code. + * note: IRDA uart only accessible on C115 and C117 PCB */ + uart_init(CONS_UART_NR, 1); + uart_baudrate(CONS_UART_NR, UART_115200); + + /* Initialize hardware timers */ + hwtimer_init(); + + /* Initialize DMA controller */ + dma_init(); + + /* Initialize real time clock */ + rtc_init(); + + /* Initialize system timers (uses hwtimer 2) */ + timer_init(); + + /* Initialize LCD driver (uses I2C) and backlight */ + display = &st7558_display; + display_init(); + bl_mode_pwl(1); + bl_level(50); + + /* Initialize keypad driver */ + keypad_init(1); + + /* Initialize ABB driver (uses SPI) */ + twl3025_init(); +} diff --git a/src/target/firmware/board/compal_e88/loader.lds b/src/target/firmware/board/compal_e88/loader.lds new file mode 100644 index 00000000..a7a001fc --- /dev/null +++ b/src/target/firmware/board/compal_e88/loader.lds @@ -0,0 +1,147 @@ +/* + * Linker script for flashed loader on the Compal E88 + * + * This script creates a binary that can replace a standard firmware + * located at 0x2000. It works in conjunction with the compal ramloader. + * + * The interrupt vectors and start address are at known, fixed offsets. + * + */ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +MEMORY +{ + /* 2 MBytes of external flash memory */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x200000 + /* 256 kBytes of internal zero-waitstate sram */ + IRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x040000 + /* 256 kBytes of external slow sram */ + ERAM (rw) : ORIGIN = 0x01000000, LENGTH = 0x040000 +} +SECTIONS +{ + /* Provide symbols for the compal loader */ + .compal.loader 0x00000000 (NOLOAD) : { + _compal_loader_start = .; + . = 0x2000; + _compal_loader_end = .; + } > FLASH + + /* Compal-style image header */ + .compal.header 0x00002000 : { + _compal_header_start = .; + KEEP(*(.compal.header)) + *(.compal.header) + . = 0xA0; + _compal_header_end = .; + } > FLASH + + /* Compal-style vector table */ + .compal.vectors 0x000020A0 : { + PROVIDE(_exceptions = .); + KEEP(*(.text.exceptions)) + *(.text.exceptions) + } > FLASH + + /* Compal-style entry point */ + .text.start 0x000020F8 : { + PROVIDE(_start = .); + KEEP(*(.text.start)) + *(.text.start) + } > FLASH + + /* code */ + .text : { + /* regular code */ + *(.text*) + /* gcc voodoo */ + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx) + } > FLASH + PROVIDE(_text_start = ADDR(.text)); + PROVIDE(_text_end = ADDR(.text) + SIZEOF(.text)); + + /* constructor pointers */ + .ctors : { + /* ctor count */ + LONG(SIZEOF(.ctors) / 4 - 2) + /* ctor pointers */ + KEEP(*(SORT(.ctors))) + /* end of list */ + LONG(0) + } > FLASH + PROVIDE(_ctor_start = LOADADDR(.ctors)); + PROVIDE(_ctor_end = LOADADDR(.ctors) + SIZEOF(.ctors)); + + /* destructor pointers */ + .dtors : { + /* dtor count */ + LONG(SIZEOF(.dtors) / 4 - 2) + /* dtor pointers */ + KEEP(*(SORT(.dtors))) + /* end of list */ + LONG(0) + } > FLASH + PROVIDE(_dtor_start = LOADADDR(.dtors)); + PROVIDE(_dtor_end = LOADADDR(.dtors) + SIZEOF(.dtors)); + + /* read-only data */ + .rodata : { + *(.rodata*) + } > FLASH + PROVIDE(_rodata_start = ADDR(.rodata)); + PROVIDE(_rodata_end = ADDR(.rodata) + SIZEOF(.rodata)); + + /* pic offset tables */ + .got : { + . = ALIGN(4); + *(.got) + *(.got.plt) *(.igot.plt) *(.got) *(.igot) + . = ALIGN(4); + } > FLASH + PROVIDE(_got_start = ADDR(.got)); + PROVIDE(_got_end = ADDR(.got) + SIZEOF(.got)); + + /* reserved ram */ + .compal.reservedram 0x800000 (NOLOAD) : { + . = 0xff; + } > IRAM + + /* initialized data */ + .data : AT (LOADADDR(.got) + SIZEOF(.got)) { + . = ALIGN(4); + *(.data) + . = ALIGN(4); + } > IRAM + PROVIDE(__data_start = LOADADDR(.data)); + PROVIDE(__data_end = LOADADDR(.data) + SIZEOF(.data)); + PROVIDE(_data_start = ADDR(.data)); + PROVIDE(_data_end = ADDR(.data) + SIZEOF(.data)); + + /* ram code */ + .ramtext : AT (LOADADDR(.data) + SIZEOF(.data)) { + . = ALIGN(4); + *(.ramtext) + . = ALIGN(4); + } > IRAM + PROVIDE(__ramtext_start = LOADADDR(.ramtext)); + PROVIDE(__ramtext_end = LOADADDR(.ramtext) + SIZEOF(.ramtext)); + PROVIDE(_ramtext_start = ADDR(.ramtext)); + PROVIDE(_ramtext_end = ADDR(.ramtext) + SIZEOF(.ramtext)); + + /* uninitialized data */ + .bss (NOLOAD) : { + . = ALIGN(4); + *(.bss) + . = ALIGN(4); + } > IRAM + PROVIDE(__bss_start = ADDR(.bss)); + PROVIDE(__bss_end = ADDR(.bss) + SIZEOF(.bss)); + PROVIDE(_bss_start = __bss_start); + PROVIDE(_bss_end = __bss_end); + + /* end of image */ + . = ALIGN(4); + _end = .; + PROVIDE(end = .); +} diff --git a/src/target/firmware/board/compal_e99/init.c b/src/target/firmware/board/compal_e99/init.c new file mode 100644 index 00000000..47725a99 --- /dev/null +++ b/src/target/firmware/board/compal_e99/init.c @@ -0,0 +1,140 @@ +/* Initialization for the Compal E99 (Motorola C155) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Steve Markgraf <steve@steve-m.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <ctors.h> +#include <memory.h> +#include <board.h> +#include <keypad.h> +#include <console.h> +#include <flash/cfi_flash.h> + +#include <calypso/irq.h> +#include <calypso/clock.h> +#include <calypso/dma.h> +#include <calypso/rtc.h> +#include <calypso/timer.h> +#include <calypso/uart.h> +#include <calypso/backlight.h> + +#include <comm/sercomm.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> +#include <display.h> + +#define ARMIO_LATCH_OUT 0xfffe4802 +#define ARMIO_CNTL_REG 0xfffe4804 +#define ASIC_CONF_REG 0xfffef008 + +static void board_io_init(void) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + /* LCD Set I/O(3) / SA0 to I/O(3) mode */ + reg &= ~( (1 << 12) | (1 << 10) | (1 << 7) | (1 << 1)) ; + /* don't set function pins to I2C Mode, C155 uses UWire */ + /* TWL3025: Set SPI+RIF RX clock to rising edge */ + reg |= (1 << 13) | (1 << 14); + writew(reg, ASIC_CONF_REG); + + /* LCD Set I/O(3) to output mode and enable C155 backlight (IO1) */ + /* FIXME: Put the display backlight control to backlight.c */ + reg = readw(ARMIO_CNTL_REG); + reg &= ~( (1 << 3) | (1 << 1)); + writew(reg, ARMIO_CNTL_REG); + + /* LCD Set I/O(3) output low */ + reg = readw(ARMIO_LATCH_OUT); + reg &= ~(1 << 3); + reg |= (1 << 1); + writew(reg, ARMIO_LATCH_OUT); +} + +void board_init(void) +{ + /* Disable watchdog (compal loader leaves it enabled) */ + wdog_enable(0); + + /* Configure memory interface */ + calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1); + calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1); + calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0); + + /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */ + calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2); + + /* Configure the RHEA bridge with some sane default values */ + calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0); + + /* Initialize board-specific GPIO */ + board_io_init(); + + /* Enable bootrom mapping to route exception vectors to RAM */ + calypso_bootrom(1); + calypso_exceptions_install(); + + /* Initialize interrupt controller */ + irq_init(); + + /* initialize MODEM UART to be used for sercomm*/ + uart_init(SERCOMM_UART_NR, 1); + uart_baudrate(SERCOMM_UART_NR, UART_115200); + + /* initialize IRDA UART to be used for old-school console code. + * note: IRDA uart only accessible on C115 and C117 PCB */ + uart_init(CONS_UART_NR, 1); + uart_baudrate(CONS_UART_NR, UART_115200); + + /* Initialize hardware timers */ + hwtimer_init(); + + /* Initialize DMA controller */ + dma_init(); + + /* Initialize real time clock */ + rtc_init(); + + /* Initialize system timers (uses hwtimer 2) */ + timer_init(); + + /* Initialize LCD driver (uses UWire) and backlight */ + display = &ssd1783_display; + display_init(); + bl_mode_pwl(1); + bl_level(50); + + /* Initialize keypad driver */ + keypad_init(1); + + /* Initialize ABB driver (uses SPI) */ + twl3025_init(); +} diff --git a/src/target/firmware/board/gta0x/init.c b/src/target/firmware/board/gta0x/init.c new file mode 100644 index 00000000..573b763d --- /dev/null +++ b/src/target/firmware/board/gta0x/init.c @@ -0,0 +1,136 @@ +/* Initialization for the Openmoko Freerunner modem */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <ctors.h> +#include <memory.h> +#include <board.h> +#include <keypad.h> +#include <console.h> +#include <flash/cfi_flash.h> + +#include <calypso/irq.h> +#include <calypso/clock.h> +#include <calypso/dma.h> +#include <calypso/rtc.h> +#include <calypso/timer.h> +#include <calypso/uart.h> +#include <calypso/backlight.h> + +#include <comm/sercomm.h> +#include <comm/timer.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> +#include <display.h> + +#define ARMIO_LATCH_OUT 0xfffe4802 +#define ARMIO_CNTL_REG 0xfffe4804 +#define ASIC_CONF_REG 0xfffef008 + +static void board_io_init(void) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + /* LCD Set I/O(3) / SA0 to I/O(3) mode */ + reg &= ~(1 << 10); + /* Set function pins to I2C Mode */ + reg |= 0x1080; /* SCL / SDA */ + /* TWL3025: Set SPI+RIF RX clock to rising edge */ + reg |= (1 << 13) | (1 << 14); + writew(reg, ASIC_CONF_REG); + + /* LCD Set I/O(3) to output mode */ + reg = readw(ARMIO_CNTL_REG); + reg &= ~(1 << 3); + writew(reg, ARMIO_CNTL_REG); + + /* LCD Set I/O(3) output low */ + reg = readw(ARMIO_LATCH_OUT); + reg &= ~(1 << 3); + writew(reg, ARMIO_LATCH_OUT); +} + +void board_init(void) +{ + /* Configure the memory interface */ + calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1); + calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1); + calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0); + + /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */ + calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2); + + /* Configure the RHEA bridge with some sane default values */ + calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0); + + /* Initialize board-specific GPIO */ + board_io_init(); + + /* Enable bootrom mapping to route exception vectors to RAM */ + calypso_bootrom(1); + calypso_exceptions_install(); + + /* Initialize interrupt controller */ + irq_init(); + + /* initialize MODEM UART to be used for sercomm*/ + uart_init(SERCOMM_UART_NR, 1); + uart_baudrate(SERCOMM_UART_NR, UART_115200); + + /* Initialize IRDA UART to be used for old-school console code. + * note: IRDA uart only accessible on C115 and C117 PCB */ + uart_init(CONS_UART_NR, 1); + uart_baudrate(CONS_UART_NR, UART_115200); + + /* Initialize hardware timers */ + hwtimer_init(); + + /* Initialize DMA controller */ + dma_init(); + + /* Initialize real time clock */ + rtc_init(); + + /* Initialize system timers (uses hwtimer 2) */ + timer_init(); + + /* Initialize LCD driver (uses I2C) and backlight */ + display = &st7558_display; + display_init(); + bl_mode_pwl(1); + bl_level(50); + + /* Initialize keypad driver */ + keypad_init(1); + + /* Initialize ABB driver (uses SPI) */ + twl3025_init(); +} diff --git a/src/target/firmware/board/gta0x/rf_power.c b/src/target/firmware/board/gta0x/rf_power.c new file mode 100644 index 00000000..645c8a39 --- /dev/null +++ b/src/target/firmware/board/gta0x/rf_power.c @@ -0,0 +1,63 @@ +/* Tx RF power calibration for the FIC GTA0x phones */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <osmocore/utils.h> + +/* GSM900 ARFCN 33, Measurements by Steve Markgraf / May 2010 */ +/* FIXME those are from compal ... need real GTA calibration */ +const int16_t dbm2apc_gsm900[] = { + [0] = 151, + [1] = 152, + [2] = 153, + [3] = 155, + [4] = 156, + [5] = 158, + [6] = 160, + [7] = 162, + [8] = 164, + [9] = 167, + [10] = 170, + [11] = 173, + [12] = 177, + [13] = 182, + [14] = 187, + [15] = 192, + [16] = 199, + [17] = 206, + [18] = 214, + [19] = 223, + [20] = 233, + [21] = 244, + [22] = 260, + [23] = 271, + [24] = 288, + [25] = 307, + [26] = 327, + [27] = 350, + [28] = 376, + [29] = 407, + [30] = 456, + [31] = 575, +}; + +const int dbm2apc_gsm900_max = ARRAY_SIZE(dbm2apc_gsm900) - 1; diff --git a/src/target/firmware/board/manifest.c b/src/target/firmware/board/manifest.c new file mode 100644 index 00000000..025a7224 --- /dev/null +++ b/src/target/firmware/board/manifest.c @@ -0,0 +1,7 @@ + +#include "manifest.h" + +const char *manifest_application = APPLICATION; +const char *manifest_revision = GIT_REVISION; +const char *manifest_board = BOARD; +const char *manifest_environment = ENVIRONMENT; diff --git a/src/target/firmware/calypso/Makefile b/src/target/firmware/calypso/Makefile new file mode 100644 index 00000000..8fcad7b6 --- /dev/null +++ b/src/target/firmware/calypso/Makefile @@ -0,0 +1,4 @@ + +LIBRARIES+=calypso +calypso_DIR=calypso +calypso_SRCS=arm.c clock.c delay.c dma.c dsp.c du.c i2c.c irq.c rtc.c sim.c spi.c tpu.c tsp.c keypad.c misc.c timer.c backlight.c uart.c uwire.c diff --git a/src/target/firmware/calypso/arm.c b/src/target/firmware/calypso/arm.c new file mode 100644 index 00000000..8794ee35 --- /dev/null +++ b/src/target/firmware/calypso/arm.c @@ -0,0 +1,26 @@ + +/* enable IRQ+FIQ interrupts */ +void arm_enable_interrupts (void) +{ + unsigned long temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "bic %0, %0, #0xc0\n" + "msr cpsr_c, %0" + : "=r" (temp) + : + : "memory"); +} + +/* disable IRQ/FIQ interrupts + * returns true if interrupts had been enabled before we disabled them */ +int arm_disable_interrupts(void) +{ + unsigned long old,temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "orr %1, %0, #0xc0\n" + "msr cpsr_c, %1" + : "=r" (old), "=r" (temp) + : + : "memory"); + return (old & 0x80) == 0; +} diff --git a/src/target/firmware/calypso/backlight.c b/src/target/firmware/calypso/backlight.c new file mode 100644 index 00000000..a18dcb91 --- /dev/null +++ b/src/target/firmware/calypso/backlight.c @@ -0,0 +1,69 @@ +/* Calypso DBB internal PWL (Pulse Width / Light) Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <memory.h> + +#define BASE_ADDR_PWL 0xfffe8000 +#define PWL_REG(m) (BASE_ADDR_PWL + (m)) + +#define ASIC_CONF_REG 0xfffef008 +#define LIGHT_LEVEL_REG 0xfffe4810 + +enum pwl_reg { + PWL_LEVEL = 0, + PWL_CTRL = 1, +}; + +#define ASCONF_PWL_ENA (1 << 4) + +void bl_mode_pwl(int on) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + + if (on) { + /* Enable pwl */ + writeb(0x01, PWL_REG(PWL_CTRL)); + /* Switch pin from LT to PWL */ + reg |= ASCONF_PWL_ENA; + writew(reg, ASIC_CONF_REG); + } else { + /* Switch pin from PWL to LT */ + reg |= ~ASCONF_PWL_ENA; + writew(reg, ASIC_CONF_REG); + /* Disable pwl */ + writeb(0x00, PWL_REG(PWL_CTRL)); + } +} + +void bl_level(uint8_t level) +{ + if (readw(ASIC_CONF_REG) & ASCONF_PWL_ENA) { + writeb(level, PWL_REG(PWL_LEVEL)); + } else { + /* we need to scale the light level, as the + * ARMIO light controller only knows 0..63 */ + writeb(level>>2, LIGHT_LEVEL_REG); + } +} diff --git a/src/target/firmware/calypso/clock.c b/src/target/firmware/calypso/clock.c new file mode 100644 index 00000000..246b6e00 --- /dev/null +++ b/src/target/firmware/calypso/clock.c @@ -0,0 +1,200 @@ +/* Driver for Calypso clock management */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +//#define DEBUG +#include <debug.h> + +#include <memory.h> +#include <calypso/clock.h> + +#define REG_DPLL 0xffff9800 +#define DPLL_LOCK (1 << 0) +#define DPLL_BREAKLN (1 << 1) +#define DPLL_BYPASS_DIV_SHIFT 2 /* 2 bits */ +#define DPLL_PLL_ENABLE (1 << 4) +#define DPLL_PLL_DIV_SHIFT 5 /* 2 bits */ +#define DPLL_PLL_MULT_SHIFT 7 /* 5 bits */ +#define DPLL_TEST (1 << 12) +#define DPLL_IOB (1 << 13) /* Initialize on break */ +#define DPLL_IAI (1 << 14) /* Initialize after Idle */ + +#define BASE_ADDR_CLKM 0xfffffd00 +#define CLKM_REG(m) (BASE_ADDR_CLKM+(m)) + +enum clkm_reg { + CNTL_ARM_CLK = 0, + CNTL_CLK = 2, + CNTL_RST = 4, + CNTL_ARM_DIV = 8, +}; + +/* CNTL_ARM_CLK */ +#define ARM_CLK_BIG_SLEEP (1 << 0) /* MCU Master Clock enabled? */ +#define ARM_CLK_CLKIN_SEL0 (1 << 1) /* MCU source clock (0 = DPLL output, 1 = VTCXO or CLKIN */ +#define ARM_CLK_CLKIN_SEL (1 << 2) /* 0 = VTCXO or 1 = CLKIN */ +#define ARM_CLK_MCLK_DIV5 (1 << 3) /* enable 1.5 or 2.5 division factor */ +#define ARM_CLK_MCLK_DIV_SHIFT 4 /* 3 bits */ +#define ARM_CLK_DEEP_POWER_SHIFT 8 +#define ARM_CLK_DEEP_SLEEP 12 + +/* CNTL_CLK */ +#define CLK_IRQ_CLK_DIS (1 << 0) /* IRQ clock control (0 always, 1 according ARM_MCLK_EN) */ +#define CLK_BRIDGE_CLK_DIS (1 << 1) +#define CLK_TIMER_CLK_DIS (1 << 2) +#define CLK_DPLL_DIS (1 << 3) /* 0: DPLL is not stopped during SLEEP */ +#define CLK_CLKOUT_EN (1 << 4) /* Enable CLKOUT output pins */ +#define CLK_EN_IDLE3_FLG (1 << 5) /* DSP idle flag control (1 = + * SAM/HOM register forced to HOM when DSP IDLE3) */ +#define CLK_VCLKOUT_DIV2 (1 << 6) /* 1: VCLKOUT-FR is divided by 2 */ +#define CLK_VTCXO_DIV2 (1 << 7) /* 1: VTCXO is dividied by 2 */ + +#define BASE_ADDR_MEMIF 0xfffffb00 +#define MEMIF_REG(x) (BASE_ADDR_MEMIF+(x)) + +enum memif_reg { + API_RHEA_CTL = 0x0e, + EXTRA_CONF = 0x10, +}; + +static void dump_reg16(uint32_t addr, char *name) +{ + printf("%s=0x%04x\n", name, readw(addr)); +} + +void calypso_clk_dump(void) +{ + dump_reg16(REG_DPLL, "REG_DPLL"); + dump_reg16(CLKM_REG(CNTL_ARM_CLK), "CNTL_ARM_CLK"); + dump_reg16(CLKM_REG(CNTL_CLK), "CNTL_CLK"); + dump_reg16(CLKM_REG(CNTL_RST), "CNTL_RST"); + dump_reg16(CLKM_REG(CNTL_ARM_DIV), "CNTL_ARM_DIV"); +} + +void calypso_pll_set(uint16_t inp) +{ + uint8_t mult = inp >> 8; + uint8_t div = inp & 0xff; + uint16_t reg = readw(REG_DPLL); + + reg &= ~0x0fe0; + reg |= (div & 0x3) << DPLL_PLL_DIV_SHIFT; + reg |= (mult & 0x1f) << DPLL_PLL_MULT_SHIFT; + reg |= DPLL_PLL_ENABLE; + + writew(reg, REG_DPLL); +} + +void calypso_reset_set(enum calypso_rst calypso_rst, int active) +{ + uint8_t reg = readb(CLKM_REG(CNTL_RST)); + + if (active) + reg |= calypso_rst; + else + reg &= ~calypso_rst; + + writeb(reg, CLKM_REG(CNTL_RST)); +} + +int calypso_reset_get(enum calypso_rst calypso_rst) +{ + uint8_t reg = readb(CLKM_REG(CNTL_RST)); + + if (reg & calypso_rst) + return 1; + else + return 0; +} + +void calypso_clock_set(uint8_t vtcxo_div2, uint16_t inp, enum mclk_div mclk_div) +{ + uint16_t cntl_clock = readw(CLKM_REG(CNTL_CLK)); + uint16_t cntl_arm_clk = readw(CLKM_REG(CNTL_ARM_CLK)); + + /* First set the vtcxo_div2 */ + cntl_clock &= ~CLK_VCLKOUT_DIV2; + if (vtcxo_div2) + cntl_clock |= CLK_VTCXO_DIV2; + else + cntl_clock &= ~CLK_VTCXO_DIV2; + writew(cntl_clock, CLKM_REG(CNTL_CLK)); + + /* Then configure the MCLK divider */ + cntl_arm_clk &= ~ARM_CLK_CLKIN_SEL0; + if (mclk_div & 0x80) { + mclk_div &= ~0x80; + cntl_arm_clk |= ARM_CLK_MCLK_DIV5; + } else + cntl_arm_clk &= ~ARM_CLK_MCLK_DIV5; + cntl_arm_clk &= ~(0x7 << ARM_CLK_MCLK_DIV_SHIFT); + cntl_arm_clk |= (mclk_div << ARM_CLK_MCLK_DIV_SHIFT); + writew(cntl_arm_clk, CLKM_REG(CNTL_ARM_CLK)); + + /* Then finally set the PLL */ + calypso_pll_set(inp); +} + +void calypso_mem_cfg(enum calypso_bank bank, uint8_t ws, + enum calypso_mem_width width, int we) +{ + writew((ws & 0x1f) | ((width & 3) << 5) | ((we & 1) << 7), + BASE_ADDR_MEMIF + bank); +} + +void calypso_bootrom(int enable) +{ + uint16_t conf = readw(MEMIF_REG(EXTRA_CONF)); + + conf |= (3 << 8); + + if (enable) + conf &= ~(1 << 9); + + writew(conf, MEMIF_REG(EXTRA_CONF)); +} + +void calypso_debugunit(int enable) +{ + uint16_t conf = readw(MEMIF_REG(EXTRA_CONF)); + + if (enable) + conf &= ~(1 << 11); + else + conf |= (1 << 11); + + writew(conf, MEMIF_REG(EXTRA_CONF)); +} + +#define REG_RHEA_CNTL 0xfffff900 +#define REG_API_CNTL 0xfffff902 +#define REG_ARM_RHEA 0xfffff904 + +void calypso_rhea_cfg(uint8_t fac0, uint8_t fac1, uint8_t timeout, + uint8_t ws_h, uint8_t ws_l, uint8_t w_en0, uint8_t w_en1) +{ + writew(fac0 | (fac1 << 4) | (timeout << 8), REG_RHEA_CNTL); + writew(ws_h | (ws_l << 5), REG_API_CNTL); + writew(w_en0 | (w_en1 << 1), REG_ARM_RHEA); +} diff --git a/src/target/firmware/calypso/delay.c b/src/target/firmware/calypso/delay.c new file mode 100644 index 00000000..09f0043d --- /dev/null +++ b/src/target/firmware/calypso/delay.c @@ -0,0 +1,16 @@ +#include <delay.h> + +/* FIXME: We need proper calibrated delay loops at some point! */ +void delay_us(unsigned int us) +{ + volatile unsigned int i; + + for (i= 0; i < us*4; i++) { i; } +} + +void delay_ms(unsigned int ms) +{ + volatile unsigned int i; + + for (i= 0; i < ms*1300; i++) { i; } +} diff --git a/src/target/firmware/calypso/dma.c b/src/target/firmware/calypso/dma.c new file mode 100644 index 00000000..35c5be82 --- /dev/null +++ b/src/target/firmware/calypso/dma.c @@ -0,0 +1,44 @@ +/* Driver for Calypso DMA controller */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <memory.h> + +#define BASE_ADDR_DMA 0xfffffc00 + +enum dma_reg { + CONTROLLER_CONF = 0x00, + ALLOC_CONFIG = 0x02, +}; +#define DMA_REG(m) (BASE_ADDR_DMA + (m)) + +#define DMA_RAD(x) DMA_REG((x)*0x10 + 0x0) +#define DMA_RDPATH(x) DMA_REG((x)*0x10 + 0x2) +#define DMA_AAD(x) DMA_REG((x)*0x10 + 0x4) +#define DMA_ALGTH(x) DMA_REG((x)*0x10 + 0x6) +#define DMA_CTRL(x) DMA_REG((x)*0x10 + 0x8) +#define DMA_CUR_OFF_API(x) DMA_REG((x)*0x10 + 0xa) + +void dma_init(void) +{ + /* DMA 1 (RIF Tx), 2 (RIF Rx) allocated to DSP, all others to ARM */ + writew(0x000c, DMA_REG(ALLOC_CONFIG)); +} diff --git a/src/target/firmware/calypso/dsp.c b/src/target/firmware/calypso/dsp.c new file mode 100644 index 00000000..0d9521f0 --- /dev/null +++ b/src/target/firmware/calypso/dsp.c @@ -0,0 +1,690 @@ +#define DEBUG +/* Driver for the Calypso integrated DSP */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <calypso/clock.h> +#include <calypso/dsp.h> +#include <calypso/dsp_api.h> +#include <calypso/tpu.h> + +#include <abb/twl3025.h> + +#include <osmocore/gsm_utils.h> + + +#define REG_API_CONTROL 0xfffe0000 +#define APIC_R_SMODE_HOM (1 << 1) /* API is configured in HOM mode */ +#define APIC_R_HINT (1 << 3) /* Host processor interrupt (DSP->MCU) */ +#define APIC_W_DSPINT (1 << 2) /* ARM issues interrupt to DSP */ + +#define REG_API_WS 0xfffff902 /* Number of wait states for ARM access to API memory */ +#define REG_ARM_RHEA_CTL 0xfffff904 /* Write buffer bypassing */ +#define REG_EXT_RHEA_CTL 0xfffff906 /* Some timeout */ + +#define API_SIZE 0x2000U /* in words */ + +#define BASE_API_RAM 0xffd00000 /* Base address of API RAM form ARM point of view */ + +#define DSP_BASE_API 0x0800 /* Base address of API RAM for DSP */ +#define DSP_BASE_API_MIRROR 0xe000 /* Base address of API RAM for DSP (API boot mirrot */ +#define DSP_START 0x7000 /* DSP Start address */ + +/* Boot loader */ +#define BL_CMD_STATUS (BASE_API_RAM + 0x0ffe) /* Status / Command var */ +#define BL_ADDR_LO (BASE_API_RAM + 0x0ffc) /* Address (16 lsbs) */ +#define BL_ADDR_HI (BASE_API_RAM + 0x0ff8) /* Address (ext page bits) */ +#define BL_SIZE (BASE_API_RAM + 0x0ffa) /* Size */ + +#define BL_MAX_BLOCK_SIZE 0x7F0 /* Maximum size of copied block */ + + /* Possible values for the download status */ +#define BL_STATUS_NA 0 +#define BL_STATUS_IDLE 1 +#define BL_CMD_COPY_BLOCK 2 +#define BL_CMD_COPY_MODE 4 + +#define BL_MODE_PROG_WRITE 0 +#define BL_MODE_DATA_WRITE 1 +#define BL_MODE_PROG_READ 2 +#define BL_MODE_DATA_READ 3 +#define BL_MODE_PROM_READ 4 +#define BL_MODE_DROM_READ 5 + + +struct dsp_section { + uint32_t addr; /* addr for DSP */ + uint32_t size; /* size in words */ + const uint16_t *data; +}; + +#include "dsp_params.c" +#include "dsp_bootcode.c" +#include "dsp_dumpcode.c" + +struct dsp_api dsp_api = { + .ndb = (T_NDB_MCU_DSP *) BASE_API_NDB, + .db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0, + .db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0, + .param = (T_PARAM_MCU_DSP *) BASE_API_PARAM, + .r_page = 0, + .w_page = 0, +}; + + +void dsp_dump_version(void) +{ + printf("DSP Download Status: 0x%04x\n", readw(BL_CMD_STATUS)); + printf("DSP API Version: 0x%04x 0x%04x\n", + dsp_api.ndb->d_version_number1, dsp_api.ndb->d_version_number2); +} + +static void dsp_bl_wait_ready(void) +{ + while (readw(BL_CMD_STATUS) != BL_STATUS_IDLE); +} + +static void dsp_bl_start_at(uint16_t addr) +{ + writew(0, BL_ADDR_HI); + writew(addr, BL_ADDR_LO); + writew(0, BL_SIZE); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); +} + +static int dsp_bl_upload_sections(const struct dsp_section *sec) +{ + /* Make sure the bootloader is ready */ + dsp_bl_wait_ready(); + + /* Set mode */ + writew(BL_MODE_DATA_WRITE, BASE_API_RAM); + writew(BL_CMD_COPY_MODE, BL_CMD_STATUS); + dsp_bl_wait_ready(); + + /* Scan all sections */ + for (; sec->data; sec++) { + volatile uint16_t *api = (volatile uint16_t *)BASE_API_RAM; + unsigned int i; + + if (sec->size > BL_MAX_BLOCK_SIZE) + return -1; /* not supported for now */ + + /* Copy data to API */ + for (i=0; i<sec->size; i++) + api[i] = sec->data[i]; + + /* Issue DRAM write */ + writew(sec->addr >> 16, BL_ADDR_HI); + writew(sec->addr & 0xffff, BL_ADDR_LO); + writew(sec->size, BL_SIZE); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + /* Wait for completion */ + dsp_bl_wait_ready(); + } + + return 0; +} + +static int dsp_upload_sections_api(const struct dsp_section *sec, uint16_t dsp_base_api) +{ + for (; sec->data; sec++) { + unsigned int i; + volatile uint16_t *dptr; + + if (sec->addr & ~((1<<16)-1)) /* 64k max addr */ + return -1; + if (sec->addr < dsp_base_api) + return -1; + if ((sec->addr + sec->size) > (dsp_base_api + API_SIZE)) + return -1; + + dptr = (volatile uint16_t *)(BASE_API_RAM + ((sec->addr - dsp_base_api) * sizeof(uint16_t))); + for (i=0; i<sec->size; i++) + *dptr++ = sec->data[i]; + } + + /* FIXME need eioio or wb ? */ + + return 0; +} + +static void dsp_pre_boot(const struct dsp_section *bootcode) +{ + dputs("Assert DSP into Reset\n"); + calypso_reset_set(RESET_DSP, 1); + + if (bootcode) { + dputs("Loading initial DSP bootcode (API boot mode)\n"); + dsp_upload_sections_api(dsp_bootcode, DSP_BASE_API_MIRROR); + + writew(BL_STATUS_NA, BL_CMD_STATUS); + } else + delay_ms(10); + + dputs("Releasing DSP from Reset\n"); + calypso_reset_set(RESET_DSP, 0); + + /* Wait 10 us */ + delay_ms(100); + + dsp_bl_wait_ready(); +} + +static void dsp_set_params(int16_t *param_tab, int param_size) +{ + int i; + int16_t *param_ptr = (int16_t *) BASE_API_PARAM; + + /* Start DSP up to bootloader */ + dsp_pre_boot(dsp_bootcode); + + /* FIXME: Implement Patch download, if any */ + + dputs("Setting some dsp_api.ndb values\n"); + dsp_api.ndb->d_background_enable = 0; + dsp_api.ndb->d_background_abort = 0; + dsp_api.ndb->d_background_state = 0; + dsp_api.ndb->d_debug_ptr = 0x0074; + dsp_api.ndb->d_debug_bk = 0x0001; + dsp_api.ndb->d_pll_config = 0x154; //C_PLL_CONFIG; + dsp_api.ndb->p_debug_buffer = 0x17ff; //C_DEBUG_BUFFER_ADD; + dsp_api.ndb->d_debug_buffer_size = 7; //C_DEBUG_BUFFER_SIZE; + dsp_api.ndb->d_debug_trace_type = 0; //C_DEBUG_TRACE_TYPE; + dsp_api.ndb->d_dsp_state = 3; //C_DSP_IDLE3; + dsp_api.ndb->d_audio_gain_ul = 0; + dsp_api.ndb->d_audio_gain_dl = 0; + dsp_api.ndb->d_es_level_api = 0x5213; + dsp_api.ndb->d_mu_api = 0x5000; + + dputs("Setting API NDB parameters\n"); + for (i = 0; i < param_size; i ++) + *param_ptr++ = param_tab[i]; + + dsp_dump_version(); + + dputs("Finishing download phase\n"); + dsp_bl_start_at(DSP_START); + + dsp_dump_version(); +} + +void dsp_api_memset(uint16_t *ptr, int octets) +{ + uint16_t i; + for (i = 0; i < octets / sizeof(uint16_t); i++) + *ptr++ = 0; +} + +/* memcpy from RAM to DSP API, 16 bits by 16 bits. If odd byte count, last word will + * be zero filled */ +void dsp_memcpy_to_api(volatile uint16_t *dsp_buf, const uint8_t *mcu_buf, int n, int be) +{ + int odd, i; + + odd = n & 1; + n >>= 1; + + if (be) { + for (i=0; i<n; i++) { + uint16_t w; + w = *(mcu_buf++) << 8; + w |= *(mcu_buf++); + *(dsp_buf++) = w; + } + if (odd) + *dsp_buf = *mcu_buf << 8; + } else { + for (i=0; i<n; i++) { + uint16_t w; + w = *(mcu_buf++); + w |= *(mcu_buf++) << 8; + *(dsp_buf++) = w; + } + if (odd) + *dsp_buf = *mcu_buf; + } +} + +/* memcpy from DSP API to RAM, accessing API 16 bits word at a time */ +void dsp_memcpy_from_api(uint8_t *mcu_buf, const volatile uint16_t *dsp_buf, int n, int be) +{ + int odd, i; + + odd = n & 1; + n >>= 1; + + if (be) { + for (i=0; i<n; i++) { + uint16_t w = *(dsp_buf++); + *(mcu_buf++) = w >> 8; + *(mcu_buf++) = w; + } + if (odd) + *mcu_buf = *(dsp_buf++) >> 8; + } else { + for (i=0; i<n; i++) { + uint16_t w = *(dsp_buf++); + *(mcu_buf++) = w; + *(mcu_buf++) = w >> 8; + } + if (odd) + *mcu_buf = *(dsp_buf++); + } +} + +static void dsp_audio_init(void) +{ + T_NDB_MCU_DSP *ndb = dsp_api.ndb; + uint8_t i; + + ndb->d_vbctrl1 = ABB_VAL_T(VBCTRL1, 0x00B); /* VULSWITCH=0, VDLAUX=1, VDLEAR=1 */ + ndb->d_vbctrl2 = ABB_VAL_T(VBCTRL2, 0x000); /* MICBIASEL=0, VDLHSO=0, MICAUX=0 */ + + /* + * TODO: the following two settings are used to control + * the volume and uplink/downlink/sidetone gain. Make them + * adjustable by the user. + */ + + ndb->d_vbuctrl = ABB_VAL_T(VBUCTRL, 0x009); /* Uplink gain amp 3dB, Sidetone gain -5dB */ + ndb->d_vbdctrl = ABB_VAL_T(VBDCTRL, 0x066); /* Downlink gain amp 0dB, Volume control -6 dB */ + + ndb->d_toneskb_init = 0; /* MCU/DSP audio task com. register */ + ndb->d_toneskb_status = 0; /* MCU/DSP audio task com. register */ + + ndb->d_shiftul = 0x100; + ndb->d_shiftdl = 0x100; + + ndb->d_melo_osc_used = 0; + ndb->d_melo_osc_active = 0; + +#define SC_END_OSCILLATOR_MASK 0xfffe + + ndb->a_melo_note0[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note1[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note2[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note3[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note4[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note5[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note6[0] = SC_END_OSCILLATOR_MASK; + ndb->a_melo_note7[0] = SC_END_OSCILLATOR_MASK; + +#define MAX_FIR_COEF 31 + + /* Initialize the FIR as an all band pass */ + dsp_api.param->a_fir31_downlink[0] = 0x4000; + dsp_api.param->a_fir31_uplink[0] = 0x4000; + for (i = 1; i < MAX_FIR_COEF; i++) + { + dsp_api.param->a_fir31_downlink[i] = 0; + dsp_api.param->a_fir31_uplink[i] = 0; + } + +#define B_GSM_ONLY ((1L << 13) | (1L << 11)) /* GSM normal mode */ +#define B_BT_CORDLESS (1L << 12) /* Bluetooth cordless mode */ +#define B_BT_HEADSET (1L << 14) /* Bluetooth headset mode */ + + /* Bit set by the MCU to close the loop between the audio UL and DL path. */ + /* This features is used to find the FIR coefficient. */ +#define B_FIR_LOOP (1L << 1) + + /* Reset the FIR loopback and the audio mode */ + ndb->d_audio_init &= ~(B_FIR_LOOP | B_GSM_ONLY | B_BT_HEADSET | B_BT_CORDLESS); + + /* Set the GSM mode */ + ndb->d_audio_init |= (B_GSM_ONLY); + + ndb->d_aec_ctrl = 0; + + /* DSP background task through pending task queue */ + dsp_api.param->d_gsm_bgd_mgt = 0; + + ndb->d_audio_compressor_ctrl = 0x0401; + +#define NO_MELODY_SELECTED (0) + + ndb->d_melody_selection = NO_MELODY_SELECTED; +} + +static void dsp_ndb_init(void) +{ + T_NDB_MCU_DSP *ndb = dsp_api.ndb; + uint8_t i; + + #define APCDEL_DOWN (2+0) // minimum value: 2 + #define APCDEL_UP (6+3+1) // minimum value: 6 + + /* load APC ramp: set to "no ramp" so that there will be no output if + * not properly initialised at some other place. */ + for (i = 0; i < 16; i++) + dsp_api.ndb->a_ramp[i] = ABB_VAL(APCRAM, ABB_RAMP_VAL(0, 0)); + + /* Iota registers values will be programmed at 1st DSP communication interrupt */ + + /* Enable f_tx delay of 400000 cyc DEBUG */ + ndb->d_debug1 = ABB_VAL_T(0, 0x000); + ndb->d_afcctladd= ABB_VAL_T(AFCCTLADD, 0x000); // Value at reset + ndb->d_vbuctrl = ABB_VAL_T(VBUCTRL, 0x0C9); // Uplink gain amp 0dB, Sidetone gain to mute + ndb->d_vbdctrl = ABB_VAL_T(VBDCTRL, 0x006); // Downlink gain amp 0dB, Volume control 0 dB + ndb->d_bbctrl = ABB_VAL_T(BBCTRL, 0x2C1); // value at reset + ndb->d_bulgcal = ABB_VAL_T(BULGCAL, 0x000); // value at reset + ndb->d_apcoff = ABB_VAL_T(APCOFF, 0x040); // value at reset + ndb->d_bulioff = ABB_VAL_T(BULIOFF, 0x0FF); // value at reset + ndb->d_bulqoff = ABB_VAL_T(BULQOFF, 0x0FF); // value at reset + ndb->d_dai_onoff= ABB_VAL_T(APCOFF, 0x000); // value at reset + ndb->d_auxdac = ABB_VAL_T(AUXDAC, 0x000); // value at reset + ndb->d_vbctrl1 = ABB_VAL_T(VBCTRL1, 0x00B); // VULSWITCH=0, VDLAUX=1, VDLEAR=1. + ndb->d_vbctrl2 = ABB_VAL_T(VBCTRL2, 0x000); // MICBIASEL=0, VDLHSO=0, MICAUX=0 + + /* APCDEL will be initialized on rach only */ + ndb->d_apcdel1 = ABB_VAL_T(APCDEL1, ((APCDEL_DOWN-2) << 5) | (APCDEL_UP-6)); + ndb->d_apcdel2 = ABB_VAL_T(APCDEL2, 0x000); + + ndb->d_fb_mode = 1; /* mode 1 FCCH burst detection */ + ndb->d_fb_det = 0; /* we have not yet detected a FB */ + ndb->a_cd[0] = (1<<B_FIRE1); /* CCCH/SACCH downlink */ + ndb->a_dd_0[0] = 0; + ndb->a_dd_0[2] = 0xffff; + ndb->a_dd_1[0] = 0; + ndb->a_dd_1[2] = 0xffff; + ndb->a_du_0[0] = 0; + ndb->a_du_0[2] = 0xffff; + ndb->a_du_1[0] = 0; + ndb->a_du_1[2] = 0xffff; + ndb->a_fd[0] = (1<<B_FIRE1); + ndb->a_fd[2] = 0xffff; + ndb->d_a5mode = 0; + ndb->d_tch_mode = 0x0800; + + #define GUARD_BITS 8 // 11 or 9 for TSM30, 7 for Freerunner + ndb->d_tch_mode |= (((GUARD_BITS - 4) & 0x000F) << 7); //Bit 7..10: guard bits + + ndb->a_sch26[0] = (1<<B_SCH_CRC); + + /* Interrupt RIF transmit if FIFO <= threshold with threshold == 0 */ + /* MCM = 1, XRST = 0, CLKX_AUTO=1, TXM=1, NCLK_EN=1, NCLK13_EN=1, + * THRESHOLD = 0, DIV_CLK = 0 (13MHz) */ + ndb->d_spcx_rif = 0x179; + + /* Init audio related parameters */ + dsp_audio_init(); +} + +static void dsp_db_init(void) +{ + dsp_api_memset((uint16_t *)BASE_API_W_PAGE_0, sizeof(T_DB_MCU_TO_DSP)); + dsp_api_memset((uint16_t *)BASE_API_W_PAGE_1, sizeof(T_DB_MCU_TO_DSP)); + dsp_api_memset((uint16_t *)BASE_API_R_PAGE_0, sizeof(T_DB_DSP_TO_MCU)); + dsp_api_memset((uint16_t *)BASE_API_R_PAGE_1, sizeof(T_DB_DSP_TO_MCU)); +} + +void dsp_power_on(void) +{ + /* proabaly a good idea to initialize the whole API area to a know value */ + dsp_api_memset((uint16_t *)BASE_API_RAM, API_SIZE * 2); // size is in words + + dsp_set_params((int16_t *)&dsp_params, sizeof(dsp_params)/2); + dsp_ndb_init(); + dsp_db_init(); + dsp_api.frame_ctr = 0; + dsp_api.r_page = dsp_api.w_page = dsp_api.r_page_used = 0; +} + +/* test for frequency burst detection */ +#define REG_INT_STAT 0xffff1004 +static void wait_for_frame_irq(void) +{ + //puts("Waiting for Frame Interrupt"); + //while (readb(REG_INT_STAT) & 1) + while (readb((void *)0xffff1000) & (1<<4)) + ;// putchar('.'); + //puts("Done!\n"); +} + +void dsp_end_scenario(void) +{ + /* FIXME: we don't yet deal with the MISC_TASK */ + + /* End the DSP Scenario */ + dsp_api.ndb->d_dsp_page = B_GSM_TASK | dsp_api.w_page; + dsp_api.w_page ^= 1; + + /* Tell TPU to generate a FRAME interrupt to the DSP */ + tpu_dsp_frameirq_enable(); + tpu_frame_irq_en(1, 1); +} + +void dsp_load_rx_task(uint16_t task, uint8_t burst_id, uint8_t tsc) +{ + dsp_api.db_w->d_task_d = task; + dsp_api.db_w->d_burst_d = burst_id; + dsp_api.db_w->d_ctrl_system |= tsc & 0x7; +} + +void dsp_load_tx_task(uint16_t task, uint8_t burst_id, uint8_t tsc) +{ + dsp_api.db_w->d_task_u = task; + dsp_api.db_w->d_burst_u = burst_id; + dsp_api.db_w->d_ctrl_system |= tsc & 0x7; +} + +/* no AMR yet */ +void dsp_load_tch_param(struct gsm_time *next_time, + uint8_t chan_mode, uint8_t chan_type, uint8_t chan_sub, + uint8_t tch_loop, uint8_t sync_tch, uint8_t tn) +{ + uint16_t d_ctrl_tch; + uint16_t fn, a5fn0, a5fn1; + + /* d_ctrl_tch + ---------- + bit [0..3] -> b_chan_mode + bit [4..7] -> b_chan_type + bit [8] -> b_sync_tch_ul + bit [9] -> b_sync_tch_dl + bit [10] -> b_stop_tch_ul + bit [11] -> b_stop_tch_dl + bit [12..14] -> b_tch_loop + bit [15] -> b_subchannel */ + d_ctrl_tch = (chan_mode << B_CHAN_MODE) | + (chan_type << B_CHAN_TYPE) | + (chan_sub << B_SUBCHANNEL) | + (sync_tch << B_SYNC_TCH_UL) | + (sync_tch << B_SYNC_TCH_DL) | + (tch_loop << B_TCH_LOOP); + + /* used for ciphering and TCH traffic */ + + /* d_fn + ---- + + for TCH_F: + bit [0..7] -> b_fn_report = (fn - (tn * 13) + 104) % 104) + bit [8..15] -> b_fn_sid = (fn % 104) + + for TCH_H: + tn_report = (tn & ~1) | subchannel + bit [0..7] -> b_fn_report = (fn - tn_report * 13) + 104) % 104) + bit [8..15] -> b_fn_sid = (fn % 104) + + for other: irrelevant + */ + + if (chan_type == TCH_F) { + fn = ((next_time->fn - (tn * 13) + 104) % 104) | + ((next_time->fn % 104) << 8); + } else if (chan_type == TCH_H) { + uint8_t tn_report = (tn & ~1) | chan_sub; + fn = ((next_time->fn - (tn_report * 13) + 104) % 104) | + ((next_time->fn % 104) << 8); + } else { + /* irrelevant */ + fn = 0; + } + + /* a_a5fn + ------ + byte[0] bit [0..4] -> T2 + byte[0] bit [5..10] -> T3 + byte[1] bit [0..10] -> T1 */ + + a5fn0 = ((uint16_t)next_time->t3 << 5) | + (uint16_t)next_time->t2; + a5fn1 = (uint16_t)next_time->t1; + + dsp_api.db_w->d_fn = fn; /* Fn_sid & Fn_report */ + dsp_api.db_w->a_a5fn[0] = a5fn0; /* cyphering FN part 1 */ + dsp_api.db_w->a_a5fn[1] = a5fn1; /* cyphering FN part 2 */ + dsp_api.db_w->d_ctrl_tch = d_ctrl_tch; /* Channel config. */ +} + +void dsp_load_ciph_param(int mode, uint8_t *key) +{ + dsp_api.ndb->d_a5mode = mode; + + if (!mode || !key) + return; + + /* key is expected in the same format as in RSL + * Encryption information IE. So we need to load the + * bytes backward in A5 unit */ + dsp_api.ndb->a_kc[0] = (uint16_t)key[7] | ((uint16_t)key[6] << 8); + dsp_api.ndb->a_kc[1] = (uint16_t)key[5] | ((uint16_t)key[4] << 8); + dsp_api.ndb->a_kc[2] = (uint16_t)key[3] | ((uint16_t)key[2] << 8); + dsp_api.ndb->a_kc[3] = (uint16_t)key[1] | ((uint16_t)key[0] << 8); +} + +#define SC_CHKSUM_VER (BASE_API_W_PAGE_0 + (2 * (0x08DB - 0x800))) +static void dsp_dump_csum(void) +{ + printf("dsp page : %u\n", dsp_api.ndb->d_dsp_page); + printf("dsp code version : 0x%04x\n", dsp_api.db_r->a_pm[0]); + printf("dsp checksum : 0x%04x\n", dsp_api.db_r->a_pm[1]); + printf("dsp patch version : 0x%04x\n", readw(SC_CHKSUM_VER)); +} + +void dsp_checksum_task(void) +{ + dsp_dump_csum(); + dsp_api.db_w->d_task_md = CHECKSUM_DSP_TASK; + dsp_api.ndb->d_fb_mode = 1; + + dsp_end_scenario(); + + wait_for_frame_irq(); + + dsp_dump_csum(); +} + +#define L1D_AUXAPC 0x0012 +#define L1D_APCRAM 0x0014 + +void dsp_load_apc_dac(uint16_t apc) +{ + dsp_api.db_w->d_power_ctl = (apc << 6) | L1D_AUXAPC; +} + + +static void _dsp_dump_range(uint32_t addr, uint32_t size, int mode) +{ + uint32_t bs; + + /* Mode selection */ + writew(mode, BASE_API_RAM); + writew(BL_CMD_COPY_MODE, BL_CMD_STATUS); + dsp_bl_wait_ready(); + + /* Block by block dump */ + while (size) { + volatile uint16_t *api = (volatile uint16_t *)BASE_API_RAM; + + bs = (size > BL_MAX_BLOCK_SIZE) ? BL_MAX_BLOCK_SIZE : size; + size -= bs; + + writew(addr >> 16, BL_ADDR_HI); + writew(addr & 0xffff, BL_ADDR_LO); + writew(bs, BL_SIZE); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + dsp_bl_wait_ready(); + + while (bs--) { + if ((addr&15)==0) + printf("%05x : ", addr); + printf("%04hx%c", *api++, ((addr&15)==15)?'\n':' '); + addr++; + } + }; + puts("\n"); +} + +void dsp_dump(void) +{ + static const struct { + const char *name; + uint32_t addr; + uint32_t size; + int mode; + } dr[] = { + { "Registers", 0x00000, 0x0060, BL_MODE_DATA_READ }, + { "DROM", 0x09000, 0x5000, BL_MODE_DROM_READ }, + { "PDROM", 0x0e000, 0x2000, BL_MODE_DROM_READ }, + { "PROM0", 0x07000, 0x7000, BL_MODE_PROM_READ }, + { "PROM1", 0x18000, 0x8000, BL_MODE_PROM_READ }, + { "PROM2", 0x28000, 0x8000, BL_MODE_PROM_READ }, + { "PROM3", 0x38000, 0x2000, BL_MODE_PROM_READ }, + { NULL, 0, 0, -1 } + }; + + int i; + + /* Start DSP up to bootloader */ + dsp_pre_boot(dsp_bootcode); + + /* Load and execute our dump code in the DSP */ + dsp_upload_sections_api(dsp_dumpcode, DSP_BASE_API); + dsp_bl_start_at(DSP_DUMPCODE_START); + + /* our dump code actually simulates the boot loaded + * but with added read commands */ + dsp_bl_wait_ready(); + + /* Test the 'version' command */ + writew(0xffff, BL_CMD_STATUS); + dsp_bl_wait_ready(); + printf("DSP bootloader version 0x%04x\n", readw(BASE_API_RAM)); + + /* Dump each range */ + for (i=0; dr[i].name; i++) { + printf("DSP dump: %s [%05x-%05x]\n", dr[i].name, + dr[i].addr, dr[i].addr+dr[i].size-1); + _dsp_dump_range(dr[i].addr, dr[i].size, dr[i].mode); + } +} + diff --git a/src/target/firmware/calypso/dsp_bootcode.c b/src/target/firmware/calypso/dsp_bootcode.c new file mode 100644 index 00000000..2db46568 --- /dev/null +++ b/src/target/firmware/calypso/dsp_bootcode.c @@ -0,0 +1,9 @@ +/* Calypso integrated DSP boot code */ + +#define _SA_DECL (const uint16_t *)&(const uint16_t []) + +/* We don't really need any DSP boot code, it happily works with its own ROM */ +static const struct dsp_section *dsp_bootcode = NULL; + +#undef _SA_DECL + diff --git a/src/target/firmware/calypso/dsp_dumpcode.c b/src/target/firmware/calypso/dsp_dumpcode.c new file mode 100644 index 00000000..265a1c12 --- /dev/null +++ b/src/target/firmware/calypso/dsp_dumpcode.c @@ -0,0 +1,45 @@ +/* Generated from src/target_dsp/calypso/dsp_dump.bin */ + +#define _SA_DECL (const uint16_t *)&(const uint16_t []) + +static const struct dsp_section dsp_dumpcode[] = { + { + .addr = 0x1000, + .size = 0x005b, + .data = _SA_DECL { + 0x69f8, 0x0029, 0x0002, 0xea1f, + 0x7718, 0x1100, 0x7714, 0x0000, + 0x7712, 0x0800, 0x767f, 0x0001, + 0x607f, 0xffff, 0xf820, 0x1014, + 0xf273, 0x1008, 0x7682, 0x0100, + 0x607f, 0x0004, 0xf820, 0x101c, + 0xf273, 0x1008, 0x7214, 0x0800, + 0x607f, 0x0002, 0xf820, 0x100c, + 0x127e, 0x8813, 0x3c7c, 0x137d, + 0x8911, 0xf84c, 0x1028, 0xf4e2, + 0x7715, 0x0014, 0x963d, 0xfa30, + 0x104b, 0x6d89, 0x963f, 0xfa30, + 0x103f, 0x963e, 0xf495, 0xf830, + 0x103a, 0x47f8, 0x0011, 0x7f92, + 0xf073, 0x1008, 0x47f8, 0x0011, + 0x7e92, 0xf073, 0x1008, 0xf830, + 0x1046, 0x47f8, 0x0011, 0xe589, + 0xf073, 0x1008, 0x47f8, 0x0011, + 0xe598, 0xf073, 0x1008, 0x4911, + 0x891a, 0xf830, 0x1055, 0xf072, + 0x1052, 0xf074, 0x7213, 0xf073, + 0x1008, 0xf072, 0x1058, 0xf074, + 0xe4b8, 0xf073, 0x1008, + }, + }, + { /* Guard */ + .addr = 0, + .size = 0, + .data = NULL, + }, +}; + +#define DSP_DUMPCODE_START 0x1000 + +#undef _SA_DECL + diff --git a/src/target/firmware/calypso/dsp_params.c b/src/target/firmware/calypso/dsp_params.c new file mode 100644 index 00000000..e08b46e6 --- /dev/null +++ b/src/target/firmware/calypso/dsp_params.c @@ -0,0 +1,94 @@ +/* Values from an actual phone firmware that uses the 3306 DSP ROM code version */ +static T_PARAM_MCU_DSP dsp_params = { + .d_transfer_rate = 0x6666, + /* Latencies */ + .d_lat_mcu_bridge = 15, + .d_lat_mcu_hom2sam = 12, + .d_lat_mcu_bef_fast_access = 5, + .d_lat_dsp_after_sam = 4, + /* DSP Start Address */ + .d_gprs_install_address = 0x7002, /* needs to be set by patch or manually */ + .d_misc_config = 1, + .d_cn_sw_workaround = 0xE, + .d_hole2_param = { 0, 0, 0, 0 }, + /* Frequency Burst */ + .d_fb_margin_beg = 24, + .d_fb_margin_end = 22, + .d_nsubb_idle = 296, + .d_nsubb_dedic = 30, + .d_fb_thr_det_iacq = 0x3333, + .d_fb_thr_det_track = 0x28f6, + /* Demodulation */ + .d_dc_off_thres = 0x7fff, + .d_dummy_thres = 17408, + .d_dem_pond_gewl = 26624, + .d_dem_pond_red = 20152, + /* TCH Full Speech */ + .d_maccthresh1 = 7872, + .d_mldt = -4, + .d_maccthresh = 7872, + .d_gu = 5772, + .d_go = 7872, + .d_attmax = 53, + .d_sm = -892, + .d_b = 208, + /* V.42 bis */ + .d_v42b_switch_hyst = 16, + .d_v42b_switch_min = 64, + .d_v42b_switch_max = 250, + .d_v42b_reset_delay = 10, + /* TCH Half Speech */ + .d_ldT_hr = -5, + .d_maccthresh_hr = 6500, + .d_maccthresh1_hr = 6500, + .d_gu_hr = 2620, + .d_go_hr = 3700, + .d_b_hr = 182, + .d_sm_hr = -1608, + .d_attmax_hr = 53, + /* TCH Enhanced FR Speech */ + .c_mldt_efr = -4, + .c_maccthresh_efr = 8000, + .c_maccthresh1_efr = 8000, + .c_gu_efr = 4522, + .c_go_efr = 6500, + .c_b_efr = 174, + .c_sm_efr = -878, + .c_attmax_efr = 53, + /* CHED TCH Full Speech */ + .d_sd_min_thr_tchfs = 15, + .d_ma_min_thr_tchfs = 738, + .d_md_max_thr_tchfs = 1700, + .d_md1_max_thr_tchfs = 99, + /* CHED TCH Half Speech */ + .d_sd_min_thr_tchhs = 37, + .d_ma_min_thr_tchhs = 344, + .d_sd_av_thr_tchhs = 1845, + .d_md_max_thr_tchhs = 2175, + .d_md1_max_thr_tchhs = 138, + /* CHED TCH/F EFR Speech */ + .d_sd_min_thr_tchefs = 15, + .d_ma_min_thr_tchefs = 738, + .d_md_max_thr_tchefs = 0x4ce, + .d_md1_max_thr_tchefs = 0x63, + /* */ + .d_wed_fil_ini = 0x122a, + .d_wed_fil_tc = 0x7c00, + .d_x_min = 0xf, + .d_x_max = 0x17, + .d_slope = 0x87, + .d_y_min = 0x2bf, + .d_y_max = 0x99c, + .d_wed_diff_threshold = 0x196, + .d_mabfi_min_thr_tchhs = 0x14c8, + /* FACCH module */ + .d_facch_thr = 0, + /* IDS module */ + .d_max_ovsp_ul = 8, + .d_sync_thres = 0x3f50, + .d_idle_thres = 0x4000, + .d_m1_thres = 5, + .d_max_ovsp_dl = 8, + .d_gsm_bgd_mgt = 0, + /* we don't set the FIR coefficients !?! */ +}; diff --git a/src/target/firmware/calypso/du.c b/src/target/firmware/calypso/du.c new file mode 100644 index 00000000..58783b06 --- /dev/null +++ b/src/target/firmware/calypso/du.c @@ -0,0 +1,51 @@ +/* Calypso DU (Debug Unit) Driver */ + +/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <memory.h> +#include <stdint.h> +#include <stdio.h> + +#include <calypso/du.h> + +#define BASE_ADDR_DU 0x03c00000 +#define DU_REG(m) (BASE_ADDR_DU+(m)) + +void calypso_du_init() { + unsigned char c; + calypso_debugunit(1); + for(c = 0; c < 64; c++) { + writew(DU_REG(c), 0x00000000); + } +} + +void calypso_du_stop() { + calypso_debugunit(0); +} + +void calypso_du_dump() { + unsigned char c; + puts("Debug unit traceback:\n"); + for(c = 0; c < 64; c++) { + uint32_t w = readw(DU_REG(c)); + printf("t-%2x: 0x%8x\n", c, (unsigned int)w); + } +} diff --git a/src/target/firmware/calypso/i2c.c b/src/target/firmware/calypso/i2c.c new file mode 100644 index 00000000..2231990b --- /dev/null +++ b/src/target/firmware/calypso/i2c.c @@ -0,0 +1,123 @@ +/* Driver for I2C Master Controller inside TI Calypso */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <i2c.h> + +#define BASE_ADDR_I2C 0xfffe2800 +#define I2C_REG(x) (BASE_ADDR_I2C+(x)) + +enum i2c_reg { + DEVICE_REG = 0, + ADDRESS_REG, + DATA_WR_REG, + DATA_RD_REG, + CMD_REG, + CONF_FIFO_REG, + CONF_CLK_REG, + CONF_CLK_FUNC_REF, + STATUS_FIFO_REG, + STATUS_ACTIVITY_REG, +}; + +#define I2C_CMD_SOFT_RESET (1 << 0) +#define I2C_CMD_EN_CLK (1 << 1) +#define I2C_CMD_START (1 << 2) +#define I2C_CMD_RW_READ (1 << 3) +#define I2C_CMD_COMP_READ (1 << 4) +#define I2C_CMD_IRQ_ENABLE (1 << 5) + +#define I2C_STATUS_ERROR_DATA (1 << 0) +#define I2C_STATUS_ERROR_DEV (1 << 1) +#define I2C_STATUS_IDLE (1 << 2) // 1: not idle, 0: idle +#define I2C_STATUS_INTERRUPT (1 << 3) + +int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len) +{ + uint8_t cmd; + + /* Calypso I2C controller doesn't support fancy addressing */ + if (alen > 1) + return -1; + + /* FIXME: implement writes longer than fifo size */ + if (len > 16) + return -1; + + printd("i2c_write(chip=0x%02u, addr=0x%02u): ", chip, addr) + + writeb(chip & 0x3f, I2C_REG(DEVICE_REG)); + writeb(addr & 0xff, I2C_REG(ADDRESS_REG)); + + /* we have to tell the controler how many bits we'll put into the fifo ?!? */ + writeb(len-1, I2C_REG(CONF_FIFO_REG)); + + /* fill the FIFO */ + while (len--) { + uint8_t byte = *buffer++; + writeb(byte, I2C_REG(DATA_WR_REG)); + printd("%02X ", byte); + } + dputchar('\n'); + + /* start the transfer */ + cmd = readb(I2C_REG(CMD_REG)); + cmd |= I2C_CMD_START; + writeb(cmd, I2C_REG(CMD_REG)); + + /* wait until transfer completes */ + while (1) { + uint8_t reg = readb(I2C_REG(STATUS_ACTIVITY_REG)); + printd("I2C Status: 0x%02x\n", rerg & 0xf); + if (!(reg & I2C_STATUS_IDLE)) // 0: idle 1: not idle + break; + } + dputs("I2C transfer completed\n"); + + return 0; +} + +void i2c_init(int speed, int slaveadd) +{ + /* scl_out = clk_func_ref / 3, + clk_func_ref = master_clock_freq / (divisor_2 + 1) + master_clock_freq = ext_clock_freq / divisor_1 */ + /* clk_func_ref = scl_out * 3, + divisor_2 = (master_clock_freq / clk_func_ref) - 1 + divisor_1 = ext_clock_freq / master_clock_freq */ + /* for a target freq of 200kHz: + ext_clock_freq = 13MHz + clk_func_ref = 3 * 300kHZ = 600kHz + divisor_1 = 1 => master_clock_freq = ext_clock_freq = 13MHz + divisor_2 = 21 => clk_func_ref = 13MHz / (21+2) = 590.91 kHz + scl_out = clk_func_ref / 3 = 509.91 kHz / 3 = 196.97kHz */ + writeb(I2C_CMD_SOFT_RESET, I2C_REG(CMD_REG)); + + writeb(0x00, I2C_REG(CONF_CLK_REG)); + writeb(21, I2C_REG(CONF_CLK_FUNC_REF)); + + writeb(I2C_CMD_EN_CLK, I2C_REG(CMD_REG)); +} diff --git a/src/target/firmware/calypso/irq.c b/src/target/firmware/calypso/irq.c new file mode 100644 index 00000000..a3d57fbe --- /dev/null +++ b/src/target/firmware/calypso/irq.c @@ -0,0 +1,266 @@ +/* Driver for Calypso IRQ controller */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <arm.h> +#include <calypso/irq.h> + +#define BASE_ADDR_IRQ 0xfffffa00 + +enum irq_reg { + IT_REG1 = 0x00, + IT_REG2 = 0x02, + MASK_IT_REG1 = 0x08, + MASK_IT_REG2 = 0x0a, + IRQ_NUM = 0x10, + FIQ_NUM = 0x12, + IRQ_CTRL = 0x14, +}; + +#define ILR_IRQ(x) (0x20 + (x*2)) +#define IRQ_REG(x) ((void *)BASE_ADDR_IRQ + (x)) + +#define NR_IRQS 32 + +static uint8_t default_irq_prio[] = { + [IRQ_WATCHDOG] = 0xff, + [IRQ_TIMER1] = 0xff, + [IRQ_TIMER2] = 0xff, + [IRQ_TSP_RX] = 0, + [IRQ_TPU_FRAME] = 3, + [IRQ_TPU_PAGE] = 0xff, + [IRQ_SIMCARD] = 0xff, + [IRQ_UART_MODEM] = 8, + [IRQ_KEYPAD_GPIO] = 4, + [IRQ_RTC_TIMER] = 9, + [IRQ_RTC_ALARM_I2C] = 10, + [IRQ_ULPD_GAUGING] = 2, + [IRQ_EXTERNAL] = 12, + [IRQ_SPI] = 0xff, + [IRQ_DMA] = 0xff, + [IRQ_API] = 0xff, + [IRQ_SIM_DETECT] = 0, + [IRQ_EXTERNAL_FIQ] = 7, + [IRQ_UART_IRDA] = 2, + [IRQ_ULPD_GSM_TIMER] = 1, + [IRQ_GEA] = 0xff, +}; + +static irq_handler *irq_handlers[NR_IRQS]; + +static void _irq_enable(enum irq_nr nr, int enable) +{ + uint16_t *reg = IRQ_REG(MASK_IT_REG1); + uint16_t val; + + if (nr > 15) { + reg = IRQ_REG(MASK_IT_REG2); + nr -= 16; + } + + val = readw(reg); + if (enable) + val &= ~(1 << nr); + else + val |= (1 << nr); + writew(val, reg); +} + +void irq_enable(enum irq_nr nr) +{ + _irq_enable(nr, 1); +} + +void irq_disable(enum irq_nr nr) +{ + _irq_enable(nr, 0); +} + +void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio) +{ + uint16_t val; + + if (prio == -1) + prio = default_irq_prio[nr]; + + if (prio > 31) + prio = 31; + + val = prio << 2; + if (edge) + val |= 0x02; + if (fiq) + val |= 0x01; + + writew(val, IRQ_REG(ILR_IRQ(nr))); +} + +/* Entry point for interrupts */ +void irq(void) +{ + uint8_t num, tmp; + irq_handler *handler; + +#if 1 + /* Hardware interrupt detection mode */ + num = readb(IRQ_REG(IRQ_NUM)) & 0x1f; + + printd("i%02x\n", num); + + handler = irq_handlers[num]; + + if (handler) + handler(num); +#else + /* Software interrupt detection mode */ + { + uint16_t it_reg, mask_reg; + uint32_t irqs; + + it_reg = readw(IRQ_REG(IT_REG1)); + mask_reg = readw(IRQ_REG(MASK_IT_REG1)); + irqs = it_reg & ~mask_reg; + + it_reg = readw(IRQ_REG(IT_REG2)); + mask_reg = readw(IRQ_REG(MASK_IT_REG2)); + irqs |= (it_reg & ~mask_reg) << 16; + + for (num = 0; num < 32; num++) { + if (irqs & (1 << num)) { + printd("i%d\n", num); + handler = irq_handlers[num]; + if (handler) + handler(num); + /* clear this interrupt */ + if (num < 16) + writew(~(1 << num), IRQ_REG(IT_REG1)); + else + writew(~(1 << (num-16)), IRQ_REG(IT_REG2)); + } + } + dputchar('\n'); + } +#endif + /* Start new IRQ agreement */ + tmp = readb(IRQ_REG(IRQ_CTRL)); + tmp |= 0x01; + writeb(tmp, IRQ_REG(IRQ_CTRL)); +} + +/* Entry point for FIQs */ +void fiq(void) +{ + uint8_t num, tmp; + irq_handler *handler; + + num = readb(IRQ_REG(FIQ_NUM)) & 0x1f; + if (num) { + printd("f%02x\n", num); + } + + handler = irq_handlers[num]; + + if (handler) + handler(num); + + /* Start new FIQ agreement */ + tmp = readb(IRQ_REG(IRQ_CTRL)); + tmp |= 0x02; + writeb(tmp, IRQ_REG(IRQ_CTRL)); +} + +void irq_register_handler(enum irq_nr nr, irq_handler *handler) +{ + if (nr > NR_IRQS) + return; + + irq_handlers[nr] = handler; +} + +#define BASE_ADDR_IBOOT_EXC 0x0080001C +extern uint32_t _exceptions; + +/* Install the exception handlers to where the ROM loader jumps */ +void calypso_exceptions_install(void) +{ + uint32_t *exceptions_dst = (uint32_t *) BASE_ADDR_IBOOT_EXC; + uint32_t *exceptions_src = &_exceptions; + int i; + + for (i = 0; i < 7; i++) + *exceptions_dst++ = *exceptions_src++; + +} + +static void set_default_priorities(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(default_irq_prio); i++) { + uint16_t val; + uint8_t prio = default_irq_prio[i]; + if (prio > 31) + prio = 31; + + val = readw(IRQ_REG(ILR_IRQ(i))); + val &= ~(0x1f << 2); + val |= prio << 2; + writew(val, IRQ_REG(ILR_IRQ(i))); + } +} + +static uint32_t irq_nest_mask; +/* mask off all interrupts that have a lower priority than irq_nr */ +static void mask_all_lower_prio_irqs(enum irq_nr irqnr) +{ + uint8_t our_prio = readb(IRQ_REG(ILR_IRQ(irqnr))) >> 2; + int i; + + for (i = 0; i < _NR_IRQ; i++) { + uint8_t prio; + + if (i == irqnr) + continue; + + prio = readb(IRQ_REG(ILR_IRQ(i))) >> 2; + if (prio >= our_prio) + irq_nest_mask |= (1 << i); + } +} + +void irq_init(void) +{ + /* set default priorities */ + set_default_priorities(); + /* mask all interrupts off */ + writew(0xffff, IRQ_REG(MASK_IT_REG1)); + writew(0xffff, IRQ_REG(MASK_IT_REG2)); + /* clear all pending interrupts */ + writew(0, IRQ_REG(IT_REG1)); + writew(0, IRQ_REG(IT_REG2)); + /* enable interrupts globally to the ARM core */ + arm_enable_interrupts(); +} diff --git a/src/target/firmware/calypso/keypad.c b/src/target/firmware/calypso/keypad.c new file mode 100644 index 00000000..fd4e0ff2 --- /dev/null +++ b/src/target/firmware/calypso/keypad.c @@ -0,0 +1,198 @@ +/* Driver for the keypad attached to the TI Calypso */ + +/* (C) 2010 by roh <roh@hyte.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <defines.h> +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <keypad.h> + +#include <calypso/irq.h> +#include <abb/twl3025.h> + + +#define KBR_LATCH_REG 0xfffe480a +#define KBC_REG 0xfffe480c +#define KBD_GPIO_INT 0xfffe4816 +#define KBD_GPIO_MASKIT 0xfffe4818 + +static key_handler_t key_handler = NULL; + +void emit_key(uint8_t key, uint8_t state) +{ + printf("key=%u %s\n", key, state == PRESSED ? "pressed" : "released"); + + if (state == RELEASED) + if (key == KEY_POWER) + twl3025_power_off(); + + if(key_handler) { + key_handler(key, state); + } +} + +volatile uint32_t lastbuttons = 0; + +#define BTN_TO_KEY(name) \ + ((diff & BTN_##name) == BTN_##name) \ + { \ + key = KEY_##name; \ + diff = diff & ~BTN_##name; \ + state = (buttons & BTN_##name) ? PRESSED : RELEASED; \ + } + +void dispatch_buttons(uint32_t buttons) +{ + uint8_t state; + + if (buttons == lastbuttons) + return; + + uint32_t diff = buttons ^ lastbuttons; + uint8_t key=KEY_INV; + + while (diff != 0) + { + if BTN_TO_KEY(POWER) + else if BTN_TO_KEY(0) + else if BTN_TO_KEY(1) + else if BTN_TO_KEY(2) + else if BTN_TO_KEY(3) + else if BTN_TO_KEY(4) + else if BTN_TO_KEY(5) + else if BTN_TO_KEY(6) + else if BTN_TO_KEY(7) + else if BTN_TO_KEY(8) + else if BTN_TO_KEY(9) + else if BTN_TO_KEY(STAR) + else if BTN_TO_KEY(HASH) + else if BTN_TO_KEY(MENU) + else if BTN_TO_KEY(LEFT_SB) + else if BTN_TO_KEY(RIGHT_SB) + else if BTN_TO_KEY(UP) + else if BTN_TO_KEY(DOWN) + else if BTN_TO_KEY(LEFT) + else if BTN_TO_KEY(RIGHT) + else if BTN_TO_KEY(OK) + else + { + printf("\nunknown keycode: 0x%08x\n", diff); + break; + } + emit_key(key, state); + } + lastbuttons = buttons; +} + +static uint8_t polling = 0; +static uint8_t with_interrupts = 0; + +static void keypad_irq(__unused enum irq_nr nr) +{ + /* enable polling */ + polling = 1; + irq_disable(IRQ_KEYPAD_GPIO); +} + +void keypad_init(uint8_t interrupts) +{ + lastbuttons = 0; + polling = 0; + writew(0, KBD_GPIO_MASKIT); + writew(0, KBC_REG); + + if(interrupts) { + with_interrupts = 1; + irq_register_handler(IRQ_KEYPAD_GPIO, &keypad_irq); + irq_config(IRQ_KEYPAD_GPIO, 0, 0, 0); + irq_enable(IRQ_KEYPAD_GPIO); + } +} + +void keypad_set_handler(key_handler_t handler) +{ + key_handler = handler; +} + +void keypad_poll() +{ + static uint16_t reg; + static uint16_t col; + static uint32_t buttons = 0, debounce1 = 0, debounce2 = 0; + + if (with_interrupts && !polling) + return; + + /* start polling */ + if (polling == 1) { + writew(0x1f & ~0x1, KBC_REG); /* first col */ + col = 0; + polling = 2; + return; + } + + /* enable keypad irq after the signal settles */ + if (polling == 3) { + if(with_interrupts) { + irq_enable(IRQ_KEYPAD_GPIO); + polling = 0; + } else { + polling = 1; + } + return; + } + + reg = readw(KBR_LATCH_REG); + buttons = (buttons & ~(0x1f << (col * 5))) + | ((~reg & 0x1f) << (col * 5 )); + /* if key is released, stay in column for faster debounce */ + if ((debounce1 | debounce2) & ~buttons) { + debounce2 = debounce1; + debounce1 = buttons; + return; + } + + col++; + if (col > 4) { + col = 0; + /* if power button, ignore other states */ + if (buttons & BTN_POWER) + buttons = lastbuttons | BTN_POWER; + else if (lastbuttons & BTN_POWER) + buttons = lastbuttons & ~BTN_POWER; + dispatch_buttons(buttons); + if (buttons == 0) { + writew(0x0, KBC_REG); + polling = 3; + return; + } + } + if (col == 4) + writew(0xff, KBC_REG); + else + writew(0x1f & ~(0x1 << col ), KBC_REG); + +} + diff --git a/src/target/firmware/calypso/misc.c b/src/target/firmware/calypso/misc.c new file mode 100644 index 00000000..460cc5d5 --- /dev/null +++ b/src/target/firmware/calypso/misc.c @@ -0,0 +1,60 @@ + +#include <stdint.h> +#include <stdio.h> +#include <memory.h> + +/* dump a memory range */ +void memdump_range(unsigned int *ptr, unsigned int len) +{ + unsigned int *end = ptr + (len/4); + unsigned int *tmp; + + for (tmp = ptr; tmp < end; tmp += 8) { + int i; + printf("%08X: ", (unsigned int) tmp); + + for (i = 0; i < 8; i++) + printf("%08X %s", *(tmp+i), i == 3 ? " " : ""); + + putchar('\n'); + } +} + +#define KBIT 1024 +#define MBIT (1024*KBIT) +void dump_mem(void) +{ + puts("Dump 64kBits of internal ROM\n"); + memdump_range((void *)0x03800000, 64*KBIT/8); + + puts("Dump 8Mbits of external flash\n"); + memdump_range((void *)0x00000000, 8*MBIT/8); + + puts("Dump 2Mbits of internal RAM\n"); + memdump_range((void *)0x00800000, 2*MBIT/8); + + puts("Dump 2Mbits of external RAM\n"); + memdump_range((void *)0x01000000, 2*MBIT/8); +} + +#define REG_DEV_ID_CODE 0xfffef000 +#define REG_DEV_VER_CODE 0xfffef002 +#define REG_DEV_ARMVER_CODE 0xfffffe00 +#define REG_cDSP_ID_CODE 0xfffffe02 +#define REG_DIE_ID_CODE 0xfffef010 + +void dump_dev_id(void) +{ + int i; + + printf("Device ID code: 0x%04x\n", readw(REG_DEV_ID_CODE)); + printf("Device Version code: 0x%04x\n", readw(REG_DEV_VER_CODE)); + printf("ARM ID code: 0x%04x\n", readw(REG_DEV_ARMVER_CODE)); + printf("cDSP ID code: 0x%04x\n", readw(REG_cDSP_ID_CODE)); + puts("Die ID code: "); + for (i = 0; i < 64/8; i += 4) + printf("%08x", readl(REG_DIE_ID_CODE+i)); + putchar('\n'); +} + + diff --git a/src/target/firmware/calypso/rtc.c b/src/target/firmware/calypso/rtc.c new file mode 100644 index 00000000..ce750c29 --- /dev/null +++ b/src/target/firmware/calypso/rtc.c @@ -0,0 +1,83 @@ +/* Driver for Calypso RTC controller */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <display.h> +#include <calypso/irq.h> + +#define BASE_ADDR_RTC 0xfffe1800 +#define RTC_REG(x) ((void *)BASE_ADDR_RTC + (x)) + +enum rtc_reg { + SECOND_REG = 0x00, + MINUTES_REG = 0x01, + HOURS_REG = 0x02, + DAYS_REG = 0x03, + MONTHS_REG = 0x04, + YEARS_REG = 0x05, + WEEK_REG = 0x06, + /* reserved */ + ALARM_SECOND_REG = 0x08, + ALARM_MINUTES_REG = 0x09, + ALARM_HOURS_REG = 0x0a, + ALARM_DAYS_REG = 0x0b, + ALARM_MONTHS_REG = 0x0c, + ALARM_YEARS_REG = 0x0d, + /* reserved */ + /* reserved */ + CTRL_REG = 0x10, + STATUS_REG = 0x11, + INT_REG = 0x12, + COMP_LSB_REG = 0x13, + COMP_MSB_REG = 0x14, + RES_PROG_REG = 0x15, +}; + +static int tick_ctr; + +static void rtc_irq_tick(__unused enum irq_nr nr) +{ + if (tick_ctr & 1) + display_set_attr(DISP_ATTR_INVERT); + else + display_unset_attr(DISP_ATTR_INVERT); + tick_ctr++; +} + +void rtc_init(void) +{ + irq_register_handler(IRQ_RTC_TIMER, &rtc_irq_tick); + irq_config(IRQ_RTC_TIMER, 0, 1, 0); + irq_enable(IRQ_RTC_TIMER); + + /* clear power-up reset */ + writeb(0x80, RTC_REG(STATUS_REG)); + /* enable RTC running */ + writeb(0x01, RTC_REG(CTRL_REG)); + /* enable periodic interrupts every second */ + writeb(0x04, RTC_REG(INT_REG)); +} diff --git a/src/target/firmware/calypso/sim.c b/src/target/firmware/calypso/sim.c new file mode 100755 index 00000000..610f968f --- /dev/null +++ b/src/target/firmware/calypso/sim.c @@ -0,0 +1,740 @@ +/* Driver for Simcard Controller inside TI Calypso/Iota */ + +/* (C) 2010 by Philipp Fabian Benedikt Maier <philipp-maier@runningserver.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <abb/twl3025.h> +#include <calypso/sim.h> +#include <calypso/irq.h> + +static int sim_rx_character_count = 0; /* How many bytes have been received by calypso_sim_receive() */ +static int sim_tx_character_count = 0; /* How many bytes have been transmitted by calypso_sim_transmit() */ +static int sim_tx_character_length = 0; /* How many bytes have to be transmitted by calypso_sim_transmit() */ +static uint8_t *rx_buffer = 0; /* RX-Buffer that is issued by calypso_sim_receive() */ +static uint8_t *tx_buffer = 0; /* TX-Buffer that is issued by calypso_sim_transmit() */ +volatile static int rxDoneFlag = 0; /* Used for rx syncronization instead of a semaphore in calypso_sim_receive() */ +volatile static int txDoneFlag = 0; /* Used for rx syncronization instead of a semaphore in calypso_sim_transmit() */ + +/* Display Register dump */ +void calypso_sim_regdump(void) +{ +#if (SIM_DEBUG == 1) + unsigned int regVal; + + + puts("\n\n\n"); + puts("====================== CALYPSO SIM REGISTER DUMP =====================\n"); + puts("Reg_sim_cmd register (R/W) - FFFE:0000\n"); + + + regVal = readw(REG_SIM_CMD); + printf(" |-REG_SIM_CMD = %04x\n", readw(REG_SIM_CMD)); + + if(regVal & REG_SIM_CMD_CMDCARDRST) + puts(" | |-REG_SIM_CMD_CMDCARDRST = 1 ==> SIM card reset sequence enabled.\n"); + else + puts(" | |-REG_SIM_CMD_CMDCARDRST = 0 ==> SIM card reset sequence disabled.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CMD_CMDIFRST) + puts(" | |-REG_SIM_CMD_CMDIFRST = 1\n"); + else + puts(" | |-REG_SIM_CMD_CMDIFRST = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CMD_CMDSTOP) + puts(" | |-REG_SIM_CMD_CMDSTOP = 1\n"); + else + puts(" | |-REG_SIM_CMD_CMDSTOP = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CMD_CMDSTART) + puts(" | |-REG_SIM_CMD_CMDSTART = 1 ==> SIM card start procedure active.\n"); + else + puts(" | |-REG_SIM_CMD_CMDSTART = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CMD_CMDSTART) + puts(" | |-REG_SIM_CMD_MODULE_CLK_EN = 1 ==> Clock of the module enabled.\n"); + else + puts(" | |-REG_SIM_CMD_MODULE_CLK_EN = 0 ==> Clock of the module disabled.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_STAT); + printf(" |-REG_SIM_STAT = %04x\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_STAT_STATNOCARD) + puts(" | |-REG_SIM_STAT_STATNOCARD = 1 ==> No card!\n"); + else + puts(" | |-REG_SIM_STAT_STATNOCARD = 0 ==> Card detected!\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_STAT_STATTXPAR) + puts(" | |-REG_SIM_STAT_STATTXPAR = 1 ==> Parity ok!\n"); + else + puts(" | |-REG_SIM_STAT_STATTXPAR = 0 ==> Parity error!\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_STAT_STATFIFOFULL) + puts(" | |-REG_SIM_STAT_STATFIFOFULL = 1 ==> Fifo full!\n"); + else + puts(" | |-REG_SIM_STAT_STATFIFOFULL = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_STAT_STATFIFOEMPTY) + puts(" | |-REG_SIM_STAT_STATFIFOEMPTY = 1 ==> Fifo empty!\n"); + else + puts(" | |-REG_SIM_STAT_STATFIFOEMPTY = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_CONF1); + printf(" |-REG_SIM_CONF1 = %04x\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFCHKPAR) + puts(" | |-REG_SIM_CONF1_CONFCHKPAR = 1 ==> Parity check on reception enabled.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFCHKPAR = 0 ==> Parity check on reception disabled.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFCODCONV) + puts(" | |-REG_SIM_CONF1_CONFCODCONV = 1 ==> Coding convention is inverse.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFCODCONV = 0 ==> Coding convention is direct (normal).\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFTXRX) + puts(" | |-REG_SIM_CONF1_CONFTXRX = 1 ==> SIO line direction is in transmit mode.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFTXRX = 0 ==> SIO line direction is in receive mode.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSCLKEN) + puts(" | |-REG_SIM_CONF1_CONFSCLKEN = 1 ==> SIM clock in normal mode.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSCLKEN = 0 ==> SIM clock in standby mode.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_reserved) + puts(" | |-REG_SIM_CONF1_reserved = 1 ==> ETU period is 4*1/Fsclk.\n"); + else + puts(" | |-REG_SIM_CONF1_reserved = 0 ==> ETU period is CONFETUPERIOD.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSCLKDIV) + puts(" | |-REG_SIM_CONF1_CONFSCLKDIV = 1 ==> SIM clock frequency is 13/8 Mhz.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSCLKDIV = 0 ==> SIM clock frequency is 13/4 Mhz.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSCLKLEV) + puts(" | |-REG_SIM_CONF1_CONFSCLKLEV = 1 ==> SIM clock idle level is high.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSCLKLEV = 0 ==> SIM clock idle level is low.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFETUPERIOD) + puts(" | |-REG_SIM_CONF1_CONFETUPERIOD = 1 ==> ETU period is 512/8*1/Fsclk.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFETUPERIOD = 0 ==> ETU period is 372/8*1/Fsclk.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFBYPASS) + puts(" | |-REG_SIM_CONF1_CONFBYPASS = 1 ==> Hardware timers and start and stop sequences are bypassed.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFBYPASS = 0 ==> Hardware timers and start and stop sequences are normal.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSVCCLEV) + puts(" | |-REG_SIM_CONF1_CONFSVCCLEV = 1 ==> SVCC Level is high (Only valid when CONFBYPASS = 1).\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSVCCLEV = 0 ==> SVCC Level is low (Only valid when CONFBYPASS = 1).\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSRSTLEV) + puts(" | |-REG_SIM_CONF1_CONFSRSTLEV = 1 ==> SRST Level is high (Only valid when CONFBYPASS = 1).\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSRSTLEV = 0 ==> SRST Level is low (Only valid when CONFBYPASS = 1).\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + printf(" | |-REG_SIM_CONF1_CONFTRIG = 0x%x (FIFO trigger level)\n",(regVal >> REG_SIM_CONF1_CONFTRIG) & REG_SIM_CONF1_CONFTRIG_MASK); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_CONF1_CONFSIOLOW) + puts(" | |-REG_SIM_CONF1_CONFSIOLOW = 1 ==> I/O is forced to low.\n"); + else + puts(" | |-REG_SIM_CONF1_CONFSIOLOW = 0\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_CONF2); + printf(" |-REG_SIM_CONF2 = %04x\n", regVal); + printf(" | |-REG_SIM_CONF2_CONFTFSIM = 0x%x (time delay for filtering of SIM_CD)\n",(regVal >> REG_SIM_CONF2_CONFTFSIM) & REG_SIM_CONF2_CONFTFSIM_MASK); + printf(" | |-REG_SIM_CONF2_CONFTDSIM = 0x%x (time delay for contact activation/deactivation)\n",(regVal >> REG_SIM_CONF2_CONFTDSIM) & REG_SIM_CONF2_CONFTDSIM_MASK); + printf(" | |-REG_SIM_CONF2_CONFWAITI = 0x%x (CONFWAITI overflow wait time between two received chars)\n",(regVal >> REG_SIM_CONF2_CONFWAITI) & REG_SIM_CONF2_CONFWAITI_MASK); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_IT); + printf(" |-REG_SIM_IT = %04x\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_IT_SIM_NATR) + puts(" | |-REG_SIM_IT_SIM_NATR = 1 ==> No answer to reset!\n"); + else + puts(" | |-REG_SIM_IT_SIM_NATR = 0 ==> On read access to REG_SIM_IT.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_IT_SIM_WT) + puts(" | |-REG_SIM_IT_SIM_WT = 1 ==> Character underflow!\n"); + else + puts(" | |-REG_SIM_IT_SIM_WT = 0 ==> On read access to REG_SIM_IT.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_IT_SIM_OV) + puts(" | |-REG_SIM_IT_SIM_OV = 1 ==> Receive overflow!\n"); + else + puts(" | |-REG_SIM_IT_SIM_OV = 0 ==> On read access to REG_SIM_IT.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_IT_SIM_TX) + puts(" | |-REG_SIM_IT_SIM_TX = 1 ==> Waiting for character to transmit...\n"); + else + { + puts(" | |-REG_SIM_IT_SIM_TX = 0 ==> On write access to REG_SIM_DTX or on switching\n"); + puts(" | | from transmit to receive mode (CONFTXRX bit)\n"); + } + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_IT_SIM_RX) + puts(" | |-REG_SIM_IT_SIM_RX = 1 ==> Waiting characters to be read...\n"); + else + puts(" | |-REG_SIM_IT_SIM_RX = 0 ==> On read access to REG_SIM_DRX.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_DRX); + printf(" |-REG_SIM_DRX = %04x\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + printf(" | |-REG_SIM_DRX_SIM_DRX = 0x%x (next data byte in FIFO available for reading)\n",(regVal >> REG_SIM_DRX_SIM_DRX) & REG_SIM_DRX_SIM_DRX_MASK); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_DRX_STATRXPAR) + puts(" | |-REG_SIM_DRX_STATRXPAR = 1 ==> Parity Ok.\n"); + else + puts(" | |-REG_SIM_DRX_STATRXPAR = 0 ==> Parity error!\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_DTX); + printf(" |-REG_SIM_DTX = %02x (next data byte to be transmitted)\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = readw(REG_SIM_MASKIT); + printf(" |-REG_SIM_MASKIT = %04x\n", regVal); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_NATR) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_NATR = 1 ==> No-answer-to-reset interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_NATR = 0 ==> No-answer-to-reset interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_WT) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_WT = 1 ==> Character wait-time overflow interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_WT = 0 ==> Character wait-time overflow interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_OV) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_OV = 1 ==> Receive overflow interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_OV = 0 ==> Receive overflow interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_TX) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_TX = 1 ==> Waiting characters to be transmit interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_TX = 0 ==> Waiting characters to be transmit interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_RX) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_RX = 1 ==> Waiting characters to be read interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_RX = 0 ==> Waiting characters to be read interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + if(regVal & REG_SIM_MASKIT_MASK_SIM_CD) + puts(" | |-REG_SIM_MASKIT_MASK_SIM_CD = 1 ==> SIM card insertion/extraction interrupt is masked.\n"); + else + puts(" | |-REG_SIM_MASKIT_MASK_SIM_CD = 0 ==> SIM card insertion/extraction interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); + + regVal = REG_SIM_IT_CD; + printf(" |-REG_SIM_IT_CD = %04x\n", regVal); + if(regVal & REG_SIM_IT_CD_IT_CD) + puts(" |-REG_SIM_IT_CD_IT_CD = 1 ==> SIM card insertion/extraction interrupt is masked.\n"); + else + puts(" |-REG_SIM_IT_CD_IT_CD = 0 ==> SIM card insertion/extraction interrupt is unmasked.\n"); + delay_ms(SIM_DEBUG_OUTPUTDELAY); +#endif + return; +} + +/* Apply power to the simcard (use nullpointer to ignore atr) */ +int calypso_sim_powerup(uint8_t *atr) +{ + /* Enable level shifters and voltage regulator */ + twl3025_reg_write(VRPCSIM, VRPCSIM_SIMLEN | VRPCSIM_RSIMEN | VRPCSIM_SIMSEL); +#if (SIM_DEBUG == 1) + puts(" * Power enabled!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + /* Enable clock */ + writew(REG_SIM_CMD_MODULE_CLK_EN | REG_SIM_CMD_CMDSTART, REG_SIM_CMD); +#if (SIM_DEBUG == 1) + puts(" * Clock enabled!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + /* Release reset */ + writew(readw(REG_SIM_CONF1) | REG_SIM_CONF1_CONFBYPASS | REG_SIM_CONF1_CONFSRSTLEV | REG_SIM_CONF1_CONFSVCCLEV, REG_SIM_CONF1); +#if (SIM_DEBUG == 1) + puts(" * Reset released!\n"); +#endif + + /* Catch ATR */ + if(atr != 0) + return calypso_sim_receive(atr); + else + return 0; +} + + +/* Powerdown simcard */ +void calypso_sim_powerdown(void) +{ + writew(readw(REG_SIM_CONF1) & ~REG_SIM_CONF1_CONFBYPASS, REG_SIM_CONF1); +#if (SIM_DEBUG == 1) + puts(" * Reset pulled down!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + writew(REG_SIM_CMD_MODULE_CLK_EN | REG_SIM_CMD_CMDSTOP, REG_SIM_CMD); +#if (SIM_DEBUG == 1) + puts(" * Clock disabled!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + writew(0, REG_SIM_CMD); +#if (SIM_DEBUG == 1) + puts(" * Module disabled!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + /* Disable level shifters and voltage regulator */ + twl3025_reg_write(VRPCSIM, 0); +#if (SIM_DEBUG == 1) + puts(" * Power disabled!\n"); +#endif + delay_ms(SIM_OPERATION_DELAY); + + return; +} + +/* reset the simcard (see note 1) */ +int calypso_sim_reset(uint8_t *atr) +{ + + /* Pull reset down */ + writew(readw(REG_SIM_CONF1) & ~REG_SIM_CONF1_CONFSRSTLEV , REG_SIM_CONF1); +#if (SIM_DEBUG == 1) + puts(" * Reset pulled down!\n"); +#endif + + delay_ms(SIM_OPERATION_DELAY); + + /* Pull reset down */ + writew(readw(REG_SIM_CONF1) | REG_SIM_CONF1_CONFSRSTLEV , REG_SIM_CONF1); +#if (SIM_DEBUG == 1) + puts(" * Reset released!\n"); +#endif + + /* Catch ATR */ + if(atr != 0) + return calypso_sim_receive(atr); + else + return 0; +} + +/* Receive raw data through the sim interface */ +int calypso_sim_receive(uint8_t *data) +{ + /* Prepare buffers and flags */ + rx_buffer = data; + sim_rx_character_count = 0; + rxDoneFlag = 0; + + /* Switch I/O direction to input */ + writew(readw(REG_SIM_CONF1) & ~REG_SIM_CONF1_CONFTXRX, REG_SIM_CONF1); + + /* Unmask the interrupts that are needed to perform this action */ + writew(~(REG_SIM_MASKIT_MASK_SIM_RX | REG_SIM_MASKIT_MASK_SIM_WT), REG_SIM_MASKIT); + + /* Wait till rxDoneFlag is set */ + while(rxDoneFlag == 0); + + /* Disable all interrupt driven functions by masking all interrupts */ + writew(0xFF, REG_SIM_MASKIT); + + /* Hand back the number of bytes received */ + return sim_rx_character_count; + + return; +} + +/* Transmit raw data through the sim interface */ +int calypso_sim_transmit(uint8_t *data, int length) +{ + /* Prepare buffers and flags */ + tx_buffer = data; + sim_tx_character_count = 0; + txDoneFlag = 0; + sim_tx_character_length = length; + + /* Switch I/O direction to output */ + writew(readw(REG_SIM_CONF1) | REG_SIM_CONF1_CONFTXRX, REG_SIM_CONF1); + + /* Unmask the interrupts that are needed to perform this action */ + writew(~(REG_SIM_MASKIT_MASK_SIM_TX), REG_SIM_MASKIT); + + /* Transmit the first byte manually to start the interrupt cascade */ + writew(*tx_buffer,REG_SIM_DTX); + tx_buffer++; + sim_tx_character_count++; + + /* Wait till rxDoneFlag is set */ + while(txDoneFlag == 0); + + /* Disable all interrupt driven functions by masking all interrupts */ + writew(0xFF, REG_SIM_MASKIT); + + return 0; +} + + +/* IRQ-Handler for simcard interface */ +void sim_irq_handler(enum irq_nr irq) +{ + int regVal = readw(REG_SIM_IT); + + + /* Display interrupt information */ +#if (SIM_DEBUG == 1) + puts("SIM-ISR: Interrupt caught: "); +#endif + if(regVal & REG_SIM_IT_SIM_NATR) + { +#if (SIM_DEBUG == 1) + puts(" No answer to reset!\n"); +#endif + } + + /* Used by: calypso_sim_receive() to determine when the transmission is over */ + if(regVal & REG_SIM_IT_SIM_WT) + { +#if (SIM_DEBUG == 1) + puts(" Character underflow!\n"); +#endif + rxDoneFlag = 1; + } + + if(regVal & REG_SIM_IT_SIM_OV) + { +#if (SIM_DEBUG == 1) + puts(" Receive overflow!\n"); +#endif + } + + /* Used by: calypso_sim_transmit() to transmit the data */ + if(regVal & REG_SIM_IT_SIM_TX) + { +#if (SIM_DEBUG == 1) + puts(" Waiting for character to transmit...\n"); +#endif + if(sim_tx_character_count >= sim_tx_character_length) + txDoneFlag = 1; + else + { + writew(*tx_buffer,REG_SIM_DTX); + tx_buffer++; + sim_tx_character_count++; + } + } + + /* Used by: calypso_sim_receive() to receive the incoming data */ + if(regVal & REG_SIM_IT_SIM_RX) + { +#if (SIM_DEBUG == 1) + puts(" Waiting characters to be read...\n"); +#endif + /* Increment character count - this is what calypso_sim_receive() hands back */ + sim_rx_character_count++; + + /* Read byte from rx-fifo and write it to the issued buffer */ + *rx_buffer = (uint8_t) (readw(REG_SIM_DRX) & 0xFF); + rx_buffer++; + } +} + +/* Transceive T0 Apdu to sim acording to GSM 11.11 Page 34 */ +int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 */ + uint8_t ins, /* Instruction */ + uint8_t p1, /* First parameter */ + uint8_t p2, /* Second parameter */ + uint8_t p3le, /* Length of the data that should be transceived */ + uint8_t *data, /* Data payload */ + uint8_t *status, /* Status word (2 byte array, see note 1) */ + uint8_t mode) /* Mode of operation: 1=GET, 0=PUT */ + + /* Note 1: You can use a null-pointer (0) if you are not interested in + the status word */ +{ + uint8_t transmissionBuffer[256]; + uint8_t numberOfReceivedBytes; + +#if (SIM_DEBUG == 1) + printf("SIM-T0: Transceiving APDU-Header: (%02x %02x %02x %02x %02x)\n",cla,ins,p1,p2,p3le); +#endif + + /* Transmit APDU header */ + memset(transmissionBuffer,0,sizeof(transmissionBuffer)); + transmissionBuffer[0] = cla; + transmissionBuffer[1] = ins; + transmissionBuffer[2] = p1; + transmissionBuffer[3] = p2; + transmissionBuffer[4] = p3le; + calypso_sim_transmit(transmissionBuffer,5); + + /* Case 1: No input, No Output */ + if(p3le == 0) + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: Case 1: No input, No Output (See also GSM 11.11 Page 34)\n"); +#endif + numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer); + + if(numberOfReceivedBytes == 2) + { +#if (SIM_DEBUG == 1) + printf("SIM-T0: Status-word received: %02x %02x\n", transmissionBuffer[0], transmissionBuffer[1]); +#endif + /* Hand back status word */ + if(status != 0) + { + status[0] = transmissionBuffer[0]; + status[1] = transmissionBuffer[1]; + } + + return 0; + } + else + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error -- aborting!\n"); +#endif + return -1; + } + } + + /* Case 2: No input / Output of known length */ + else if(mode == SIM_APDU_PUT) + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: Case 2: No input / Output of known length (See also GSM 11.11 Page 34)\n"); +#endif + + numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer); + + /* Error situation: The card has aborted, sends no data but a status word */ + if(numberOfReceivedBytes == 2) + { +#if (SIM_DEBUG == 1) + printf("SIM-T0: Status-word received (ERROR): %02x %02x\n", transmissionBuffer[0], transmissionBuffer[1]); +#endif + /* Hand back status word */ + if(status != 0) + { + status[0] = transmissionBuffer[0]; + status[1] = transmissionBuffer[1]; + } + + return 0; + } + /* Acknoledge byte received */ + else if(numberOfReceivedBytes == 1) + { +#if (SIM_DEBUG == 1) + printf("SIM-T0: ACK received: %02x\n", transmissionBuffer[0]); +#endif + /* Check if ACK is valid */ + if(transmissionBuffer[0] != ins) + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Invalid ACK byte -- aborting!\n"); +#endif + return -1; + } + + /* Transmit body */ + calypso_sim_transmit(data,p3le); + + /* Receive status word */ + numberOfReceivedBytes = calypso_sim_receive(transmissionBuffer); + + /* Check status word */ + if(numberOfReceivedBytes == 2) + { +#if (SIM_DEBUG == 1) + printf("SIM-T0: Status-word received: %02x %02x\n", transmissionBuffer[0], transmissionBuffer[1]); +#endif + + /* Hand back status word */ + if(status != 0) + { + status[0] = transmissionBuffer[0]; + status[1] = transmissionBuffer[1]; + } + + return 0; + } + else + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Missing or invalid status word -- aborting!\n"); +#endif + return -1; + } + } + else + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Missing ACK byte -- aborting!\n"); +#endif + return -1; + } + } + + /* Case 4: Input / No output */ + else if(mode == SIM_APDU_GET) + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: Case 4: Input / No output (See also GSM 11.11 Page 34)\n"); +#endif + + numberOfReceivedBytes = calypso_sim_receive(data); + + /* Error situation: The card has aborted, sends no data but a status word */ + if(numberOfReceivedBytes == 2) + { + +#if (SIM_DEBUG == 1) + printf("SIM-T0: Status-word received (ERROR): %02x %02x\n", data[0], data[1]); +#endif + /* Hand back status word */ + if(status != 0) + { + status[0] = data[0]; + status[1] = data[1]; + } + + return 0; + } + + /* Data correctly received */ + else if(numberOfReceivedBytes == p3le + 1 + 2) + { +#if (SIM_DEBUG == 1) + printf("SIM-T0: ACK received: %02x\n", data[0]); +#endif + /* Check if ACK is valid */ + if(data[0] != ins) + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Invalid ACK byte -- aborting!\n"); +#endif + return -1; + } + +#if (SIM_DEBUG == 1) + printf("SIM-T0: Status-word received: %02x %02x\n", data[p3le + 1], data[p3le + 2]); +#endif + /* Hand back status word */ + if(status != 0) + { + status[0] = data[p3le + 1]; + status[1] = data[p3le + 2]; + } + + /* Move data one position left to cut away the ACK-Byte */ + memcpy(data,data+1,p3le); + + return 0; + } + else + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Incorrect or missing answer -- aborting!\n"); +#endif + return -1; + } + } + + /* Should not happen, if it happens then the programmer has submitted invalid parameters! */ + else + { +#if (SIM_DEBUG == 1) + puts("SIM-T0: T0 Protocol error: Invalid case (program bug!) -- aborting!\n"); +#endif + } + + /* Note: The other cases are not implemented because they are already covered + by the CASE 1,2 and 4. */ + + return 0; +} + + +/* Initialize simcard interface */ +void calypso_sim_init(void) +{ + /* Register IRQ handler and turn interrupts on */ +#if (SIM_DEBUG == 1) + puts("SIM: Registering interrupt handler for simcard-interface\n"); +#endif + irq_register_handler(IRQ_SIMCARD, &sim_irq_handler); + irq_config(IRQ_SIMCARD, 0, 0, 0xff); + irq_enable(IRQ_SIMCARD); +} + diff --git a/src/target/firmware/calypso/spi.c b/src/target/firmware/calypso/spi.c new file mode 100644 index 00000000..049ac080 --- /dev/null +++ b/src/target/firmware/calypso/spi.c @@ -0,0 +1,141 @@ +/* Driver for SPI Master Controller inside TI Calypso */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +//#define DEBUG +#include <debug.h> + +#include <memory.h> +#include <spi.h> +#include <delay.h> + +#define BASE_ADDR_SPI 0xfffe3000 +#define SPI_REG(n) (BASE_ADDR_SPI+(n)) + +enum spi_regs { + REG_SET1 = 0x00, + REG_SET2 = 0x02, + REG_CTRL = 0x04, + REG_STATUS = 0x06, + REG_TX_LSB = 0x08, + REG_TX_MSB = 0x0a, + REG_RX_LSB = 0x0c, + REG_RX_MSB = 0x0e, +}; + +#define SPI_SET1_EN_CLK (1 << 0) +#define SPI_SET1_WR_IRQ_DIS (1 << 4) +#define SPI_SET1_RDWR_IRQ_DIS (1 << 5) + +#define SPI_CTRL_RDWR (1 << 0) +#define SPI_CTRL_WR (1 << 1) +#define SPI_CTRL_NB_SHIFT 2 +#define SPI_CTRL_AD_SHIFT 7 + +#define SPI_STATUS_RE (1 << 0) /* Read End */ +#define SPI_STATUS_WE (1 << 1) /* Write End */ + +void spi_init(void) +{ + writew(SPI_SET1_EN_CLK | SPI_SET1_WR_IRQ_DIS | SPI_SET1_RDWR_IRQ_DIS, + SPI_REG(REG_SET1)); + + writew(0x0001, SPI_REG(REG_SET2)); +} + +int spi_xfer(uint8_t dev_idx, uint8_t bitlen, const void *dout, void *din) +{ + uint8_t bytes_per_xfer; + uint8_t reg_status, reg_ctrl = 0; + uint32_t tmp; + + if (bitlen == 0) + return 0; + + if (bitlen > 32) + return -1; + + if (dev_idx > 4) + return -1; + + bytes_per_xfer = bitlen / 8; + if (bitlen % 8) + bytes_per_xfer ++; + + reg_ctrl |= (bitlen - 1) << SPI_CTRL_NB_SHIFT; + reg_ctrl |= (dev_idx & 0x7) << SPI_CTRL_AD_SHIFT; + + if (bitlen <= 8) { + tmp = *(uint8_t *)dout; + tmp <<= 24 + (8-bitlen); /* align to MSB */ + } else if (bitlen <= 16) { + tmp = *(uint16_t *)dout; + tmp <<= 16 + (16-bitlen); /* align to MSB */ + } else { + tmp = *(uint32_t *)dout; + tmp <<= (32-bitlen); /* align to MSB */ + } + printd("spi_xfer(dev_idx=%u, bitlen=%u, data_out=0x%08x): ", + dev_idx, bitlen, tmp); + + /* fill transmit registers */ + writew(tmp >> 16, SPI_REG(REG_TX_MSB)); + writew(tmp & 0xffff, SPI_REG(REG_TX_LSB)); + + /* initiate transfer */ + if (din) + reg_ctrl |= SPI_CTRL_RDWR; + else + reg_ctrl |= SPI_CTRL_WR; + writew(reg_ctrl, SPI_REG(REG_CTRL)); + printd("reg_ctrl=0x%04x ", reg_ctrl); + + /* wait until the transfer is complete */ + while (1) { + reg_status = readw(SPI_REG(REG_STATUS)); + printd("status=0x%04x ", reg_status); + if (din && (reg_status & SPI_STATUS_RE)) + break; + else if (reg_status & SPI_STATUS_WE) + break; + } + /* FIXME: calibrate how much delay we really need (seven 13MHz cycles) */ + delay_ms(1); + + if (din) { + tmp = readw(SPI_REG(REG_RX_MSB)) << 16; + tmp |= readw(SPI_REG(REG_RX_LSB)); + printd("data_in=0x%08x ", tmp); + + if (bitlen <= 8) + *(uint8_t *)din = tmp & 0xff; + else if (bitlen <= 16) + *(uint16_t *)din = tmp & 0xffff; + else + *(uint32_t *)din = tmp; + } + dputchar('\n'); + + return 0; +} diff --git a/src/target/firmware/calypso/timer.c b/src/target/firmware/calypso/timer.c new file mode 100644 index 00000000..1dd55f26 --- /dev/null +++ b/src/target/firmware/calypso/timer.c @@ -0,0 +1,156 @@ +/* Calypso DBB internal Timer Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <memory.h> +#include <stdint.h> + +#include <defines.h> + +#include <calypso/timer.h> +#include <calypso/irq.h> + +#define BASE_ADDR_TIMER 0xfffe3800 +#define TIMER2_OFFSET 0x3000 + +#define TIMER_REG(n, m) (((n)-1) ? (BASE_ADDR_TIMER + TIMER2_OFFSET + (m)) : (BASE_ADDR_TIMER + (m))) + +enum timer_reg { + CNTL_TIMER = 0x00, + LOAD_TIMER = 0x02, + READ_TIMER = 0x04, +}; + +enum timer_ctl { + CNTL_START = (1 << 0), + CNTL_AUTO_RELOAD = (1 << 1), + CNTL_CLOCK_ENABLE = (1 << 5), +}; + +/* Regular Timers (1 and 2) */ + +void hwtimer_enable(int num, int on) +{ + uint8_t ctl; + + if (num < 1 || num > 2) { + printf("Unknown timer %u\n", num); + return; + } + + ctl = readb(TIMER_REG(num, CNTL_TIMER)); + if (on) + ctl |= CNTL_START|CNTL_CLOCK_ENABLE; + else + ctl &= ~CNTL_START; + writeb(ctl, TIMER_REG(num, CNTL_TIMER)); +} + +void hwtimer_config(int num, uint8_t pre_scale, int auto_reload) +{ + uint8_t ctl; + + ctl = (pre_scale & 0x7) << 2; + if (auto_reload) + ctl |= CNTL_AUTO_RELOAD; + + writeb(ctl, TIMER_REG(num, CNTL_TIMER)); +} + +void hwtimer_load(int num, uint16_t val) +{ + writew(val, TIMER_REG(num, LOAD_TIMER)); +} + +uint16_t hwtimer_read(int num) +{ + uint8_t ctl = readb(TIMER_REG(num, CNTL_TIMER)); + + /* somehow a read results in an abort */ + if ((ctl & (CNTL_START|CNTL_CLOCK_ENABLE)) != (CNTL_START|CNTL_CLOCK_ENABLE)) + return 0xFFFF; + return readw(TIMER_REG(num, READ_TIMER)); +} + +void hwtimer_init(void) +{ + writeb(CNTL_CLOCK_ENABLE, TIMER_REG(1, CNTL_TIMER)); + writeb(CNTL_CLOCK_ENABLE, TIMER_REG(2, CNTL_TIMER)); +} + +/* Watchdog Timer */ + +#define BASE_ADDR_WDOG 0xfffff800 +#define WDOG_REG(m) (BASE_ADDR_WDOG + m) + +enum wdog_reg { + WD_CNTL_TIMER = CNTL_TIMER, + WD_LOAD_TIMER = LOAD_TIMER, + WD_READ_TIMER = 0x02, + WD_MODE = 0x04, +}; + +enum wdog_ctl { + WD_CTL_START = (1 << 7), + WD_CTL_AUTO_RELOAD = (1 << 8) +}; + +enum wdog_mode { + WD_MODE_DIS_ARM = 0xF5, + WD_MODE_DIS_CONFIRM = 0xA0, + WD_MODE_ENABLE = (1 << 15) +}; + +#define WD_CTL_PRESCALE(value) (((value)&0x07) << 9) + +static void wdog_irq(__unused enum irq_nr nr) +{ + puts("=> WATCHDOG\n"); +} + +void wdog_enable(int on) +{ + if (on) { + irq_config(IRQ_WATCHDOG, 0, 0, 0); + irq_register_handler(IRQ_WATCHDOG, &wdog_irq); + irq_enable(IRQ_WATCHDOG); + writew(WD_MODE_ENABLE, WDOG_REG(WD_MODE)); + } else { + writew(WD_MODE_DIS_ARM, WDOG_REG(WD_MODE)); + writew(WD_MODE_DIS_CONFIRM, WDOG_REG(WD_MODE)); + } +} + +void wdog_reset(void) +{ +#if 0 + // XXX: this is supposed to reset immediately but does not seem to + writew(0xF5, WDOG_REG(WD_MODE)); + writew(0xFF, WDOG_REG(WD_MODE)); +#else + // enable watchdog + writew(WD_MODE_ENABLE, WDOG_REG(WD_MODE)); + // force expiration + writew(0x0000, WDOG_REG(WD_LOAD_TIMER)); + writew(0x0000, WDOG_REG(WD_LOAD_TIMER)); +#endif +} diff --git a/src/target/firmware/calypso/tpu.c b/src/target/firmware/calypso/tpu.c new file mode 100644 index 00000000..ed7aea74 --- /dev/null +++ b/src/target/firmware/calypso/tpu.c @@ -0,0 +1,346 @@ +/* Calypso DBB internal TPU (Time Processing Unit) Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> + +/* Using TPU_DEBUG you will send special HLDC messages to the host PC + * containing the full TPU RAM content at the time you call tpu_enable() */ +//#define TPU_DEBUG + +#define BASE_ADDR_TPU 0xffff1000 +#define TPU_REG(x) (BASE_ADDR_TPU+(x)) + +#define BASE_ADDR_TPU_RAM 0xffff9000 +#define TPU_RAM_END 0xffff97ff + +enum tpu_reg_arm { + TPU_CTRL = 0x0, /* Control & Status Register */ + INT_CTRL = 0x2, /* Interrupt Control Register */ + INT_STAT = 0x4, /* Interrupt Status Register */ + TPU_OFFSET = 0xC, /* Offset operand value register */ + TPU_SYNCHRO = 0xE, /* synchro operand value register */ + IT_DSP_PG = 0x20, +}; + +enum tpu_ctrl_bits { + TPU_CTRL_RESET = (1 << 0), + TPU_CTRL_PAGE = (1 << 1), + TPU_CTRL_EN = (1 << 2), + /* unused */ + TPU_CTRL_DSP_EN = (1 << 4), + /* unused */ + TPU_CTRL_MCU_RAM_ACC = (1 << 6), + TPU_CTRL_TSP_RESET = (1 << 7), + TPU_CTRL_IDLE = (1 << 8), + TPU_CTRL_WAIT = (1 << 9), + TPU_CTRL_CK_ENABLE = (1 << 10), + TPU_CTRL_FULL_WRITE = (1 << 11), +}; + +enum tpu_int_ctrl_bits { + ICTRL_MCU_FRAME = (1 << 0), + ICTRL_MCU_PAGE = (1 << 1), + ICTRL_DSP_FRAME = (1 << 2), + ICTRL_DSP_FRAME_FORCE = (1 << 3), +}; + +static uint16_t *tpu_ptr = (uint16_t *)BASE_ADDR_TPU_RAM; + +#ifdef TPU_DEBUG +#include <comm/sercomm.h> +#include <layer1/sync.h> +static void tpu_ram_read_en(int enable) +{ + uint16_t reg; + + reg = readw(TPU_REG(TPU_CTRL)); + if (enable) + reg |= TPU_CTRL_MCU_RAM_ACC; + else + reg &= ~TPU_CTRL_MCU_RAM_ACC; + writew(reg, TPU_REG(TPU_CTRL)); +} + +static void tpu_debug(void) +{ + uint16_t *tpu_base = (uint16_t *)BASE_ADDR_TPU_RAM; + unsigned int tpu_size = tpu_ptr - tpu_base; + struct msgb *msg = sercomm_alloc_msgb(tpu_size*2); + uint16_t *data; + uint32_t *fn; + uint16_t reg; + int i; + + /* prepend tpu memory dump with frame number */ + fn = (uint32_t *) msgb_put(msg, sizeof(fn)); + *fn = l1s.current_time.fn; + + tpu_ram_read_en(1); + + data = (uint16_t *) msgb_put(msg, tpu_size*2); + for (i = 0; i < tpu_size; i ++) + data[i] = tpu_base[i]; + + tpu_ram_read_en(0); + + sercomm_sendmsg(SC_DLCI_DEBUG, msg); +} +#else +static void tpu_debug(void) { } +#endif + +#define BIT_SET 1 +#define BIT_CLEAR 0 + +/* wait for a certain control bit to be set */ +static int tpu_wait_ctrl_bit(uint16_t bit, int set) +{ + int timeout = 10*1000; + + while (1) { + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + if (set) { + if (reg & bit) + break; + } else { + if (!(reg & bit)) + break; + } + timeout--; + if (timeout <= 0) { + puts("Timeout while waiting for TPU ctrl bit!\n"); + return -1; + } + } + + return 0; +} + +/* assert or de-assert TPU reset */ +void tpu_reset(int active) +{ + uint16_t reg; + + printd("tpu_reset(%u)\n", active); + reg = readw(TPU_REG(TPU_CTRL)); + if (active) { + reg |= (TPU_CTRL_RESET|TPU_CTRL_TSP_RESET); + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_SET); + } else { + reg &= ~(TPU_CTRL_RESET|TPU_CTRL_TSP_RESET); + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_CLEAR); + } +} + +/* Enable or Disable a new scenario loaded into the TPU */ +void tpu_enable(int active) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + printd("tpu_enable(%u)\n", active); + + tpu_debug(); + + if (active) + reg |= TPU_CTRL_EN; + else + reg &= ~TPU_CTRL_EN; + writew(reg, TPU_REG(TPU_CTRL)); + + /* After the new scenario is loaded, TPU switches the MCU-visible memory + * page, i.e. we can write without any danger */ + tpu_rewind(); +#if 0 + { + int i; + uint16_t oldreg = 0; + + for (i = 0; i < 100000; i++) { + reg = readw(TPU_REG(TPU_CTRL)); + if (i == 0 || oldreg != reg) { + printd("%d TPU state: 0x%04x\n", i, reg); + } + oldreg = reg; + } + } +#endif +} + +/* Enable or Disable the clock of teh TPU Module */ +void tpu_clk_enable(int active) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + printd("tpu_clk_enable(%u)\n", active); + if (active) { + reg |= TPU_CTRL_CK_ENABLE; + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_SET); + } else { + reg &= ~TPU_CTRL_CK_ENABLE; + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_CLEAR); + } +} + +/* Enable Frame Interrupt generation on next frame. DSP will reset it */ +void tpu_dsp_frameirq_enable(void) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + reg |= TPU_CTRL_DSP_EN; + writew(reg, TPU_REG(TPU_CTRL)); + + tpu_wait_ctrl_bit(TPU_CTRL_DSP_EN, BIT_SET); +} + +/* Is a Frame interrupt still pending for the DSP ? */ +int tpu_dsp_fameirq_pending(void) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + if (reg & TPU_CTRL_DSP_EN) + return 1; + + return 0; +} + +void tpu_rewind(void) +{ + dputs("tpu_rewind()\n"); + tpu_ptr = (uint16_t *) BASE_ADDR_TPU_RAM; +} + +void tpu_enqueue(uint16_t instr) +{ + printd("tpu_enqueue(tpu_ptr=%p, instr=0x%04x)\n", tpu_ptr, instr); + *tpu_ptr++ = instr; + if (tpu_ptr > (uint16_t *) TPU_RAM_END) + puts("TPU enqueue beyond end of TPU memory\n"); +} + +void tpu_init(void) +{ + uint16_t *ptr; + + /* Put TPU into Reset and enable clock */ + tpu_reset(1); + tpu_clk_enable(1); + + /* set all TPU RAM to zero */ + for (ptr = (uint16_t *) BASE_ADDR_TPU_RAM; ptr < (uint16_t *) TPU_RAM_END; ptr++) + *ptr = 0x0000; + + /* Get TPU out of reset */ + tpu_reset(0); + /* Disable all interrupts */ + writeb(0x7, TPU_REG(INT_CTRL)); + + tpu_rewind(); + tpu_enq_offset(0); + tpu_enq_sync(0); +} + +void tpu_test(void) +{ + int i; + + /* program a sequence of TSPACT events into the TPU */ + for (i = 0; i < 10; i++) { + puts("TSP ACT enable: "); + tsp_act_enable(0x0001); + tpu_enq_wait(10); + puts("TSP ACT disable: "); + tsp_act_disable(0x0001); + tpu_enq_wait(10); + } + tpu_enq_sleep(); + + /* tell the chip to execute the scenario */ + tpu_enable(1); +} + +void tpu_wait_idle(void) +{ + dputs("Waiting for TPU Idle "); + /* Wait until TPU is doing something */ + delay_us(3); + /* Wait until TPU is idle */ + while (readw(TPU_REG(TPU_CTRL)) & TPU_CTRL_IDLE) + dputchar('.'); + dputs("Done!\n"); +} + +void tpu_frame_irq_en(int mcu, int dsp) +{ + uint8_t reg = readb(TPU_REG(INT_CTRL)); + if (mcu) + reg &= ~ICTRL_MCU_FRAME; + else + reg |= ICTRL_MCU_FRAME; + + if (dsp) + reg &= ~ICTRL_DSP_FRAME; + else + reg |= ICTRL_DSP_FRAME; + + writeb(reg, TPU_REG(INT_CTRL)); +} + +void tpu_force_dsp_frame_irq(void) +{ + uint8_t reg = readb(TPU_REG(INT_CTRL)); + reg |= ICTRL_DSP_FRAME_FORCE; + writeb(reg, TPU_REG(INT_CTRL)); +} + +uint16_t tpu_get_offset(void) +{ + return readw(TPU_REG(TPU_OFFSET)); +} + +uint16_t tpu_get_synchro(void) +{ + return readw(TPU_REG(TPU_SYNCHRO)); +} + +/* add two numbers, modulo 5000, and ensure the result is positive */ +uint16_t add_mod5000(int16_t a, int16_t b) +{ + int32_t sum = (int32_t)a + (int32_t)b; + + sum %= 5000; + + /* wrap around zero */ + if (sum < 0) + sum += 5000; + + return sum; +} diff --git a/src/target/firmware/calypso/tsp.c b/src/target/firmware/calypso/tsp.c new file mode 100644 index 00000000..5d24f48e --- /dev/null +++ b/src/target/firmware/calypso/tsp.c @@ -0,0 +1,121 @@ +/* Calypso DBB internal TSP (Time Serial Port) Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> + +static uint16_t tspact_state; + +/* initiate a TSP write through the TPU */ +void tsp_write(uint8_t dev_idx, uint8_t bitlen, uint32_t dout) +{ + if (bitlen <= 8) { + tpu_enq_move(TPUI_TX_1, dout & 0xff); + } else if (bitlen <= 16) { + tpu_enq_move(TPUI_TX_1, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_2, dout & 0xff); + } else if (bitlen <= 24) { + tpu_enq_move(TPUI_TX_1, (dout >> 16) & 0xff); + tpu_enq_move(TPUI_TX_2, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_3, dout & 0xff); + } else { + tpu_enq_move(TPUI_TX_1, (dout >> 24) & 0xff); + tpu_enq_move(TPUI_TX_2, (dout >> 16) & 0xff); + tpu_enq_move(TPUI_TX_3, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_4, dout & 0xff); + } + tpu_enq_move(TPUI_TSP_CTRL1, (dev_idx << 5) | (bitlen - 1)); + tpu_enq_move(TPUI_TSP_CTRL2, TPUI_CTRL2_WR); +} + +/* Configure clock edge and chip enable polarity for a device */ +void tsp_setup(uint8_t dev_idx, int clk_rising, int en_positive, int en_edge) +{ + uint8_t reg = TPUI_TSP_SET1 + (dev_idx / 2); + uint8_t val = 0; + uint8_t shift; + + if (dev_idx & 1) + shift = 4; + else + shift = 0; + + if (clk_rising) + val |= 1; + if (en_positive) + val |= 2; + if (en_edge) + val |= 4; + + tpu_enq_move(reg, (val << shift)); +} + +/* Update the TSPACT state, including enable and disable */ +void tsp_act_update(uint16_t new_act) +{ + uint8_t low = new_act & 0xff; + uint8_t high = new_act >> 8; + + if (low != (tspact_state & 0xff)) + tpu_enq_move(TPUI_TSP_ACT_L, low); + if (high != (tspact_state >> 8)) + tpu_enq_move(TPUI_TSP_ACT_U, high); + + tspact_state = new_act; +} + +/* Enable one or multiple TSPACT signals */ +void tsp_act_enable(uint16_t bitmask) +{ + uint16_t new_act = tspact_state | bitmask; + tsp_act_update(new_act); +} + +/* Disable one or multiple TSPACT signals */ +void tsp_act_disable(uint16_t bitmask) +{ + uint16_t new_act = tspact_state & ~bitmask; + tsp_act_update(new_act); +} + +/* Obtain the current tspact state */ +uint16_t tsp_act_state(void) +{ + return tspact_state; +} + +/* Toggle one or multiple TSPACT signals */ +void tsp_act_toggle(uint16_t bitmask) +{ + uint16_t new_act = tspact_state ^ bitmask; + tsp_act_update(new_act); +} + +void tsp_init(void) +{ + tsp_act_update(0); +} diff --git a/src/target/firmware/calypso/uart.c b/src/target/firmware/calypso/uart.c new file mode 100644 index 00000000..394078de --- /dev/null +++ b/src/target/firmware/calypso/uart.c @@ -0,0 +1,440 @@ +/* Calypso DBB internal UART Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <debug.h> + +#include <memory.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include <defines.h> +#include <console.h> +#include <comm/sercomm.h> + +#include <calypso/irq.h> +#include <calypso/uart.h> + +#define BASE_ADDR_UART_MODEM 0xffff5000 +#define OFFSET_IRDA 0x800 + +#define UART_REG(n,m) (BASE_ADDR_UART_MODEM + ((n)*OFFSET_IRDA)+(m)) + +#define LCR7BIT 0x80 +#define LCRBFBIT 0x40 +#define MCR6BIT 0x20 +#define REG_OFFS(m) ((m) & ~(LCR7BIT|LCRBFBIT|MCR6BIT)) +/* read access LCR[7] = 0 */ +enum uart_reg { + RHR = 0, + IER = 1, + IIR = 2, + LCR = 3, + MCR = 4, + LSR = 5, + MSR = 6, + SPR = 7, + MDR1 = 8, + DMR2 = 9, + SFLSR = 0x0a, + RESUME = 0x0b, + SFREGL = 0x0c, + SFREGH = 0x0d, + BLR = 0x0e, + ACREG = 0x0f, + SCR = 0x10, + SSR = 0x11, + EBLR = 0x12, +/* read access LCR[7] = 1 */ + DLL = RHR | LCR7BIT, + DLH = IER | LCR7BIT, + DIV1_6 = ACREG | LCR7BIT, +/* read/write access LCR[7:0] = 0xbf */ + EFR = IIR | LCRBFBIT, + XON1 = MCR | LCRBFBIT, + XON2 = LSR | LCRBFBIT, + XOFF1 = MSR | LCRBFBIT, + XOFF2 = SPR | LCRBFBIT, +/* read/write access if EFR[4] = 1 and MCR[6] = 1 */ + TCR = MSR | MCR6BIT, + TLR = SPR | MCR6BIT, +}; +/* write access LCR[7] = 0 */ +#define THR RHR +#define FCR IIR /* only if EFR[4] = 1 */ +#define TXFLL SFLSR +#define TXFLH RESUME +#define RXFLL SFREGL +#define RXFLH SFREGH + +enum fcr_bits { + FIFO_EN = (1 << 0), + RX_FIFO_CLEAR = (1 << 1), + TX_FIFO_CLEAR = (1 << 2), + DMA_MODE = (1 << 3), +}; +#define TX_FIFO_TRIG_SHIFT 4 +#define RX_FIFO_TRIG_SHIFT 6 + +enum iir_bits { + IIR_INT_PENDING = 0x01, + IIR_INT_TYPE = 0x3E, + IIR_INT_TYPE_RX_STATUS_ERROR = 0x06, + IIR_INT_TYPE_RX_TIMEOUT = 0x0C, + IIR_INT_TYPE_RHR = 0x04, + IIR_INT_TYPE_THR = 0x02, + IIR_INT_TYPE_MSR = 0x00, + IIR_INT_TYPE_XOFF = 0x10, + IIR_INT_TYPE_FLOW = 0x20, + IIR_FCR0_MIRROR = 0xC0, +}; + +#define UART_REG_UIR 0xffff6000 + +/* enable or disable the divisor latch for access to DLL, DLH */ +static void uart_set_lcr7bit(int uart, int on) +{ + uint8_t reg; + + reg = readb(UART_REG(uart, LCR)); + if (on) + reg |= (1 << 7); + else + reg &= ~(1 << 7); + writeb(reg, UART_REG(uart, LCR)); +} + +static uint8_t old_lcr; +static void uart_set_lcr_bf(int uart, int on) +{ + if (on) { + old_lcr = readb(UART_REG(uart, LCR)); + writeb(0xBF, UART_REG(uart, LCR)); + } else { + writeb(old_lcr, UART_REG(uart, LCR)); + } +} + +/* Enable or disable the TCR_TLR latch bit in MCR[6] */ +static void uart_set_mcr6bit(int uart, int on) +{ + uint8_t mcr; + /* we assume EFR[4] is always set to 1 */ + mcr = readb(UART_REG(uart, MCR)); + if (on) + mcr |= (1 << 6); + else + mcr &= ~(1 << 6); + writeb(mcr, UART_REG(uart, MCR)); +} + +static void uart_reg_write(int uart, enum uart_reg reg, uint8_t val) +{ + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 1); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 1); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 1); + + writeb(val, UART_REG(uart, REG_OFFS(reg))); + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 0); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 0); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 0); +} + +/* read from a UART register, applying any required latch bits */ +static uint8_t uart_reg_read(int uart, enum uart_reg reg) +{ + uint8_t ret; + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 1); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 1); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 1); + + ret = readb(UART_REG(uart, REG_OFFS(reg))); + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 0); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 0); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 0); + + return ret; +} + +static void uart_irq_handler_cons(__unused enum irq_nr irqnr) +{ + const uint8_t uart = CONS_UART_NR; + uint8_t iir; + + //uart_putchar_nb(uart, 'U'); + + iir = uart_reg_read(uart, IIR); + if (iir & IIR_INT_PENDING) + return; + + switch (iir & IIR_INT_TYPE) { + case IIR_INT_TYPE_RHR: + break; + case IIR_INT_TYPE_THR: + if (cons_rb_flush() == 1) { + /* everything was flushed, disable THR IRQ */ + uint8_t ier = uart_reg_read(uart, IER); + ier &= ~(1 << 1); + uart_reg_write(uart, IER, ier); + } + break; + case IIR_INT_TYPE_MSR: + break; + case IIR_INT_TYPE_RX_STATUS_ERROR: + break; + case IIR_INT_TYPE_RX_TIMEOUT: + break; + case IIR_INT_TYPE_XOFF: + break; + } +} + +static void uart_irq_handler_sercomm(__unused enum irq_nr irqnr) +{ + const uint8_t uart = SERCOMM_UART_NR; + uint8_t iir, ch; + + //uart_putchar_nb(uart, 'U'); + + iir = uart_reg_read(uart, IIR); + if (iir & IIR_INT_PENDING) + return; + + switch (iir & IIR_INT_TYPE) { + case IIR_INT_TYPE_RX_TIMEOUT: + case IIR_INT_TYPE_RHR: + /* as long as we have rx data available */ + while (uart_getchar_nb(uart, &ch)) { + if (sercomm_drv_rx_char(ch) < 0) { + /* sercomm cannot receive more data right now */ + uart_irq_enable(uart, UART_IRQ_RX_CHAR, 0); + } + } + break; + case IIR_INT_TYPE_THR: + /* as long as we have space in the FIFO */ + while (!uart_tx_busy(uart)) { + /* get a byte from sercomm */ + if (!sercomm_drv_pull(&ch)) { + /* no more bytes in sercomm, stop TX interrupts */ + uart_irq_enable(uart, UART_IRQ_TX_EMPTY, 0); + break; + } + /* write the byte into the TX FIFO */ + uart_putchar_nb(uart, ch); + } + break; + case IIR_INT_TYPE_MSR: + printf("UART IRQ MSR\n"); + break; + case IIR_INT_TYPE_RX_STATUS_ERROR: + printf("UART IRQ RX_SE\n"); + break; + case IIR_INT_TYPE_XOFF: + printf("UART IRQXOFF\n"); + break; + } +} + +static const uint8_t uart2irq[] = { + [0] = IRQ_UART_IRDA, + [1] = IRQ_UART_MODEM, +}; + +void uart_init(uint8_t uart, uint8_t interrupts) +{ + uint8_t irq = uart2irq[uart]; + + uart_reg_write(uart, IER, 0x00); + if (uart == CONS_UART_NR) { + cons_init(); + if(interrupts) { + irq_register_handler(irq, &uart_irq_handler_cons); + irq_config(irq, 0, 0, 0xff); + irq_enable(irq); + } + } else { + sercomm_init(); + if(interrupts) { + irq_register_handler(irq, &uart_irq_handler_sercomm); + irq_config(irq, 0, 0, 0xff); + irq_enable(irq); + } + uart_irq_enable(uart, UART_IRQ_RX_CHAR, 1); + } +#if 0 + if (uart == 1) { + /* assign UART to MCU and unmask interrupts*/ + writeb(UART_REG_UIR, 0x00); + } +#endif + + /* if we don't initialize these, we get strange corruptions in the + received data... :-( */ + uart_reg_write(uart, MDR1, 0x07); /* turn off UART */ + uart_reg_write(uart, XON1, 0x00); /* Xon1/Addr Register */ + uart_reg_write(uart, XON2, 0x00); /* Xon2/Addr Register */ + uart_reg_write(uart, XOFF1, 0x00); /* Xoff1 Register */ + uart_reg_write(uart, XOFF2, 0x00); /* Xoff2 Register */ + uart_reg_write(uart, EFR, 0x00); /* Enhanced Features Register */ + + /* select UART mode */ + uart_reg_write(uart, MDR1, 0); + /* no XON/XOFF flow control, ENHANCED_EN, no auto-RTS/CTS */ + uart_reg_write(uart, EFR, (1 << 4)); + /* enable Tx/Rx FIFO, Tx trigger at 56 spaces, Rx trigger at 60 chars */ + uart_reg_write(uart, FCR, FIFO_EN | RX_FIFO_CLEAR | TX_FIFO_CLEAR | + (3 << TX_FIFO_TRIG_SHIFT) | (3 << RX_FIFO_TRIG_SHIFT)); + + /* THR interrupt only when TX FIFO and TX shift register are empty */ + uart_reg_write(uart, SCR, (1 << 0));// | (1 << 3)); + + /* 8 bit, 1 stop bit, no parity, no break */ + uart_reg_write(uart, LCR, 0x03); + + uart_set_lcr7bit(uart, 0); +} + +void uart_poll(uint8_t uart) { + if(uart == CONS_UART_NR) { + uart_irq_handler_cons(0); + } else { + uart_irq_handler_sercomm(0); + } +} + +void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on) +{ + uint8_t ier = uart_reg_read(uart, IER); + uint8_t mask = 0; + + switch (irq) { + case UART_IRQ_TX_EMPTY: + mask = (1 << 1); + break; + case UART_IRQ_RX_CHAR: + mask = (1 << 0); + break; + } + + if (on) + ier |= mask; + else + ier &= ~mask; + + uart_reg_write(uart, IER, ier); +} + + +void uart_putchar_wait(uint8_t uart, int c) +{ + /* wait while TX FIFO indicates full */ + while (readb(UART_REG(uart, SSR)) & 0x01) { } + + /* put character in TX FIFO */ + writeb(c, UART_REG(uart, THR)); +} + +int uart_putchar_nb(uint8_t uart, int c) +{ + /* if TX FIFO indicates full, abort */ + if (readb(UART_REG(uart, SSR)) & 0x01) + return 0; + + writeb(c, UART_REG(uart, THR)); + return 1; +} + +int uart_getchar_nb(uint8_t uart, uint8_t *ch) +{ + uint8_t lsr; + + lsr = readb(UART_REG(uart, LSR)); + + /* something strange happened */ + if (lsr & 0x02) + printf("LSR RX_OE\n"); + if (lsr & 0x04) + printf("LSR RX_PE\n"); + if (lsr & 0x08) + printf("LSR RX_FE\n"); + if (lsr & 0x10) + printf("LSR RX_BI\n"); + if (lsr & 0x80) + printf("LSR RX_FIFO_STS\n"); + + /* is the Rx FIFO empty? */ + if (!(lsr & 0x01)) + return 0; + + *ch = readb(UART_REG(uart, RHR)); + //printf("getchar_nb(%u) = %02x\n", uart, *ch); + return 1; +} + +int uart_tx_busy(uint8_t uart) +{ + if (readb(UART_REG(uart, SSR)) & 0x01) + return 1; + return 0; +} + +static const uint16_t divider[] = { + [UART_38400] = 21, /* 38,690 */ + [UART_57600] = 14, /* 58,035 */ + [UART_115200] = 7, /* 116,071 */ + [UART_230400] = 4, /* 203,125! (-3% would be 223,488) */ + [UART_460800] = 2, /* 406,250! (-3% would be 446,976) */ + [UART_921600] = 1, /* 812,500! (-3% would be 893,952) */ +}; + +int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt) +{ + uint16_t div; + + if (bdrt > ARRAY_SIZE(divider)) + return -1; + + div = divider[bdrt]; + uart_set_lcr7bit(uart, 1); + writeb(div & 0xff, UART_REG(uart, DLL)); + writeb(div >> 8, UART_REG(uart, DLH)); + uart_set_lcr7bit(uart, 0); + + return 0; +} diff --git a/src/target/firmware/calypso/uwire.c b/src/target/firmware/calypso/uwire.c new file mode 100644 index 00000000..b79d9f38 --- /dev/null +++ b/src/target/firmware/calypso/uwire.c @@ -0,0 +1,136 @@ +/* Driver for uWire Master Controller inside TI Calypso */ + +/* (C) 2010 by Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +//#define DEBUG +#include <debug.h> + +#include <memory.h> +#include <uwire.h> +#include <delay.h> + +#define BASE_ADDR_UWIRE 0xfffe4000 +#define UWIRE_REG(n) (BASE_ADDR_UWIRE+(n)) + +enum uwire_regs { + REG_DATA = 0x00, + REG_CSR = 0x02, + REG_SR1 = 0x04, + REG_SR2 = 0x06, + REG_SR3 = 0x08, +}; + +#define UWIRE_CSR_BITS_RD(n) (((n) & 0x1f) << 0) +#define UWIRE_CSR_BITS_WR(n) (((n) & 0x1f) << 5) +#define UWIRE_CSR_IDX(n) (((n) & 3) << 10) +#define UWIRE_CSR_CS_CMD (1 << 12) +#define UWIRE_CSR_START (1 << 13) +#define UWIRE_CSR_CSRB (1 << 14) +#define UWIRE_CSR_RDRB (1 << 15) + +#define UWIRE_CSn_EDGE_RD (1 << 0) /* 1=falling 0=rising */ +#define UWIRE_CSn_EDGE_WR (1 << 1) /* 1=falling 0=rising */ +#define UWIRE_CSn_CS_LVL (1 << 2) +#define UWIRE_CSn_FRQ_DIV2 (0 << 3) +#define UWIRE_CSn_FRQ_DIV4 (1 << 3) +#define UWIRE_CSn_FRQ_DIV8 (2 << 3) +#define UWIRE_CSn_CKH + +#define UWIRE_CSn_SHIFT(n) (((n) & 1) ? 6 : 0) +#define UWIRE_CSn_REG(n) (((n) & 2) ? REG_SR2 : REG_SR1) + +#define UWIRE_SR3_CLK_EN (1 << 0) +#define UWIRE_SR3_CLK_DIV2 (0 << 1) +#define UWIRE_SR3_CLK_DIV4 (1 << 1) +#define UWIRE_SR3_CLK_DIV7 (2 << 1) +#define UWIRE_SR3_CLK_DIV10 (3 << 1) + +static inline void _uwire_wait(int mask, int val) +{ + while ((readw(UWIRE_REG(REG_CSR)) & mask) != val); +} + +void uwire_init(void) +{ + writew(UWIRE_SR3_CLK_EN | UWIRE_SR3_CLK_DIV2, UWIRE_REG(REG_SR3)); + /* FIXME only init CS0 for now */ + writew(((UWIRE_CSn_CS_LVL | UWIRE_CSn_FRQ_DIV2) << UWIRE_CSn_SHIFT(0)), + UWIRE_REG(UWIRE_CSn_REG(0))); + writew(UWIRE_CSR_IDX(0) | UWIRE_CSR_CS_CMD, UWIRE_REG(REG_CSR)); + _uwire_wait(UWIRE_CSR_CSRB, 0); +} + +int uwire_xfer(int cs, int bitlen, const void *dout, void *din) +{ + uint16_t tmp = 0; + + if (bitlen <= 0 || bitlen > 16) + return -1; + if (cs < 0 || cs > 4) + return -1; + + /* FIXME uwire_init always select CS0 for now */ + + printd("uwire_xfer(dev_idx=%u, bitlen=%u\n", cs, bitlen); + + /* select the chip */ + writew(UWIRE_CSR_IDX(0) | UWIRE_CSR_CS_CMD, UWIRE_REG(REG_CSR)); + _uwire_wait(UWIRE_CSR_CSRB, 0); + + if (dout) { + if (bitlen <= 8) + tmp = *(uint8_t *)dout; + else if (bitlen <= 16) + tmp = *(uint16_t *)dout; + tmp <<= 16 - bitlen; /* align to MSB */ + writew(tmp, UWIRE_REG(REG_DATA)); + printd(", data_out=0x%04hx", tmp); + } + + tmp = (dout ? UWIRE_CSR_BITS_WR(bitlen) : 0) | + (din ? UWIRE_CSR_BITS_RD(bitlen) : 0) | + UWIRE_CSR_START; + writew(tmp, UWIRE_REG(REG_CSR)); + + _uwire_wait(UWIRE_CSR_CSRB, 0); + + if (din) { + _uwire_wait(UWIRE_CSR_RDRB, UWIRE_CSR_RDRB); + + tmp = readw(UWIRE_REG(REG_DATA)); + printd(", data_in=0x%08x", tmp); + + if (bitlen <= 8) + *(uint8_t *)din = tmp & 0xff; + else if (bitlen <= 16) + *(uint16_t *)din = tmp & 0xffff; + } + /* unselect the chip */ + writew(UWIRE_CSR_IDX(0) | 0, UWIRE_REG(REG_CSR)); + _uwire_wait(UWIRE_CSR_CSRB, 0); + + printd(")\n"); + + return 0; +} diff --git a/src/target/firmware/comm/Makefile b/src/target/firmware/comm/Makefile new file mode 100644 index 00000000..25fbb983 --- /dev/null +++ b/src/target/firmware/comm/Makefile @@ -0,0 +1,5 @@ + +LIBRARIES+=comm +comm_DIR=comm +comm_SRCS=msgb.c sercomm.c sercomm_cons.c timer.c + diff --git a/src/target/firmware/comm/msgb.c b/src/target/firmware/comm/msgb.c new file mode 100644 index 00000000..d412844c --- /dev/null +++ b/src/target/firmware/comm/msgb.c @@ -0,0 +1,74 @@ +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> + +#include <debug.h> +#include <delay.h> + +#include <osmocore/msgb.h> + +#include <calypso/backlight.h> + +#define NO_TALLOC + +void *tall_msgb_ctx; + +#ifdef NO_TALLOC +/* This is a poor mans static allocator for msgb objects */ +#define MSGB_DATA_SIZE 256+4 +#define MSGB_NUM 32 +struct supermsg { + uint8_t allocated; + struct msgb msg; + uint8_t buf[MSGB_DATA_SIZE]; +}; +static struct supermsg msgs[MSGB_NUM]; +void *_talloc_zero(void *ctx, unsigned int size, const char *name) +{ + unsigned int i; + if (size > sizeof(struct msgb) + MSGB_DATA_SIZE) + goto panic; + + while (1) { + for (i = 0; i < ARRAY_SIZE(msgs); i++) { + if (!msgs[i].allocated) { + msgs[i].allocated = 1; + memset(&msgs[i].msg, 0, sizeof(&msgs[i].msg)); + memset(&msgs[i].buf, 0, sizeof(&msgs[i].buf)); + return &msgs[i].msg; + } + } + cons_puts("unable to allocate msgb\n"); + bl_level(++i % 50); + delay_ms(50); + } +panic: + return NULL; +} +void talloc_free(void *msg) +{ + struct supermsg *smsg = container_of(msg, struct supermsg, msg); + smsg->allocated = 0; +} +#endif diff --git a/src/target/firmware/comm/sercomm.c b/src/target/firmware/comm/sercomm.c new file mode 100644 index 00000000..c78b3623 --- /dev/null +++ b/src/target/firmware/comm/sercomm.c @@ -0,0 +1,277 @@ +/* Serial communications layer, based on HDLC */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <errno.h> + +#include <osmocore/msgb.h> + +#ifdef HOST_BUILD +#define SERCOMM_RX_MSG_SIZE 2048 +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#endif +#include <sercomm.h> +#define local_irq_save(x) (void) x +#define local_fiq_disable() +#define local_irq_restore(x) (void) x + +#else +#define SERCOMM_RX_MSG_SIZE 256 +#include <debug.h> +#include <osmocore/linuxlist.h> +#include <asm/system.h> + +#include <comm/sercomm.h> +#include <calypso/uart.h> +#endif + + +enum rx_state { + RX_ST_WAIT_START, + RX_ST_ADDR, + RX_ST_CTRL, + RX_ST_DATA, + RX_ST_ESCAPE, +}; + +static struct { + int initialized; + + /* transmit side */ + struct { + struct llist_head dlci_queues[_SC_DLCI_MAX]; + struct msgb *msg; + enum rx_state state; + uint8_t *next_char; + } tx; + + /* receive side */ + struct { + dlci_cb_t dlci_handler[_SC_DLCI_MAX]; + struct msgb *msg; + enum rx_state state; + uint8_t dlci; + uint8_t ctrl; + } rx; + +} sercomm; + +void sercomm_init(void) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) + INIT_LLIST_HEAD(&sercomm.tx.dlci_queues[i]); + + sercomm.rx.msg = NULL; + sercomm.initialized = 1; + + /* set up the echo dlci */ + sercomm_register_rx_cb(SC_DLCI_ECHO, &sercomm_sendmsg); +} + +int sercomm_initialized(void) +{ + return sercomm.initialized; +} + +/* user interface for transmitting messages for a given DLCI */ +void sercomm_sendmsg(uint8_t dlci, struct msgb *msg) +{ + unsigned long flags; + uint8_t *hdr; + + /* prepend address + control octet */ + hdr = msgb_push(msg, 2); + hdr[0] = dlci; + hdr[1] = HDLC_C_UI; + + /* This functiion can be called from any context: FIQ, IRQ + * and supervisor context. Proper locking is important! */ + local_irq_save(flags); + local_fiq_disable(); + msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg); + local_irq_restore(flags); + +#ifndef HOST_BUILD + /* tell UART that we have something to send */ + uart_irq_enable(SERCOMM_UART_NR, UART_IRQ_TX_EMPTY, 1); +#endif +} + +/* how deep is the Tx queue for a given DLCI */ +unsigned int sercomm_tx_queue_depth(uint8_t dlci) +{ + struct llist_head *le; + unsigned int num = 0; + + llist_for_each(le, &sercomm.tx.dlci_queues[dlci]) { + num++; + } + + return num; +} + +/* fetch one octet of to-be-transmitted serial data */ +int sercomm_drv_pull(uint8_t *ch) +{ + /* we are always called from interrupt context in this function, + * which means that any data structures we use need to be for + * our exclusive access */ + if (!sercomm.tx.msg) { + unsigned int i; + /* dequeue a new message from the queues */ + for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) { + sercomm.tx.msg = msgb_dequeue(&sercomm.tx.dlci_queues[i]); + if (sercomm.tx.msg) + break; + } + if (sercomm.tx.msg) { + /* start of a new message, send start flag octet */ + *ch = HDLC_FLAG; + sercomm.tx.next_char = sercomm.tx.msg->data; + return 1; + } else { + /* no more data avilable */ + return 0; + } + } + + if (sercomm.tx.state == RX_ST_ESCAPE) { + /* we've already transmitted the ESCAPE octet, + * we now need to trnsmit the escaped data */ + *ch = *sercomm.tx.next_char++; + sercomm.tx.state = RX_ST_DATA; + } else if (sercomm.tx.next_char >= sercomm.tx.msg->tail) { + /* last character has already been transmitted, + * send end-of-message octet */ + *ch = HDLC_FLAG; + /* we've reached the end of the message buffer */ + msgb_free(sercomm.tx.msg); + sercomm.tx.msg = NULL; + sercomm.tx.next_char = NULL; + /* escaping for the two control octets */ + } else if (*sercomm.tx.next_char == HDLC_FLAG || + *sercomm.tx.next_char == HDLC_ESCAPE || + *sercomm.tx.next_char == 0x00) { + /* send an escape octet */ + *ch = HDLC_ESCAPE; + /* invert bit 5 of the next octet to be sent */ + *sercomm.tx.next_char ^= (1 << 5); + sercomm.tx.state = RX_ST_ESCAPE; + } else { + /* standard case, simply send next octet */ + *ch = *sercomm.tx.next_char++; + } + return 1; +} + +/* register a handler for a given DLCI */ +int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb) +{ + if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler)) + return -EINVAL; + + if (sercomm.rx.dlci_handler[dlci]) + return -EBUSY; + + sercomm.rx.dlci_handler[dlci] = cb; + return 0; +} + +/* dispatch an incomnig message once it is completely received */ +static void dispatch_rx_msg(uint8_t dlci, struct msgb *msg) +{ + if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler) || + !sercomm.rx.dlci_handler[dlci]) { + msgb_free(msg); + return; + } + sercomm.rx.dlci_handler[dlci](dlci, msg); +} + +/* the driver has received one byte, pass it into sercomm layer */ +int sercomm_drv_rx_char(uint8_t ch) +{ + uint8_t *ptr; + + /* we are always called from interrupt context in this function, + * which means that any data structures we use need to be for + * our exclusive access */ + if (!sercomm.rx.msg) + sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE); + + if (msgb_tailroom(sercomm.rx.msg) == 0) { + //cons_puts("sercomm_drv_rx_char() overflow!\n"); + msgb_free(sercomm.rx.msg); + sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE); + sercomm.rx.state = RX_ST_WAIT_START; + return 0; + } + + switch (sercomm.rx.state) { + case RX_ST_WAIT_START: + if (ch != HDLC_FLAG) + break; + sercomm.rx.state = RX_ST_ADDR; + break; + case RX_ST_ADDR: + sercomm.rx.dlci = ch; + sercomm.rx.state = RX_ST_CTRL; + break; + case RX_ST_CTRL: + sercomm.rx.ctrl = ch; + sercomm.rx.state = RX_ST_DATA; + break; + case RX_ST_DATA: + if (ch == HDLC_ESCAPE) { + /* drop the escape octet, but change state */ + sercomm.rx.state = RX_ST_ESCAPE; + break; + } else if (ch == HDLC_FLAG) { + /* message is finished */ + dispatch_rx_msg(sercomm.rx.dlci, sercomm.rx.msg); + /* allocate new buffer */ + sercomm.rx.msg = NULL; + /* start all over again */ + sercomm.rx.state = RX_ST_WAIT_START; + + /* do not add the control char */ + break; + } + /* default case: store the octet */ + ptr = msgb_put(sercomm.rx.msg, 1); + *ptr = ch; + break; + case RX_ST_ESCAPE: + /* store bif-5-inverted octet in buffer */ + ch ^= (1 << 5); + ptr = msgb_put(sercomm.rx.msg, 1); + *ptr = ch; + /* transition back to nromal DATA state */ + sercomm.rx.state = RX_ST_DATA; + break; + } + + return 1; +} diff --git a/src/target/firmware/comm/sercomm_cons.c b/src/target/firmware/comm/sercomm_cons.c new file mode 100644 index 00000000..5d7842fb --- /dev/null +++ b/src/target/firmware/comm/sercomm_cons.c @@ -0,0 +1,140 @@ +/* Serial console layer, layered on top of sercomm HDLC */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <string.h> + +#include <asm/system.h> + +#include <calypso/uart.h> + +#include <console.h> +#include <osmocore/msgb.h> +#include <comm/sercomm.h> +#include <comm/sercomm_cons.h> + +static struct { + struct msgb *cur_msg; +} scons; + +static void raw_puts(const char *s) +{ + int i = strlen(s); + while (i--) + uart_putchar_wait(SERCOMM_UART_NR, *s++); +} + +#ifdef DEBUG +#define raw_putd(x) raw_puts(x) +#else +#define raw_putd(x) +#endif + +int sercomm_puts(const char *s) +{ + unsigned long flags; + const int len = strlen(s); + unsigned int bytes_left = len; + + if (!sercomm_initialized()) { + raw_putd("sercomm not initialized: "); + raw_puts(s); + return len - 1; + } + + /* This function is called from any context: Supervisor, IRQ, FIQ, ... + * as such, we need to ensure re-entrant calls are either supported or + * avoided. */ + local_irq_save(flags); + local_fiq_disable(); + + while (bytes_left > 0) { + unsigned int write_num, space_left, flush; + uint8_t *data; + + if (!scons.cur_msg) + scons.cur_msg = sercomm_alloc_msgb(SERCOMM_CONS_ALLOC); + + if (!scons.cur_msg) { + raw_putd("cannot allocate sercomm msgb: "); + raw_puts(s); + return -ENOMEM; + } + + /* space left in the current msgb */ + space_left = msgb_tailroom(scons.cur_msg); + + if (space_left <= bytes_left) { + write_num = space_left; + /* flush buffer when it is full */ + flush = 1; + } else { + write_num = bytes_left; + flush = 0; + } + + /* obtain pointer where to copy the data */ + data = msgb_put(scons.cur_msg, write_num); + + /* copy data while looking for \n line termination */ + { + unsigned int i; + for (i = 0; i < write_num; i++) { + /* flush buffer at end of line, but skip + * flushing if we have a backlog in order to + * increase efficiency of msgb filling */ + if (*s == '\n' && + sercomm_tx_queue_depth(SC_DLCI_CONSOLE) < 4) + flush = 1; + *data++ = *s++; + } + } + bytes_left -= write_num; + + if (flush) { + sercomm_sendmsg(SC_DLCI_CONSOLE, scons.cur_msg); + /* reset scons.cur_msg pointer to ensure we allocate + * a new one next round */ + scons.cur_msg = NULL; + } + } + + local_irq_restore(flags); + + return len - 1; +} + +int sercomm_putchar(int c) +{ + char s[2]; + int rc; + + s[0] = c & 0xff; + s[1] = '\0'; + + rc = sercomm_puts(s); + if (rc < 0) + return rc; + + return c; +} diff --git a/src/target/firmware/comm/timer.c b/src/target/firmware/comm/timer.c new file mode 100644 index 00000000..9799cfc5 --- /dev/null +++ b/src/target/firmware/comm/timer.c @@ -0,0 +1,217 @@ +/* (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <debug.h> +#include <osmocore/linuxlist.h> + +#include <comm/timer.h> + +#include <calypso/timer.h> +#include <calypso/irq.h> + +#include <keypad.h> + +static LLIST_HEAD(timer_list); + +unsigned long volatile jiffies; + +#define TIMER_HZ 100 + +#define time_after(a,b) \ + (typecheck(unsigned long, a) && \ + typecheck(unsigned long, b) && \ + ((long)(b) - (long)(a) < 0)) +#define time_before(a,b) time_after(b,a) + +void add_timer(struct timer_list *timer) +{ + struct timer_list *list_timer; + + /* TODO: Optimize and remember the closest item... */ + timer->active = 1; + + /* this might be called from within update_timers */ + llist_for_each_entry(list_timer, &timer_list, entry) + if (timer == list_timer) + return; + + timer->in_list = 1; + llist_add(&timer->entry, &timer_list); +} + +void schedule_timer(struct timer_list *timer, int milliseconds) +{ + timer->expires = jiffies + ((milliseconds * TIMER_HZ) / 1000); + add_timer(timer); +} + +void del_timer(struct timer_list *timer) +{ + if (timer->in_list) { + timer->active = 0; + timer->in_list = 0; + llist_del(&timer->entry); + } +} + +int timer_pending(struct timer_list *timer) +{ + return timer->active; +} + +#if 0 +/* + * if we have a nearest time return the delta between the current + * time and the time of the nearest timer. + * If the nearest timer timed out return NULL and then we will + * dispatch everything after the select + */ +struct timeval *nearest_timer() +{ + struct timeval current_time; + + if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0) + return NULL; + + if (gettimeofday(¤t_time, NULL) == -1) + return NULL; + + unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec; + unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; + + if (nearestTime < currentTime) { + s_select_time.tv_sec = 0; + s_select_time.tv_usec = 0; + } else { + s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS; + s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS; + } + + return &s_select_time; +} + +/* + * Find the nearest time and update s_nearest_time + */ +void prepare_timers() +{ + struct timer_list *timer, *nearest_timer = NULL; + llist_for_each_entry(timer, &timer_list, entry) { + if (!nearest_timer || time_before(timer->expires, nearest_timer->expires)) { + nearest_timer = timer; + } + } + + if (nearest_timer) { + s_nearest_time = nearest_timer->timeout; + } else { + memset(&s_nearest_time, 0, sizeof(struct timeval)); + } +} +#endif + +/* + * fire all timers... and remove them + */ +int update_timers(void) +{ + struct timer_list *timer, *tmp; + int work = 0; + + /* + * The callbacks might mess with our list and in this case + * even llist_for_each_entry_safe is not safe to use. To allow + * del_timer, add_timer, schedule_timer to be called from within + * the callback we jump through some loops. + * + * First we set the handled flag of each active timer to zero, + * then we iterate over the list and execute the callbacks. As the + * list might have been changed (specially the next) from within + * the callback we have to start over again. Once every callback + * is dispatched we will remove the non-active from the list. + * + * TODO: If this is a performance issue we can poison a global + * variable in add_timer and del_timer and only then restart. + */ + llist_for_each_entry(timer, &timer_list, entry) { + timer->handled = 0; + } + +restart: + llist_for_each_entry(timer, &timer_list, entry) { + if (!timer->handled && time_before(timer->expires, jiffies)) { + timer->handled = 1; + timer->active = 0; + (*timer->cb)(timer->data); + work = 1; + goto restart; + } + } + + llist_for_each_entry_safe(timer, tmp, &timer_list, entry) { + timer->handled = 0; + if (!timer->active) { + del_timer(timer); + } + } + + return work; +} + +int timer_check(void) +{ + struct timer_list *timer; + int i = 0; + + llist_for_each_entry(timer, &timer_list, entry) { + i++; + } + return i; +} + +static void timer_irq(enum irq_nr irq) +{ + /* we only increment jiffies here. FIXME: does this need to be atomic? */ + jiffies++; + + keypad_poll(); +} + +void timer_init(void) +{ + /* configure TIMER2 for our purpose */ + hwtimer_enable(2, 1); + /* The timer runs at 13MHz / 32, i.e. 406.25kHz */ +#if (TIMER_HZ == 100) + hwtimer_load(2, 4062); + hwtimer_config(2, 0, 1); +#elif (TIMER_HZ == 10) + /* prescaler 4, 1015 ticks until expiry */ + hwtimer_load(2, 1015); + hwtimer_config(2, 4, 1); +#endif + hwtimer_enable(2, 1); + + /* register interrupt handler with default priority, EDGE triggered */ + irq_register_handler(IRQ_TIMER2, &timer_irq); + irq_config(IRQ_TIMER2, 0, 1, -1); + irq_enable(IRQ_TIMER2); +} diff --git a/src/target/firmware/display/display.c b/src/target/firmware/display/display.c new file mode 100644 index 00000000..1c8f1fb4 --- /dev/null +++ b/src/target/firmware/display/display.c @@ -0,0 +1,20 @@ + +#include <stdint.h> + +#include <display.h> + +struct display_driver *display; + +int display_puts(const char *str) +{ + char c; + + if (display->puts) + display->puts(str); + else { + while ((c = *str++)) + display_putchar(c); + } + + return 0; +} diff --git a/src/target/firmware/display/font_r8x8.c b/src/target/firmware/display/font_r8x8.c Binary files differnew file mode 100644 index 00000000..f6a8a820 --- /dev/null +++ b/src/target/firmware/display/font_r8x8.c diff --git a/src/target/firmware/display/font_r8x8_horiz.c b/src/target/firmware/display/font_r8x8_horiz.c new file mode 100644 index 00000000..046d09bf --- /dev/null +++ b/src/target/firmware/display/font_r8x8_horiz.c @@ -0,0 +1,261 @@ +/* 8x8 font, right aligned, horizontal scanning */ + +const unsigned char fontdata_r8x8_horiz[] ={ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7e,0x81,0xa5,0x81,0xbd,0x99,0x81,0x7e, + 0x7e,0xff,0xdb,0xff,0xc3,0xe7,0xff,0x7e, + 0x6c,0xfe,0xfe,0xfe,0x7c,0x38,0x10,0x00, + 0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x08,0x00, + 0x1c,0x1c,0x1c,0x7f,0x7f,0x6b,0x08,0x1c, + 0x10,0x10,0x38,0x7c,0xfe,0x7c,0x10,0x38, + 0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00, + 0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff, + 0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00, + 0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff, + 0x0f,0x07,0x0f,0x7d,0xcc,0xcc,0xcc,0x78, + 0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18, + 0x3f,0x33,0x3f,0x30,0x30,0x70,0xf0,0xe0, + 0x7f,0x63,0x7f,0x63,0x63,0x67,0xe6,0xc0, + 0x18,0xdb,0x3c,0xe7,0xe7,0x3c,0xdb,0x18, + 0x80,0xe0,0xf8,0xfe,0xf8,0xe0,0x80,0x00, + 0x02,0x0e,0x3e,0xfe,0x3e,0x0e,0x02,0x00, + 0x18,0x3c,0x7e,0x18,0x18,0x7e,0x3c,0x18, + 0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x00, + 0x7f,0xdb,0xdb,0x7b,0x1b,0x1b,0x1b,0x00, + 0x3e,0x63,0x38,0x6c,0x6c,0x38,0xcc,0x78, + 0x00,0x00,0x00,0x00,0x7e,0x7e,0x7e,0x00, + 0x18,0x3c,0x7e,0x18,0x7e,0x3c,0x18,0xff, + 0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x00, + 0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00, + 0x00,0x18,0x0c,0xfe,0x0c,0x18,0x00,0x00, + 0x00,0x30,0x60,0xfe,0x60,0x30,0x00,0x00, + 0x00,0x00,0xc0,0xc0,0xc0,0xfe,0x00,0x00, + 0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00, + 0x00,0x18,0x3c,0x7e,0xff,0xff,0x00,0x00, + 0x00,0xff,0xff,0x7e,0x3c,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x30,0x78,0x78,0x30,0x30,0x00,0x30,0x00, + 0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x00, + 0x6c,0x6c,0xfe,0x6c,0xfe,0x6c,0x6c,0x00, + 0x18,0x3e,0x60,0x3c,0x06,0x7c,0x18,0x00, + 0x00,0x63,0x66,0x0c,0x18,0x33,0x63,0x00, + 0x1c,0x36,0x1c,0x3b,0x6e,0x66,0x3b,0x00, + 0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00, + 0x0c,0x18,0x30,0x30,0x30,0x18,0x0c,0x00, + 0x30,0x18,0x0c,0x0c,0x0c,0x18,0x30,0x00, + 0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00, + 0x00,0x30,0x30,0xfc,0x30,0x30,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30, + 0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00, + 0x03,0x06,0x0c,0x18,0x30,0x60,0x40,0x00, + 0x3e,0x63,0x67,0x6f,0x7b,0x73,0x3e,0x00, + 0x18,0x38,0x58,0x18,0x18,0x18,0x7e,0x00, + 0x3c,0x66,0x06,0x1c,0x30,0x66,0x7e,0x00, + 0x3c,0x66,0x06,0x1c,0x06,0x66,0x3c,0x00, + 0x0e,0x1e,0x36,0x66,0x7f,0x06,0x0f,0x00, + 0x7e,0x60,0x7c,0x06,0x06,0x66,0x3c,0x00, + 0x1c,0x30,0x60,0x7c,0x66,0x66,0x3c,0x00, + 0x7e,0x66,0x06,0x0c,0x18,0x18,0x18,0x00, + 0x3c,0x66,0x66,0x3c,0x66,0x66,0x3c,0x00, + 0x3c,0x66,0x66,0x3e,0x06,0x0c,0x38,0x00, + 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00, + 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30, + 0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x00, + 0x00,0x00,0x7e,0x00,0x00,0x7e,0x00,0x00, + 0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x00, + 0x3c,0x66,0x06,0x0c,0x18,0x00,0x18,0x00, + 0x3e,0x63,0x6f,0x69,0x6f,0x60,0x3e,0x00, + 0x18,0x3c,0x66,0x66,0x7e,0x66,0x66,0x00, + 0x7e,0x33,0x33,0x3e,0x33,0x33,0x7e,0x00, + 0x1e,0x33,0x60,0x60,0x60,0x33,0x1e,0x00, + 0x7c,0x36,0x33,0x33,0x33,0x36,0x7c,0x00, + 0x7f,0x31,0x34,0x3c,0x34,0x31,0x7f,0x00, + 0x7f,0x31,0x34,0x3c,0x34,0x30,0x78,0x00, + 0x1e,0x33,0x60,0x60,0x67,0x33,0x1f,0x00, + 0x66,0x66,0x66,0x7e,0x66,0x66,0x66,0x00, + 0x3c,0x18,0x18,0x18,0x18,0x18,0x3c,0x00, + 0x0f,0x06,0x06,0x06,0x66,0x66,0x3c,0x00, + 0x73,0x33,0x36,0x3c,0x36,0x33,0x73,0x00, + 0x78,0x30,0x30,0x30,0x31,0x33,0x7f,0x00, + 0x63,0x77,0x7f,0x7f,0x6b,0x63,0x63,0x00, + 0x63,0x73,0x7b,0x6f,0x67,0x63,0x63,0x00, + 0x3e,0x63,0x63,0x63,0x63,0x63,0x3e,0x00, + 0x7e,0x33,0x33,0x3e,0x30,0x30,0x78,0x00, + 0x3c,0x66,0x66,0x66,0x6e,0x3c,0x0e,0x00, + 0x7e,0x33,0x33,0x3e,0x36,0x33,0x73,0x00, + 0x3c,0x66,0x30,0x18,0x0c,0x66,0x3c,0x00, + 0x7e,0x5a,0x18,0x18,0x18,0x18,0x3c,0x00, + 0x66,0x66,0x66,0x66,0x66,0x66,0x7e,0x00, + 0x66,0x66,0x66,0x66,0x66,0x3c,0x18,0x00, + 0x63,0x63,0x63,0x6b,0x7f,0x77,0x63,0x00, + 0x63,0x63,0x36,0x1c,0x1c,0x36,0x63,0x00, + 0x66,0x66,0x66,0x3c,0x18,0x18,0x3c,0x00, + 0x7f,0x63,0x46,0x0c,0x19,0x33,0x7f,0x00, + 0x3c,0x30,0x30,0x30,0x30,0x30,0x3c,0x00, + 0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00, + 0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,0x00, + 0x08,0x1c,0x36,0x63,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0x18,0x18,0x0c,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x3c,0x06,0x3e,0x66,0x3b,0x00, + 0x70,0x30,0x30,0x3e,0x33,0x33,0x6e,0x00, + 0x00,0x00,0x3c,0x66,0x60,0x66,0x3c,0x00, + 0x0e,0x06,0x06,0x3e,0x66,0x66,0x3b,0x00, + 0x00,0x00,0x3c,0x66,0x7e,0x60,0x3c,0x00, + 0x1c,0x36,0x30,0x78,0x30,0x30,0x78,0x00, + 0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x7c, + 0x70,0x30,0x36,0x3b,0x33,0x33,0x73,0x00, + 0x18,0x00,0x38,0x18,0x18,0x18,0x3c,0x00, + 0x06,0x00,0x06,0x06,0x06,0x66,0x66,0x3c, + 0x70,0x30,0x33,0x36,0x3c,0x36,0x73,0x00, + 0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00, + 0x00,0x00,0x66,0x7f,0x7f,0x6b,0x63,0x00, + 0x00,0x00,0x7c,0x66,0x66,0x66,0x66,0x00, + 0x00,0x00,0x3c,0x66,0x66,0x66,0x3c,0x00, + 0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x78, + 0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x0f, + 0x00,0x00,0x6e,0x3b,0x33,0x30,0x78,0x00, + 0x00,0x00,0x3e,0x60,0x3c,0x06,0x7c,0x00, + 0x08,0x18,0x3e,0x18,0x18,0x1a,0x0c,0x00, + 0x00,0x00,0x66,0x66,0x66,0x66,0x3b,0x00, + 0x00,0x00,0x66,0x66,0x66,0x3c,0x18,0x00, + 0x00,0x00,0x63,0x6b,0x7f,0x7f,0x36,0x00, + 0x00,0x00,0x63,0x36,0x1c,0x36,0x63,0x00, + 0x00,0x00,0x66,0x66,0x66,0x3e,0x06,0x7c, + 0x00,0x00,0x7e,0x4c,0x18,0x32,0x7e,0x00, + 0x0e,0x18,0x18,0x70,0x18,0x18,0x0e,0x00, + 0x0c,0x0c,0x0c,0x00,0x0c,0x0c,0x0c,0x00, + 0x70,0x18,0x18,0x0e,0x18,0x18,0x70,0x00, + 0x3b,0x6e,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x08,0x1c,0x36,0x63,0x63,0x7f,0x00, + 0x3c,0x66,0x60,0x66,0x3c,0x0c,0x06,0x3c, + 0x00,0x66,0x00,0x66,0x66,0x66,0x3f,0x00, + 0x1c,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00, + 0x7e,0xc3,0x3c,0x06,0x3e,0x66,0x3f,0x00, + 0x66,0x00,0x3c,0x06,0x3e,0x66,0x3f,0x00, + 0x70,0x00,0x3c,0x06,0x3e,0x66,0x3f,0x00, + 0x18,0x18,0x3c,0x06,0x3e,0x66,0x3f,0x00, + 0x00,0x00,0x3c,0x60,0x60,0x3c,0x06,0x1c, + 0x7e,0xc3,0x3c,0x66,0x7e,0x60,0x3c,0x00, + 0xcc,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00, + 0x70,0x00,0x3c,0x66,0x7e,0x60,0x3c,0x00, + 0x66,0x00,0x38,0x18,0x18,0x18,0x3c,0x00, + 0x3e,0x63,0x1c,0x0c,0x0c,0x0c,0x1e,0x00, + 0x70,0x00,0x38,0x18,0x18,0x18,0x3c,0x00, + 0x63,0x1c,0x36,0x63,0x7f,0x63,0x63,0x00, + 0x18,0x18,0x00,0x3c,0x66,0x7e,0x66,0x00, + 0x1c,0x00,0xfc,0x60,0x78,0x60,0xfc,0x00, + 0x00,0x00,0x7f,0x0c,0x7f,0xcc,0x7f,0x00, + 0x1f,0x36,0x66,0x7f,0x66,0x66,0x67,0x00, + 0x3c,0x66,0x00,0x3c,0x66,0x66,0x3c,0x00, + 0x00,0x66,0x00,0x3c,0x66,0x66,0x3c,0x00, + 0x00,0x70,0x00,0x3c,0x66,0x66,0x3c,0x00, + 0x3c,0x66,0x00,0x66,0x66,0x66,0x3f,0x00, + 0x00,0x70,0x00,0x66,0x66,0x66,0x3f,0x00, + 0x00,0xcc,0x00,0xcc,0xcc,0x7c,0x0c,0xf8, + 0xc3,0x18,0x3c,0x66,0x66,0x3c,0x18,0x00, + 0x66,0x00,0x66,0x66,0x66,0x66,0x3c,0x00, + 0x0c,0x0c,0x3f,0x60,0x60,0x3f,0x0c,0x0c, + 0x1c,0x36,0x32,0x78,0x30,0x73,0x7e,0x00, + 0x66,0x66,0x3c,0x7e,0x18,0x7e,0x18,0x18, + 0xf8,0xcc,0xcc,0xfa,0xc6,0xcf,0xc6,0xc7, + 0x0e,0x1b,0x18,0x3c,0x18,0x18,0xd8,0x70, + 0x0e,0x00,0x3c,0x06,0x3e,0x66,0x3f,0x00, + 0x1c,0x00,0x38,0x18,0x18,0x18,0x3c,0x00, + 0x00,0x0e,0x00,0x3c,0x66,0x66,0x3c,0x00, + 0x00,0x0e,0x00,0x66,0x66,0x66,0x3f,0x00, + 0x00,0x7c,0x00,0x7c,0x66,0x66,0x66,0x00, + 0x7e,0x00,0x66,0x76,0x7e,0x6e,0x66,0x00, + 0x1e,0x36,0x36,0x1f,0x00,0x3f,0x00,0x00, + 0x1c,0x36,0x36,0x1c,0x00,0x3e,0x00,0x00, + 0x18,0x00,0x18,0x30,0x60,0x66,0x3c,0x00, + 0x00,0x00,0x00,0x7e,0x60,0x60,0x00,0x00, + 0x00,0x00,0x00,0xfc,0x0c,0x0c,0x00,0x00, + 0xc3,0xc6,0xcc,0xde,0x33,0x66,0xcc,0x0f, + 0xc3,0xc6,0xcc,0xdb,0x37,0x6f,0xcf,0x03, + 0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x00, + 0x00,0x33,0x66,0xcc,0x66,0x33,0x00,0x00, + 0x00,0xcc,0x66,0x33,0x66,0xcc,0x00,0x00, + 0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88, + 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, + 0xdb,0x77,0xdb,0xee,0xdb,0x77,0xdb,0xee, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18, + 0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18, + 0x36,0x36,0x36,0x36,0xf6,0x36,0x36,0x36, + 0x00,0x00,0x00,0x00,0xfe,0x36,0x36,0x36, + 0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18, + 0x36,0x36,0xf6,0x06,0xf6,0x36,0x36,0x36, + 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, + 0x00,0x00,0xfe,0x06,0xf6,0x36,0x36,0x36, + 0x36,0x36,0xf6,0x06,0xfe,0x00,0x00,0x00, + 0x36,0x36,0x36,0x36,0xfe,0x00,0x00,0x00, + 0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00, + 0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18, + 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00, + 0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18, + 0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18, + 0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36, + 0x36,0x36,0x37,0x30,0x3f,0x00,0x00,0x00, + 0x00,0x00,0x3f,0x30,0x37,0x36,0x36,0x36, + 0x36,0x36,0xf7,0x00,0xff,0x00,0x00,0x00, + 0x00,0x00,0xff,0x00,0xf7,0x36,0x36,0x36, + 0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36, + 0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00, + 0x36,0x36,0xf7,0x00,0xf7,0x36,0x36,0x36, + 0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00, + 0x36,0x36,0x36,0x36,0xff,0x00,0x00,0x00, + 0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18, + 0x00,0x00,0x00,0x00,0xff,0x36,0x36,0x36, + 0x36,0x36,0x36,0x36,0x3f,0x00,0x00,0x00, + 0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00, + 0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18, + 0x00,0x00,0x00,0x00,0x3f,0x36,0x36,0x36, + 0x36,0x36,0x36,0x36,0xff,0x36,0x36,0x36, + 0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0, + 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x3b,0x6e,0x64,0x6e,0x3b,0x00, + 0x00,0x3c,0x66,0x7c,0x66,0x7c,0x60,0x60, + 0x00,0x7e,0x66,0x60,0x60,0x60,0x60,0x00, + 0x00,0x7f,0x36,0x36,0x36,0x36,0x36,0x00, + 0x7e,0x66,0x30,0x18,0x30,0x66,0x7e,0x00, + 0x00,0x00,0x3f,0x6c,0x6c,0x6c,0x38,0x00, + 0x00,0x33,0x33,0x33,0x33,0x3e,0x30,0x60, + 0x00,0x3b,0x6e,0x0c,0x0c,0x0c,0x0c,0x00, + 0x7e,0x18,0x3c,0x66,0x66,0x3c,0x18,0x7e, + 0x1c,0x36,0x63,0x7f,0x63,0x36,0x1c,0x00, + 0x1c,0x36,0x63,0x63,0x36,0x36,0x77,0x00, + 0x0e,0x18,0x0c,0x3e,0x66,0x66,0x3c,0x00, + 0x00,0x00,0x7e,0xdb,0xdb,0x7e,0x00,0x00, + 0x06,0x0c,0x7e,0xdb,0xdb,0x7e,0x60,0xc0, + 0x1c,0x30,0x60,0x7c,0x60,0x30,0x1c,0x00, + 0x3c,0x66,0x66,0x66,0x66,0x66,0x66,0x00, + 0x00,0x7e,0x00,0x7e,0x00,0x7e,0x00,0x00, + 0x18,0x18,0x7e,0x18,0x18,0x00,0x7e,0x00, + 0x30,0x18,0x0c,0x18,0x30,0x00,0x7e,0x00, + 0x0c,0x18,0x30,0x18,0x0c,0x00,0x7e,0x00, + 0x0e,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0x70, + 0x18,0x18,0x00,0x7e,0x00,0x18,0x18,0x00, + 0x00,0x3b,0x6e,0x00,0x3b,0x6e,0x00,0x00, + 0x1c,0x36,0x36,0x1c,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x0c,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x0f,0x0c,0x0c,0x0c,0xec,0x6c,0x3c,0x1c, + 0x78,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00, + 0x70,0x18,0x30,0x60,0x78,0x00,0x00,0x00, + 0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + diff --git a/src/target/firmware/display/ssd1783.c b/src/target/firmware/display/ssd1783.c new file mode 100644 index 00000000..5696b48f --- /dev/null +++ b/src/target/firmware/display/ssd1783.c @@ -0,0 +1,257 @@ +/* Solomon SSD1783 LCD Driver (Epson S1D15G10D08B000 clone) */ + +/* (C) 2010 by Steve Markgraf <steve@steve-m.de> + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +//#define DEBUG +#include <debug.h> +#include <delay.h> +#include <uwire.h> +#include <display.h> +#include <display/ssd1783.h> +#include <calypso/clock.h> + +#define LCD_COLUMNS 98 +#define LCD_ROWS 67 +#define LCD_TOP_FREE_ROWS 3 +#define LCD_LEFT_FREE_COLS 0 +#define PIXEL_BYTES 3 +#define SSD1783_UWIRE_BITLEN 9 +#define SSD1783_DEV_ID 0 +#define FONT_HEIGHT 8 +#define FONT_WIDTH 8 + +static const uint8_t rgb8_palette[] ={ + 0x00, //P01 Intermediate red tone 000 + 0x03, //P02 Intermediate red tone 001 + 0x05, //P03 Intermediate red tone 010 + 0x07, //P04 Intermediate red tone 011 + 0x09, //P05 Intermediate red tone 100 + 0x0b, //P06 Intermediate red tone 101 + 0x0d, //P07 Intermediate red tone 110 + 0x0f, //P08 Intermediate red tone 111 + 0x00, //P09 Intermediate green tone 000 + 0x03, //P10 Intermediate green tone 001 + 0x05, //P11 Intermediate green tone 010 + 0x07, //P12 Intermediate green tone 011 + 0x09, //P13 Intermediate green tone 100 + 0x0b, //P14 Intermediate green tone 101 + 0x0d, //P15 Intermediate green tone 110 + 0x0f, //P16 Intermediate green tone 111 + 0x00, //P17 Intermediate blue tone 00 + 0x05, //P18 Intermediate blue tone 01 + 0x0a, //P19 Intermediate blue tone 10 + 0x0f, //P20 Intermediate blue tone 11 +}; + +static void ssd1783_cmd_write(const uint8_t cmd) +{ + uint16_t cmd_out = cmd; + uwire_xfer(SSD1783_DEV_ID, SSD1783_UWIRE_BITLEN, &cmd_out, NULL); +} + +static void ssd1783_data_write(const uint8_t data) +{ + uint16_t data_out = ((0x01 << 8) + data); + uwire_xfer(SSD1783_DEV_ID, SSD1783_UWIRE_BITLEN, &data_out, NULL); +} + +static void ssd1783_clrscr(void) +{ + uint16_t i; + + /* Select the whole display area for clearing */ + ssd1783_cmd_write(CMD_PASET); /* Page address set [2] */ + ssd1783_data_write(0x00); /* Start page: 0x00 */ + ssd1783_data_write(LCD_ROWS-1); /* End page */ + ssd1783_cmd_write(CMD_CASET); /* Column address set [2] */ + ssd1783_data_write(0x00); /* Start column: 0x00 */ + ssd1783_data_write((LCD_COLUMNS/2)-1); /* End column (2 pixels per column) */ + ssd1783_cmd_write(CMD_RAMWR); /* Write to memory */ + + /* Fill the display with white */ + for(i=0; i < (LCD_ROWS * (LCD_COLUMNS/2) * PIXEL_BYTES); i++){ + ssd1783_data_write(0xff); + } + ssd1783_cmd_write(CMD_NOP); /* Terminate RAMWR with NOP */ +} + +static void ssd1783_init(void) +{ + unsigned int i; + + calypso_reset_set(RESET_EXT, 0); + uwire_init(); + delay_ms(3); + + /* Begin SSD1783 initialization sequence */ + ssd1783_cmd_write(CMD_OSCON); /* Internal OSC on */ + ssd1783_cmd_write(CMD_SLPOUT); /* Sleep out (Leave sleep mode) */ + + ssd1783_cmd_write(CMD_COMSCN); /* Common scan direction [1] */ + ssd1783_data_write(0x01); /* Scan 1 -> 68, 132 <- 69 */ + ssd1783_cmd_write(CMD_DATCTL); /* Data Scan Direction [3] */ + ssd1783_data_write(0x00); /* Normal page address, normal rotation, + * scan direction in column direction */ + ssd1783_data_write(0x00); /* RGB arrangement: RGB-RGB */ + ssd1783_data_write(0x02); /* Gray-scale setup: 16 gray-scale Type A, 8-bit mode */ + + /* Initialize RGB8 palette for 8-Bit color mode */ + ssd1783_cmd_write(CMD_RGBSET8); /* 256-color position set [20] */ + for(i=0; i < sizeof(rgb8_palette); i++){ + ssd1783_data_write(rgb8_palette[i]); + } + + ssd1783_cmd_write(CMD_DISCTL); /* Display control [3] */ + ssd1783_data_write(0xff); /* no clock division, F1, F2 switching period = field */ + ssd1783_data_write(0x10); /* Drive duty, P24 = 1 */ + ssd1783_data_write(0x01); /* FR inverse set, P30=1 */ + ssd1783_cmd_write(CMD_SCSTART); /* Scroll start set [1] */ + ssd1783_data_write(0x00); /* Start block address 0x00 */ + + /* Turn on the power regulator which generates VLCD */ + ssd1783_cmd_write(CMD_PWRCTR); /* Power Control [1] */ + ssd1783_data_write(0x0b); /* Booster, follower and regulator circuit on */ + + /* FIXME: put this in a separate function (ssd1783_set_contrast) */ + ssd1783_cmd_write(CMD_VOLCTR); /* Electronic Volume Control [2] */ + ssd1783_data_write(0x29); /* Set contrast */ + ssd1783_data_write(0x05); /* Set contrast */ + + ssd1783_cmd_write(CMD_DISINV); /* Invert Display */ + ssd1783_cmd_write(CMD_TMPGRD); /* Temperature gradient set */ + ssd1783_data_write(0x00); /* default temperature gradient (-0.05% / °C) */ + ssd1783_cmd_write(CMD_BIASSET); /* Set biasing ratio [1] */ + ssd1783_data_write(0x03); /* 1/10 bias */ + ssd1783_cmd_write(CMD_FREQSET); /* Set frequency and n-line inversion [2] */ + ssd1783_data_write(0x08); /* frequency: 75Hz (POR) */ + ssd1783_data_write(0x06); /* n-line inversion: 6 lines */ + ssd1783_cmd_write(CMD_RESCMD); /* reserved command in datasheet? */ + ssd1783_cmd_write(CMD_PWMSEL); /* Select PWM/FRC, Full/8 color mode [3] */ + ssd1783_data_write(0x28); /* fixed */ + ssd1783_data_write(0x2c); /* 5 bits PWM + 1 bit FRC (POR) */ + ssd1783_data_write(0x05); /* Full color mode (0x45 would be 8 color powersaving) */ + + ssd1783_cmd_write(CMD_DISON); /* Display ON */ + ssd1783_clrscr(); /* Clear the display */ +} + +extern const unsigned char fontdata_r8x8_horiz[]; + +/* + * Pixel format for 8-bit mode, 12-bit color, 2 Pixel per 3 byte + * D7, D6, D5, D4, D3, D2, D1, D0: RRRRGGGG (8 bits) 1st write + * D7, D6, D5, D4, D3, D2, D1, D0: BBBBRRRR (8 bits) 2nd write + * D7, D6, D5, D4, D3, D2, D1, D0: GGGGBBBB (8 bits) 3rd write +*/ + +static void ssd1783_goto_xy(int xpos, int ypos) +{ + ssd1783_cmd_write(CMD_PASET); + ssd1783_data_write(xpos); + ssd1783_data_write(xpos + (FONT_HEIGHT-1)); + + ssd1783_cmd_write(CMD_CASET); + ssd1783_data_write(ypos); + ssd1783_data_write(ypos + ((FONT_WIDTH/2)-1)); + + ssd1783_cmd_write(CMD_NOP); +} + +static int ssd1783_putc_col(unsigned char c, int fColor, int bColor) +{ + int i, j; + uint8_t cols = FONT_WIDTH; + uint8_t rows = FONT_HEIGHT; + uint8_t row_slice; + uint8_t rowmask; + uint16_t pixel0; /* left pixel */ + uint16_t pixel1; /* right pixel */ + + ssd1783_cmd_write(CMD_RAMWR); + + for (i = 0; i < rows; i++) { + row_slice = fontdata_r8x8_horiz[(FONT_WIDTH * c)+i]; + printd("\nSSD1783 FontData=0x%02hx", row_slice); + rowmask = 0x80; + for (j = 0; j < cols; j += 2) { + if (!(row_slice & rowmask)) + pixel0 = bColor; + else + pixel0 = fColor; + rowmask = rowmask >> 1; + if (!(row_slice & rowmask)) + pixel1 = bColor; + else + pixel1 = fColor; + rowmask = rowmask >> 1; + /* Write the RGB-RGB pixel data */ + ssd1783_data_write((pixel0 >> 4) & 0xff); + ssd1783_data_write(((pixel0 & 0x00f) << 4) | ((pixel1 >> 8) & 0x00f)); + ssd1783_data_write(pixel1 & 0xff); + } + } + ssd1783_cmd_write(CMD_NOP); + + return c; +} + +static int ssd1783_puts_col(const char *str, int txtline, int fColor, int bColor) +{ + int i; + for (i = 0; *str != 0x00; i += (FONT_WIDTH/2)) { + ssd1783_goto_xy(((txtline*FONT_HEIGHT)+LCD_TOP_FREE_ROWS), + (i + LCD_LEFT_FREE_COLS)); + ssd1783_putc_col(*str++, fColor, bColor); + } + + return 0; +} + +/* interface to display driver core */ + +static void ssd1783_set_attr(unsigned long attr) +{ + /* FIXME */ +} + +static int ssd1783_putc(unsigned int c) +{ + return ssd1783_putc_col(c, BLACK, WHITE); +} + +static int ssd1783_puts(const char *str) +{ + return ssd1783_puts_col(str, 0, BLACK, WHITE); +} + +const struct display_driver ssd1783_display = { + .name = "ssd1783", + .init = &ssd1783_init, + .set_attr = &ssd1783_set_attr, + .unset_attr = &ssd1783_set_attr, + .clrscr = &ssd1783_clrscr, + .goto_xy = &ssd1783_goto_xy, + .putc = &ssd1783_putc, + .puts = &ssd1783_puts, +}; diff --git a/src/target/firmware/display/st7558.c b/src/target/firmware/display/st7558.c new file mode 100644 index 00000000..b92c2de1 --- /dev/null +++ b/src/target/firmware/display/st7558.c @@ -0,0 +1,121 @@ +/* Sitronix ST7558 LCD Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <i2c.h> +#include <display.h> +#include <calypso/clock.h> + +#define MORE_CONTROL 0x80 +#define CONTROL_RS_RAM 0x40 +#define CONTROL_RS_CMD 0x00 +#define Y_ADDR(n) (0x40|((n)&0xf)) +#define X_ADDR(n) (0x80|((n)&0x3f)) + +static const uint8_t setup[] = { CONTROL_RS_CMD, 0x2e, 0x21, 0x12, 0xc0, 0x0b, + 0x20, 0x11, 0x00, 0x40, 0x80 }; +static const uint8_t home[] = { CONTROL_RS_CMD, Y_ADDR(0), X_ADDR(0) }; + +/* video modes */ +static const uint8_t invert[] = { CONTROL_RS_CMD, 0x20, 0x0d }; +static const uint8_t normal[] = { CONTROL_RS_CMD, 0x20, 0x0c }; +static const uint8_t off[] = { CONTROL_RS_CMD, 0x20, 0x08 }; + +#define ST7558_SLAVE_ADDR 0x3c +static int st7558_write(const uint8_t *data, int len) +{ + int rc = i2c_write(ST7558_SLAVE_ADDR, data[0], 1, data+1, len-1); + return rc; +} + +static const uint8_t zero16[] = { CONTROL_RS_RAM, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; +static void st7558_clrscr(void) +{ + int i; + + st7558_write(home, sizeof(home)); + + for (i = 0; i < 102*9; i += 16) + st7558_write(zero16, sizeof(zero16)); + + st7558_write(home, sizeof(home)); +} + +static void st7558_init(void) +{ + /* Release nRESET */ + calypso_reset_set(RESET_EXT, 0); + + i2c_init(0,0); + + st7558_write(setup, sizeof(setup)); + st7558_clrscr(); +} + +static void st7558_set_attr(unsigned long attr) +{ + if (attr & DISP_ATTR_INVERT) + st7558_write(invert, sizeof(invert)); +} + +static void st7558_unset_attr(unsigned long attr) +{ + if (attr & DISP_ATTR_INVERT) + st7558_write(normal, sizeof(normal)); +} + +/* FIXME: we need a mini-libc */ +static void *mcpy(uint8_t *dst, const uint8_t *src, int len) +{ + while (len--) + *dst++ = *src++; + + return dst; +} + +extern const unsigned char fontdata_r8x8[]; + +static void st7558_putc(unsigned char c) +{ + uint8_t putc_buf[16]; + uint8_t bytes_per_char = 8; + + putc_buf[0] = CONTROL_RS_RAM; + mcpy(putc_buf+1, fontdata_r8x8+(c*bytes_per_char), bytes_per_char); + st7558_write(putc_buf, 1+bytes_per_char); +} + +const struct display_driver st7558_display = { + .name = "st7558", + .init = &st7558_init, + .clrscr = &st7558_clrscr, + .set_attr = &st7558_set_attr, + .unset_attr = &st7558_unset_attr, + .putc = &st7558_putc, +}; diff --git a/src/target/firmware/display/td014.c b/src/target/firmware/display/td014.c new file mode 100644 index 00000000..11ef3eab --- /dev/null +++ b/src/target/firmware/display/td014.c @@ -0,0 +1,185 @@ +/* Toppoly TD014 LCD Driver, as used in the Motorola C139/C140 */ + +/* (C) 2010 by Steve Markgraf <steve@steve-m.de> + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <delay.h> +#include <uwire.h> +#include <display.h> +#include <calypso/clock.h> + +#define LCD_COLUMNS 96 +#define LCD_ROWS 64 +#define LCD_TOP_FREE_ROWS 3 +#define LCD_LEFT_FREE_COLS 0 +#define PIXEL_BYTES 2 +#define TD014_UWIRE_BITLEN 9 +#define TD014_DEV_ID 0 +#define FONT_HEIGHT 8 +#define FONT_WIDTH 8 + +#define BLACK 0x0000 +#define WHITE 0xffff + +static void td014_cmd_write(const uint8_t cmd) +{ + uint16_t cmd_out = cmd; + uwire_xfer(TD014_DEV_ID, TD014_UWIRE_BITLEN, &cmd_out, NULL); +} + +static void td014_data_write(const uint8_t data) +{ + uint16_t data_out = ((0x01 << 8) + data); + uwire_xfer(TD014_DEV_ID, TD014_UWIRE_BITLEN, &data_out, NULL); +} + +static void td014_clrscr(void) +{ + uint16_t i; + + /* Select the whole display area for clearing */ + td014_cmd_write(0x10); + td014_data_write(0x00); + td014_cmd_write(0x11); + td014_data_write(0x00); + td014_cmd_write(0x12); + td014_data_write(LCD_COLUMNS-1); + td014_cmd_write(0x13); + td014_data_write(LCD_ROWS-1); + td014_cmd_write(0x14); + td014_data_write(0x00); + td014_cmd_write(0x15); + td014_data_write(0x00); + + /* Fill the display with white */ + for(i=0; i < (LCD_ROWS * LCD_COLUMNS * PIXEL_BYTES); i++) { + td014_data_write(0xff); + } +} + +static void td014_init(void) +{ + calypso_reset_set(RESET_EXT, 0); + uwire_init(); + delay_ms(3); + + td014_cmd_write(0x3f); + td014_data_write(0x01); + td014_cmd_write(0x20); + td014_data_write(0x03); + td014_cmd_write(0x31); + td014_data_write(0x03); + + td014_clrscr(); + +} + +extern const unsigned char fontdata_r8x8_horiz[]; + +static void td014_goto_xy(int xpos, int ypos) +{ + td014_cmd_write(0x10); + td014_data_write(ypos); + td014_cmd_write(0x11); + td014_data_write(xpos); + td014_cmd_write(0x12); + td014_data_write(ypos + FONT_HEIGHT-1); + td014_cmd_write(0x13); + td014_data_write(xpos + FONT_WIDTH-1); + td014_cmd_write(0x14); + td014_data_write(ypos); + td014_cmd_write(0x15); + td014_data_write(xpos); + +} + + /* RGB 556 Byte 1 | Byte 2 * + * Pixel format: RRRRRGGG|GGBBBBBB */ + +static int td014_putc_col(unsigned char c, int fColor, int bColor) +{ + int i, j; + uint8_t cols = FONT_WIDTH; + uint8_t rows = FONT_HEIGHT; + uint8_t row_slice; + uint8_t rowmask; + uint16_t pixel; + + for (i = 0; i < rows; i++) { + row_slice = fontdata_r8x8_horiz[(FONT_WIDTH * c)+i]; + rowmask = 0x80; + for (j = 0; j < cols; j++) { + if (!(row_slice & rowmask)) + pixel = bColor; + else + pixel = fColor; + rowmask = rowmask >> 1; + /* Write the pixel data */ + td014_data_write((pixel >> 8) & 0xff); + td014_data_write(pixel & 0xff); + } + } + return c; +} + +static int td014_puts_col(const char *str, int txtline, int fColor, int bColor) +{ + int i; + for (i = 0; *str != 0x00; i += FONT_WIDTH) { + td014_goto_xy(((txtline*FONT_HEIGHT)+LCD_TOP_FREE_ROWS), + (i + LCD_LEFT_FREE_COLS)); + td014_putc_col(*str++, fColor, bColor); + } + + return 0; +} + +/* interface to display driver core */ + +static void td014_set_attr(unsigned long attr) +{ + /* FIXME */ +} + +static int td014_putc(unsigned int c) +{ + return td014_putc_col(c, BLACK, WHITE); +} + +static int td014_puts(const char *str) +{ + return td014_puts_col(str, 0, BLACK, WHITE); +} + +const struct display_driver td014_display = { + .name = "td014", + .init = &td014_init, + .set_attr = &td014_set_attr, + .unset_attr = &td014_set_attr, + .clrscr = &td014_clrscr, + .goto_xy = &td014_goto_xy, + .putc = &td014_putc, + .puts = &td014_puts, +}; diff --git a/src/target/firmware/flash/cfi_flash.c b/src/target/firmware/flash/cfi_flash.c new file mode 100644 index 00000000..4218db16 --- /dev/null +++ b/src/target/firmware/flash/cfi_flash.c @@ -0,0 +1,579 @@ +/* NOR Flash Driver for Intel 28F160C3 NOR flash */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <debug.h> +#include <stdio.h> +#include <stdint.h> +#include <errno.h> +#include <memory.h> +#include <defines.h> +#include <flash/cfi_flash.h> + +/* XXX: memdump_range() */ +#include <calypso/misc.h> +#include <calypso/uart.h> +#include <comm/sercomm.h> + +/* XXX: strings must always be in ram */ +#if 0 +#define puts(...) +#define printf(...) +#endif + +/* global definitions */ +#define CFI_FLASH_MAX_ERASE_REGIONS 4 + +/* structure of erase region descriptor */ +struct cfi_region { + uint16_t b_count; + uint16_t b_size; +} __attribute__ ((packed)); + +/* structure of cfi query response */ +struct cfi_query { + uint8_t qry[3]; + uint16_t p_id; + uint16_t p_adr; + uint16_t a_id; + uint16_t a_adr; + uint8_t vcc_min; + uint8_t vcc_max; + uint8_t vpp_min; + uint8_t vpp_max; + uint8_t word_write_timeout_typ; + uint8_t buf_write_timeout_typ; + uint8_t block_erase_timeout_typ; + uint8_t chip_erase_timeout_typ; + uint8_t word_write_timeout_max; + uint8_t buf_write_timeout_max; + uint8_t block_erase_timeout_max; + uint8_t chip_erase_timeout_max; + uint8_t dev_size; + uint16_t interface_desc; + uint16_t max_buf_write_size; + uint8_t num_erase_regions; + struct cfi_region erase_regions[CFI_FLASH_MAX_ERASE_REGIONS]; +} __attribute__ ((packed)); + +/* manufacturer ids */ +enum cfi_manuf { + CFI_MANUF_INTEL = 0x0089, +}; + +/* algorithm ids */ +enum cfi_algo { + CFI_ALGO_INTEL_3 = 0x03 +}; + +/* various command bytes */ +enum cfi_flash_cmd { + CFI_CMD_RESET = 0xff, + CFI_CMD_READ_ID = 0x90, + CFI_CMD_CFI = 0x98, + CFI_CMD_READ_STATUS = 0x70, + CFI_CMD_CLEAR_STATUS = 0x50, + CFI_CMD_WRITE = 0x40, + CFI_CMD_BLOCK_ERASE = 0x20, + CFI_CMD_ERASE_CONFIRM = 0xD0, + CFI_CMD_PROTECT = 0x60, +}; + +/* protection commands */ +enum flash_prot_cmd { + CFI_PROT_LOCK = 0x01, + CFI_PROT_UNLOCK = 0xD0, + CFI_PROT_LOCKDOWN = 0x2F +}; + +/* offsets from base */ +enum flash_offset { + CFI_OFFSET_MANUFACTURER_ID = 0x00, + CFI_OFFSET_DEVICE_ID = 0x01, + CFI_OFFSET_INTEL_PROTECTION = 0x81, + CFI_OFFSET_CFI_RESP = 0x10 +}; + +/* offsets from block base */ +enum flash_block_offset { + CFI_OFFSET_BLOCK_LOCKSTATE = 0x02 +}; + +/* status masks */ +enum flash_status { + CFI_STATUS_READY = 0x80, + CFI_STATUS_ERASE_SUSPENDED = 0x40, + CFI_STATUS_ERASE_ERROR = 0x20, + CFI_STATUS_PROGRAM_ERROR = 0x10, + CFI_STATUS_VPP_LOW = 0x08, + CFI_STATUS_PROGRAM_SUSPENDED = 0x04, + CFI_STATUS_LOCKED_ERROR = 0x02, + CFI_STATUS_RESERVED = 0x01 +}; + +__ramtext +static inline void flash_write_cmd(const void *base_addr, uint16_t cmd) +{ + writew(cmd, base_addr); +} + +__ramtext +static inline uint16_t flash_read16(const void *base_addr, uint32_t offset) +{ + return readw(base_addr + (offset << 1)); +} + +__ramtext +static char flash_protected(uint32_t block_offset) +{ +#ifdef CONFIG_FLASH_WRITE +# ifdef CONFIG_FLASH_WRITE_LOADER + return 0; +# else + return block_offset <= 0xFFFF; +# endif +#else + return 1; +#endif +} + +__ramtext +flash_lock_t flash_block_getlock(flash_t * flash, uint32_t block_offset) +{ + const void *base_addr = flash->f_base; + + uint8_t lockstate; + flash_write_cmd(base_addr, CFI_CMD_READ_ID); + lockstate = + flash_read16(base_addr, + (block_offset >> 1) + CFI_OFFSET_BLOCK_LOCKSTATE); + flash_write_cmd(base_addr, CFI_CMD_RESET); + + if (lockstate & 0x2) { + return FLASH_LOCKED_DOWN; + } else if (lockstate & 0x01) { + return FLASH_LOCKED; + } else { + return FLASH_UNLOCKED; + } +} + +__ramtext +int flash_block_unlock(flash_t * flash, uint32_t block_offset) +{ + const void *base_addr = flash->f_base; + + if (block_offset >= flash->f_size) { + return -EINVAL; + } + + if (flash_protected(block_offset)) { + return -EPERM; + } + + printf("Unlocking block at 0x%08x, meaning %08x\n", + block_offset, base_addr + block_offset); + + flash_write_cmd(base_addr, CFI_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, CFI_PROT_UNLOCK); + flash_write_cmd(base_addr, CFI_CMD_RESET); + + return 0; +} + +__ramtext +int flash_block_lock(flash_t * flash, uint32_t block_offset) +{ + const void *base_addr = flash->f_base; + + if (block_offset >= flash->f_size) { + return -EINVAL; + } + + printf("Locking block at 0x%08x\n", block_offset); + + flash_write_cmd(base_addr, CFI_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, CFI_PROT_LOCK); + flash_write_cmd(base_addr, CFI_CMD_RESET); + + return 0; +} + +__ramtext +int flash_block_lockdown(flash_t * flash, uint32_t block_offset) +{ + const void *base_addr = flash->f_base; + + if (block_offset >= flash->f_size) { + return -EINVAL; + } + + printf("Locking down block at 0x%08x\n", block_offset); + + flash_write_cmd(base_addr, CFI_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, CFI_PROT_LOCKDOWN); + flash_write_cmd(base_addr, CFI_CMD_RESET); + + return 0; +} + +__ramtext +int flash_block_erase(flash_t * flash, uint32_t block_offset) +{ + const void *base_addr = flash->f_base; + + if (block_offset >= flash->f_size) { + return -EINVAL; + } + + if (flash_protected(block_offset)) { + return -EPERM; + } + + printf("Erasing block 0x%08x...", block_offset); + + void *block_addr = ((uint8_t *) base_addr) + block_offset; + + flash_write_cmd(base_addr, CFI_CMD_CLEAR_STATUS); + + flash_write_cmd(block_addr, CFI_CMD_BLOCK_ERASE); + flash_write_cmd(block_addr, CFI_CMD_ERASE_CONFIRM); + + flash_write_cmd(base_addr, CFI_CMD_READ_STATUS); + uint16_t status; + do { + status = flash_read16(base_addr, 0); + } while (!(status & CFI_STATUS_READY)); + + int res = 0; + if (status & CFI_STATUS_ERASE_ERROR) { + puts("error: "); + if (status & CFI_STATUS_VPP_LOW) { + puts("vpp insufficient\n"); + res = -EFAULT; + } else if (status & CFI_STATUS_LOCKED_ERROR) { + puts("block is lock-protected\n"); + res = -EPERM; + } else { + puts("unknown fault\n"); + res = -EFAULT; + } + } else { + puts("done\n"); + } + + flash_write_cmd(base_addr, CFI_CMD_RESET); + + return res; + +} + +__ramtext +int flash_program(flash_t * flash, uint32_t dst, void *src, uint32_t nbytes) +{ + const void *base_addr = flash->f_base; + int res = 0; + uint32_t i; + + /* check destination bounds */ + if (dst >= flash->f_size) { + return -EINVAL; + } + if (dst + nbytes > flash->f_size) { + return -EINVAL; + } + + /* check alignments */ + if (((uint32_t) src) % 2) { + return -EINVAL; + } + if (dst % 2) { + return -EINVAL; + } + if (nbytes % 2) { + return -EINVAL; + } + + /* check permissions */ + if (flash_protected(dst)) { + return -EPERM; + } + + /* say something */ + printf("Programming %u bytes to 0x%08x from 0x%p...", nbytes, dst, src); + + /* clear status register */ + flash_write_cmd(base_addr, CFI_CMD_CLEAR_STATUS); + + /* write the words */ + puts("writing..."); + for (i = 0; i < nbytes; i += 2) { + uint16_t *src_addr = (uint16_t *) (src + i); + uint16_t *dst_addr = (uint16_t *) (base_addr + dst + i); + + uint16_t data = *src_addr; + + flash_write_cmd(dst_addr, CFI_CMD_WRITE); + flash_write_cmd(dst_addr, data); + + flash_write_cmd(base_addr, CFI_CMD_READ_STATUS); + uint16_t status; + do { + status = flash_read16(base_addr, 0); + } while (!(status & CFI_STATUS_READY)); + + if (status & CFI_STATUS_PROGRAM_ERROR) { + puts("error: "); + if (status & CFI_STATUS_VPP_LOW) { + puts("vpp insufficient"); + res = -EFAULT; + } else if (status & CFI_STATUS_LOCKED_ERROR) { + puts("block is lock-protected"); + res = -EPERM; + } else { + puts("unknown fault"); + res = -EFAULT; + } + goto err_reset; + } + } + + flash_write_cmd(base_addr, CFI_CMD_RESET); + + /* verify the result */ + puts("verifying..."); + for (i = 0; i < nbytes; i += 2) { + uint16_t *src_addr = (uint16_t *) (src + i); + uint16_t *dst_addr = (uint16_t *) (base_addr + dst + i); + if (*src_addr != *dst_addr) { + puts("error: verification failed"); + res = -EFAULT; + goto err; + } + } + + puts("done\n"); + + return res; + + err_reset: + flash_write_cmd(base_addr, CFI_CMD_RESET); + + err: + printf(" at offset 0x%x\n", i); + + return res; +} + +/* Internal: retrieve manufacturer and device id from id space */ +__ramtext +static int get_id(void *base_addr, + uint16_t * manufacturer_id, uint16_t * device_id) +{ + flash_write_cmd(base_addr, CFI_CMD_READ_ID); + + *manufacturer_id = flash_read16(base_addr, CFI_OFFSET_MANUFACTURER_ID); + *device_id = flash_read16(base_addr, CFI_OFFSET_DEVICE_ID); + + flash_write_cmd(base_addr, CFI_CMD_RESET); + + return 0; +} + +/* Internal: retrieve cfi query response data */ +__ramtext +static int get_query(void *base_addr, struct cfi_query *query) +{ + int res = 0; + int i; + + flash_write_cmd(base_addr, CFI_CMD_CFI); + + for (i = 0; i < sizeof(struct cfi_query); i++) { + uint16_t byte = + flash_read16(base_addr, CFI_OFFSET_CFI_RESP + i); + *(((unsigned char *)query) + i) = byte; + } + + if (query->qry[0] != 'Q' || query->qry[1] != 'R' || query->qry[2] != 'Y') { + res = -ENOENT; + } + + flash_write_cmd(base_addr, CFI_CMD_RESET); + + return res; +} + +#if 0 + +/* Internal: retrieve intel protection data */ +__ramtext +static int get_intel_protection(void *base_addr, + uint16_t * lockp, uint8_t protp[8]) +{ + int i; + + /* check args */ + if (!lockp) { + return -EINVAL; + } + if (!protp) { + return -EINVAL; + } + + /* enter read id mode */ + flash_write_cmd(base_addr, CFI_CMD_READ_ID); + + /* get lock */ + *lockp = flash_read16(base_addr, CFI_OFFSET_INTEL_PROTECTION); + + /* get data */ + for (i = 0; i < 8; i++) { + protp[i] = flash_read16(base_addr, CFI_OFFSET_INTEL_PROTECTION + 1 + i); + } + + /* leave read id mode */ + flash_write_cmd(base_addr, CFI_CMD_RESET); + + return 0; +} + +static void dump_intel_protection(uint16_t lock, uint8_t data[8]) +{ + printf + (" protection lock 0x%4.4x data 0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", + lock, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); +} + +static void dump_query_algorithms(struct cfi_query *qry) +{ + printf(" primary algorithm 0x%4.4x\n", qry->p_id); + printf(" primary extended query 0x%4.4x\n", qry->p_adr); + printf(" alternate algorithm 0x%4.4x\n", qry->a_id); + printf(" alternate extended query 0x%4.4x\n", qry->a_adr); +} + +static void dump_query_timing(struct cfi_query *qry) +{ + uint32_t block_erase_typ = 1 << qry->block_erase_timeout_typ; + uint32_t block_erase_max = + (1 << qry->block_erase_timeout_max) * block_erase_typ; + uint32_t word_program_typ = 1 << qry->word_write_timeout_typ; + uint32_t word_program_max = + (1 << qry->word_write_timeout_max) * word_program_typ; + printf(" block erase typ %u ms\n", block_erase_typ); + printf(" block erase max %u ms\n", block_erase_max); + printf(" word program typ %u us\n", word_program_typ); + printf(" word program max %u us\n", word_program_max); +} + +void flash_dump_info(flash_t * flash) +{ + int i; + printf("flash at 0x%p of %d bytes with %d regions\n", + flash->f_base, flash->f_size, flash->f_nregions); + + uint16_t m_id, d_id; + if (get_id(flash->f_base, &m_id, &d_id)) { + puts(" failed to get id\n"); + } else { + printf(" manufacturer 0x%4.4x device 0x%4.4x\n", m_id, d_id); + } + + uint16_t plock; + uint8_t pdata[8]; + if (get_intel_protection(flash->f_base, &plock, pdata)) { + puts(" failed to get protection data\n"); + } else { + dump_intel_protection(plock, pdata); + } + + struct cfi_query qry; + if (get_query(flash->f_base, &qry)) { + puts(" failed to get cfi query response\n"); + } else { + dump_query_algorithms(&qry); + dump_query_timing(&qry); + } + + for (i = 0; i < flash->f_nregions; i++) { + flash_region_t *fr = &flash->f_regions[i]; + printf(" region %d: %d blocks of %d bytes at 0x%p\n", + i, fr->fr_bnum, fr->fr_bsize, fr->fr_base); + } +} + +#endif + +__ramtext +int flash_init(flash_t * flash, void *base_addr) +{ + int res; + unsigned u; + uint16_t m_id, d_id; + uint32_t base; + struct cfi_query qry; + + /* retrieve and check manufacturer and device id */ + res = get_id(base_addr, &m_id, &d_id); + if (res) { + return res; + } + if (m_id != CFI_MANUF_INTEL) { + /* we only support intel devices */ + return -ENOTSUP; + } + + /* retrieve and check query response */ + res = get_query(base_addr, &qry); + if (res) { + return res; + } + if (qry.p_id != CFI_ALGO_INTEL_3) { + /* we only support algo 3 */ + return -ENOTSUP; + } + if (qry.num_erase_regions > FLASH_MAX_REGIONS) { + /* we have a hard limit on the number of regions */ + return -ENOTSUP; + } + + /* fill in basic information */ + flash->f_base = base_addr; + flash->f_size = 1 << qry.dev_size; + + /* determine number of erase regions */ + flash->f_nregions = qry.num_erase_regions; + + /* compute actual erase region info from cfi junk */ + base = 0; + for (u = 0; u < flash->f_nregions; u++) { + flash_region_t *fr = &flash->f_regions[u]; + + fr->fr_base = (void *)base; + fr->fr_bnum = qry.erase_regions[u].b_count + 1; + fr->fr_bsize = qry.erase_regions[u].b_size * 256; + + base += fr->fr_bnum * fr->fr_bsize; + } + + return 0; +} diff --git a/src/target/firmware/include/abb/twl3025.h b/src/target/firmware/include/abb/twl3025.h new file mode 100644 index 00000000..2cd35a58 --- /dev/null +++ b/src/target/firmware/include/abb/twl3025.h @@ -0,0 +1,136 @@ +#ifndef _TWL3025_H +#define _TWL3025_H + +#define PAGE(n) (n << 7) +enum twl3025_reg { + VRPCCFG = PAGE(1) | 30, + VRPCDEV = PAGE(0) | 30, + VRPCMSK = PAGE(1) | 31, + VRPCMSKABB = PAGE(1) | 29, + VRPCSTS = PAGE(0) | 31, + /* Monitoring ADC Registers */ + MADCTRL = PAGE(0) | 13, + MADCSTAT = PAGE(0) | 24, + VBATREG = PAGE(0) | 15, + VCHGREG = PAGE(0) | 16, + ICHGREG = PAGE(0) | 17, + VBKPREG = PAGE(0) | 18, + ADIN1REG = PAGE(0) | 19, + ADIN2REG = PAGE(0) | 20, + ADIN3REG = PAGE(0) | 21, + ADIN4REG = PAGE(0) | 22, + /* Clock Generator Registers */ + TOGBR1 = PAGE(0) | 4, + TOGBR2 = PAGE(0) | 5, + PWDNRG = PAGE(1) | 9, + TAPCTRL = PAGE(1) | 19, + TAPREG = PAGE(1) | 20, + /* Automatic Frequency Control (AFC) Registers */ + AUXAFC1 = PAGE(0) | 7, + AUXAFC2 = PAGE(0) | 8, + AFCCTLADD = PAGE(1) | 21, + AFCOUT = PAGE(1) | 22, + /* Automatic Power Control (APC) Registers */ + APCDEL1 = PAGE(0) | 2, + APCDEL2 = PAGE(1) | 26, + AUXAPC = PAGE(0) | 9, + APCRAM = PAGE(0) | 10, + APCOFF = PAGE(0) | 11, + APCOUT = PAGE(1) | 12, + /* Auxiliary DAC Control Register */ + AUXDAC = PAGE(0) | 12, + /* SimCard Control Register */ + VRPCSIM = PAGE(1) | 23, + /* LED Driver Register */ + AUXLED = PAGE(1) | 24, + /* Battery Charger Interface (BCI) Registers */ + CHGREG = PAGE(0) | 25, + BCICTL1 = PAGE(0) | 28, + BCICTL2 = PAGE(0) | 29, + BCICONF = PAGE(1) | 13, + /* Interrupt and Bus Control (IBIC) Registers */ + ITMASK = PAGE(0) | 28, + ITSTATREG = PAGE(0) | 27, /* both pages! */ + PAGEREG = PAGE(0) | 1, /* both pages! */ + /* Baseband Codec (BBC) Registers */ + BULIOFF = PAGE(1) | 2, + BULQOFF = PAGE(1) | 3, + BULIDAC = PAGE(1) | 5, + BULQDAC = PAGE(1) | 4, + BULGCAL = PAGE(1) | 14, + BULDATA1 = PAGE(0) | 3, /* 16 words */ + BBCTRL = PAGE(1) | 6, + /* Voiceband Codec (VBC) Registers */ + VBCTRL1 = PAGE(1) | 8, + VBCTRL2 = PAGE(1) | 11, + VBPOP = PAGE(1) | 10, + VBUCTRL = PAGE(1) | 7, + VBDCTRL = PAGE(0) | 6, +}; +#define BULDATA2 BULDATA1 + +enum togbr2_bits { + TOGBR2_KEEPR = (1 << 0), /* Clear KEEPON bit */ + TOGBR2_KEEPS = (1 << 1), /* Set KEEPON bit */ + TOGBR2_ACTR = (1 << 2), /* Dectivate MCLK */ + TOGBR2_ACTS = (1 << 3), /* Activate MCLK */ + TOGBR2_IBUFPTR1 = (1 << 4), /* Initialize pointer of burst buffer 1 */ + TOGBR2_IBUFPTR2 = (1 << 5), /* Initialize pointer of burst buffer 2 */ + TOGBR2_IAPCPTR = (1 << 6), /* Initialize pointer of APC RAM */ +}; + +/* How a RAMP value is encoded */ +#define ABB_RAMP_VAL(up, down) ( ((down & 0x1F) << 5) | (up & 0x1F) ) + +enum twl3025_unit { + TWL3025_UNIT_AFC, + TWL3025_UNIT_MAD, + TWL3025_UNIT_ADA, + TWL3025_UNIT_VDL, + TWL3025_UNIT_VUL, +}; + +void twl3025_init(void); +void twl3025_reg_write(uint8_t reg, uint16_t data); +uint16_t twl3025_reg_read(uint8_t reg); + +void twl3025_power_off(void); + +void twl3025_clk13m(int enable); + +void twl3025_unit_enable(enum twl3025_unit unit, int on); + +enum twl3025_tsp_bits { + BULON = 0x80, + BULCAL = 0x40, + BULENA = 0x20, + BDLON = 0x10, + BDLCAL = 0x08, + BDLENA = 0x04, + STARTADC = 0x02, +}; + +extern const uint16_t twl3025_default_ramp[16]; + +/* Enqueue a TSP signal change via the TPU */ +void twl3025_tsp_write(uint8_t data); + +/* Enqueue a series of TSP commands in the TPU to (de)activate the downlink path */ +void twl3025_downlink(int on, int16_t at); + +/* Enqueue a series of TSP commands in the TPU to (de)activate the uplink path */ +void twl3025_uplink(int on, int16_t at); + +/* Update the AFC DAC value */ +void twl3025_afc_set(int16_t val); + +/* Get the AFC DAC value */ +int16_t twl3025_afc_get(void); + +/* Get the AFC DAC output value */ +uint8_t twl3025_afcout_get(void); + +/* Force a certain static AFC DAC output value */ +void twl3025_afcout_set(uint8_t val); + +#endif diff --git a/src/target/firmware/include/arm.h b/src/target/firmware/include/arm.h new file mode 100644 index 00000000..272c9c39 --- /dev/null +++ b/src/target/firmware/include/arm.h @@ -0,0 +1,7 @@ +#ifndef _ARM_H +#define _ARM_H + +void arm_enable_interrupts(void); +int arm_disable_interrupts(void); + +#endif diff --git a/src/target/firmware/include/arpa/inet.h b/src/target/firmware/include/arpa/inet.h new file mode 100644 index 00000000..9a4dd5c9 --- /dev/null +++ b/src/target/firmware/include/arpa/inet.h @@ -0,0 +1,2 @@ +/* we have this to make sure libosmocore uses our version of ntohl/htons */ +#include <byteorder.h> diff --git a/src/target/firmware/include/asm/assembler.h b/src/target/firmware/include/asm/assembler.h new file mode 100644 index 00000000..cd03e98d --- /dev/null +++ b/src/target/firmware/include/asm/assembler.h @@ -0,0 +1,113 @@ +/* + * linux/include/asm-arm/assembler.h + * + * Copyright (C) 1996-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file contains arm architecture specific defines + * for the different processors. + * + * Do not include any C declarations in this file - it is included by + * assembler source. + */ +#ifndef __ASSEMBLY__ +#error "Only include this from assembly code" +#endif + +#include <asm/ptrace.h> + +/* + * Endian independent macros for shifting bytes within registers. + */ +#ifndef __ARMEB__ +#define pull lsr +#define push lsl +#define get_byte_0 lsl #0 +#define get_byte_1 lsr #8 +#define get_byte_2 lsr #16 +#define get_byte_3 lsr #24 +#define put_byte_0 lsl #0 +#define put_byte_1 lsl #8 +#define put_byte_2 lsl #16 +#define put_byte_3 lsl #24 +#else +#define pull lsl +#define push lsr +#define get_byte_0 lsr #24 +#define get_byte_1 lsr #16 +#define get_byte_2 lsr #8 +#define get_byte_3 lsl #0 +#define put_byte_0 lsl #24 +#define put_byte_1 lsl #16 +#define put_byte_2 lsl #8 +#define put_byte_3 lsl #0 +#endif + +#define PLD(code...) + +#define MODE_USR USR_MODE +#define MODE_FIQ FIQ_MODE +#define MODE_IRQ IRQ_MODE +#define MODE_SVC SVC_MODE + +#define DEFAULT_FIQ MODE_FIQ + +/* + * LOADREGS - ldm with PC in register list (eg, ldmfd sp!, {pc}) + */ +#ifdef __STDC__ +#define LOADREGS(cond, base, reglist...)\ + ldm##cond base,reglist +#else +#define LOADREGS(cond, base, reglist...)\ + ldm/**/cond base,reglist +#endif + +/* + * Build a return instruction for this processor type. + */ +#define RETINSTR(instr, regs...)\ + instr regs + +/* + * Enable and disable interrupts + */ + .macro disable_irq + msr cpsr_c, #PSR_I_BIT | SVC_MODE + .endm + + .macro enable_irq + msr cpsr_c, #SVC_MODE + .endm + +/* + * Save the current IRQ state and disable IRQs. Note that this macro + * assumes FIQs are enabled, and that the processor is in SVC mode. + */ + .macro save_and_disable_irqs, oldcpsr + mrs \oldcpsr, cpsr + disable_irq + .endm + +/* + * Restore interrupt state previously stored in a register. We don't + * guarantee that this will preserve the flags. + */ + .macro restore_irqs, oldcpsr + msr cpsr_c, \oldcpsr + .endm + +/* + * These two are used to save LR/restore PC over a user-based access. + * The old 26-bit architecture requires that we do. On 32-bit + * architecture, we can safely ignore this requirement. + */ + .macro save_lr + .endm + + .macro restore_pc + mov pc, lr + .endm diff --git a/src/target/firmware/include/asm/atomic.h b/src/target/firmware/include/asm/atomic.h new file mode 100644 index 00000000..19e8ce6f --- /dev/null +++ b/src/target/firmware/include/asm/atomic.h @@ -0,0 +1,106 @@ +/* + * linux/include/asm-arm/atomic.h + * + * Copyright (C) 1996 Russell King. + * Copyright (C) 2002 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_ATOMIC_H +#define __ASM_ARM_ATOMIC_H + +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) { (i) } + +#define atomic_read(v) ((v)->counter) + +#include <asm/system.h> +#include <asm/compiler.h> + +#define atomic_set(v,i) (((v)->counter) = (i)) + +static inline int atomic_add_return(int i, atomic_t *v) +{ + unsigned long flags; + int val; + + local_irq_save(flags); + val = v->counter; + v->counter = val += i; + local_irq_restore(flags); + + return val; +} + +static inline int atomic_sub_return(int i, atomic_t *v) +{ + unsigned long flags; + int val; + + local_irq_save(flags); + val = v->counter; + v->counter = val -= i; + local_irq_restore(flags); + + return val; +} + +static inline int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + local_irq_restore(flags); + + return ret; +} + +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) +{ + unsigned long flags; + + local_irq_save(flags); + *addr &= ~mask; + local_irq_restore(flags); +} + +#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) + +static inline int atomic_add_unless(atomic_t *v, int a, int u) +{ + int c, old; + + c = atomic_read(v); + while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c) + c = old; + return c != u; +} +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + +#define atomic_add(i, v) (void) atomic_add_return(i, v) +#define atomic_inc(v) (void) atomic_add_return(1, v) +#define atomic_sub(i, v) (void) atomic_sub_return(i, v) +#define atomic_dec(v) (void) atomic_sub_return(1, v) + +#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0) +#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) +#define atomic_inc_return(v) (atomic_add_return(1, v)) +#define atomic_dec_return(v) (atomic_sub_return(1, v)) +#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) + +#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0) + +/* Atomic operations are already serializing on ARM */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#endif diff --git a/src/target/firmware/include/asm/bitops.h b/src/target/firmware/include/asm/bitops.h new file mode 100644 index 00000000..337d800d --- /dev/null +++ b/src/target/firmware/include/asm/bitops.h @@ -0,0 +1,225 @@ +/* + * Copyright 1995, Russell King. + * Various bits and pieces copyrights include: + * Linus Torvalds (test_bit). + * Big endian support: Copyright 2001, Nicolas Pitre + * reworked by rmk. + * + * bit 0 is the LSB of an "unsigned long" quantity. + * + * Please note that the code in this file should never be included + * from user space. Many of these are not implemented in assembler + * since they would be too costly. Also, they require privileged + * instructions (which are not available from user mode) to ensure + * that they are atomic. + */ + +#ifndef __ASM_ARM_BITOPS_H +#define __ASM_ARM_BITOPS_H + +#include <asm/system.h> + +#define smp_mb__before_clear_bit() mb() +#define smp_mb__after_clear_bit() mb() + +/* + * These functions are the basis of our bit ops. + * + * First, the atomic bitops. These use native endian. + */ +static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p |= mask; + local_irq_restore(flags); +} + +static inline void ____atomic_clear_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p &= ~mask; + local_irq_restore(flags); +} + +static inline void ____atomic_change_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p ^= mask; + local_irq_restore(flags); +} + +static inline int +____atomic_test_and_set_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res | mask; + local_irq_restore(flags); + + return res & mask; +} + +static inline int +____atomic_test_and_clear_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res & ~mask; + local_irq_restore(flags); + + return res & mask; +} + +static inline int +____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res ^ mask; + local_irq_restore(flags); + + return res & mask; +} + +//#include <asm-generic/bitops/non-atomic.h> + +/* + * A note about Endian-ness. + * ------------------------- + * + * When the ARM is put into big endian mode via CR15, the processor + * merely swaps the order of bytes within words, thus: + * + * ------------ physical data bus bits ----------- + * D31 ... D24 D23 ... D16 D15 ... D8 D7 ... D0 + * little byte 3 byte 2 byte 1 byte 0 + * big byte 0 byte 1 byte 2 byte 3 + * + * This means that reading a 32-bit word at address 0 returns the same + * value irrespective of the endian mode bit. + * + * Peripheral devices should be connected with the data bus reversed in + * "Big Endian" mode. ARM Application Note 61 is applicable, and is + * available from http://www.arm.com/. + * + * The following assumes that the data bus connectivity for big endian + * mode has been followed. + * + * Note that bit 0 is defined to be 32-bit word bit 0, not byte 0 bit 0. + */ + +/* + * Little endian assembly bitops. nr = 0 -> byte 0 bit 0. + */ +extern void _set_bit_le(int nr, volatile unsigned long * p); +extern void _clear_bit_le(int nr, volatile unsigned long * p); +extern void _change_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_set_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_clear_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_change_bit_le(int nr, volatile unsigned long * p); +extern int _find_first_zero_bit_le(const void * p, unsigned size); +extern int _find_next_zero_bit_le(const void * p, int size, int offset); +extern int _find_first_bit_le(const unsigned long *p, unsigned size); +extern int _find_next_bit_le(const unsigned long *p, int size, int offset); + +/* + * Big endian assembly bitops. nr = 0 -> byte 3 bit 0. + */ +extern void _set_bit_be(int nr, volatile unsigned long * p); +extern void _clear_bit_be(int nr, volatile unsigned long * p); +extern void _change_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_set_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_clear_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_change_bit_be(int nr, volatile unsigned long * p); +extern int _find_first_zero_bit_be(const void * p, unsigned size); +extern int _find_next_zero_bit_be(const void * p, int size, int offset); +extern int _find_first_bit_be(const unsigned long *p, unsigned size); +extern int _find_next_bit_be(const unsigned long *p, int size, int offset); + +/* + * The __* form of bitops are non-atomic and may be reordered. + */ +#define ATOMIC_BITOP_LE(name,nr,p) \ + (__builtin_constant_p(nr) ? \ + ____atomic_##name(nr, p) : \ + _##name##_le(nr,p)) + +#define ATOMIC_BITOP_BE(name,nr,p) \ + (__builtin_constant_p(nr) ? \ + ____atomic_##name(nr, p) : \ + _##name##_be(nr,p)) + +#define NONATOMIC_BITOP(name,nr,p) \ + (____nonatomic_##name(nr, p)) + +/* + * These are the little endian, atomic definitions. + */ +#define set_bit(nr,p) ATOMIC_BITOP_LE(set_bit,nr,p) +#define clear_bit(nr,p) ATOMIC_BITOP_LE(clear_bit,nr,p) +#define change_bit(nr,p) ATOMIC_BITOP_LE(change_bit,nr,p) +#define test_and_set_bit(nr,p) ATOMIC_BITOP_LE(test_and_set_bit,nr,p) +#define test_and_clear_bit(nr,p) ATOMIC_BITOP_LE(test_and_clear_bit,nr,p) +#define test_and_change_bit(nr,p) ATOMIC_BITOP_LE(test_and_change_bit,nr,p) +#define find_first_zero_bit(p,sz) _find_first_zero_bit_le(p,sz) +#define find_next_zero_bit(p,sz,off) _find_next_zero_bit_le(p,sz,off) +#define find_first_bit(p,sz) _find_first_bit_le(p,sz) +#define find_next_bit(p,sz,off) _find_next_bit_le(p,sz,off) + +#define WORD_BITOFF_TO_LE(x) ((x)) + +#if 0 +#include <asm-generic/bitops/ffz.h> +#include <asm-generic/bitops/__ffs.h> +#include <asm-generic/bitops/fls.h> +#include <asm-generic/bitops/ffs.h> + +#include <asm-generic/bitops/fls64.h> + +#include <asm-generic/bitops/sched.h> +#include <asm-generic/bitops/hweight.h> +#endif + +#define BITS_PER_LONG 32 +#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +static inline int test_bit(int nr, const volatile unsigned long *addr) +{ + return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); +} + +#endif /* _ARM_BITOPS_H */ diff --git a/src/target/firmware/include/asm/div64.h b/src/target/firmware/include/asm/div64.h new file mode 100644 index 00000000..36826168 --- /dev/null +++ b/src/target/firmware/include/asm/div64.h @@ -0,0 +1,48 @@ +#ifndef __ASM_ARM_DIV64 +#define __ASM_ARM_DIV64 + +#include <asm/system.h> + +/* + * The semantics of do_div() are: + * + * uint32_t do_div(uint64_t *n, uint32_t base) + * { + * uint32_t remainder = *n % base; + * *n = *n / base; + * return remainder; + * } + * + * In other words, a 64-bit dividend with a 32-bit divisor producing + * a 64-bit result and a 32-bit remainder. To accomplish this optimally + * we call a special __do_div64 helper with completely non standard + * calling convention for arguments and results (beware). + */ + +#ifdef __ARMEB__ +#define __xh "r0" +#define __xl "r1" +#else +#define __xl "r0" +#define __xh "r1" +#endif + +#define do_div(n,base) \ +({ \ + register unsigned int __base asm("r4") = base; \ + register unsigned long long __n asm("r0") = n; \ + register unsigned long long __res asm("r2"); \ + register unsigned int __rem asm(__xh); \ + asm( __asmeq("%0", __xh) \ + __asmeq("%1", "r2") \ + __asmeq("%2", "r0") \ + __asmeq("%3", "r4") \ + "bl __do_div64" \ + : "=r" (__rem), "=r" (__res) \ + : "r" (__n), "r" (__base) \ + : "ip", "lr", "cc"); \ + n = __res; \ + __rem; \ +}) + +#endif diff --git a/src/target/firmware/include/asm/linkage.h b/src/target/firmware/include/asm/linkage.h new file mode 100644 index 00000000..ac1c900f --- /dev/null +++ b/src/target/firmware/include/asm/linkage.h @@ -0,0 +1,18 @@ +#ifndef __ASM_LINKAGE_H +#define __ASM_LINKAGE_H + +/* asm-arm/linkage.h */ + +#define __ALIGN .align 0 +#define __ALIGN_STR ".align 0" + +/* linux/linkage.h */ + +#define ALIGN __ALIGN + +#define ENTRY(name) \ + .globl name; \ + ALIGN; \ + name: + +#endif diff --git a/src/target/firmware/include/asm/ptrace.h b/src/target/firmware/include/asm/ptrace.h new file mode 100644 index 00000000..f3a654e3 --- /dev/null +++ b/src/target/firmware/include/asm/ptrace.h @@ -0,0 +1,128 @@ +/* + * linux/include/asm-arm/ptrace.h + * + * Copyright (C) 1996-2003 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_PTRACE_H +#define __ASM_ARM_PTRACE_H + +/* + * PSR bits + */ +#define USR26_MODE 0x00000000 +#define FIQ26_MODE 0x00000001 +#define IRQ26_MODE 0x00000002 +#define SVC26_MODE 0x00000003 +#define USR_MODE 0x00000010 +#define FIQ_MODE 0x00000011 +#define IRQ_MODE 0x00000012 +#define SVC_MODE 0x00000013 +#define ABT_MODE 0x00000017 +#define UND_MODE 0x0000001b +#define SYSTEM_MODE 0x0000001f +#define MODE32_BIT 0x00000010 +#define MODE_MASK 0x0000001f +#define PSR_T_BIT 0x00000020 +#define PSR_F_BIT 0x00000040 +#define PSR_I_BIT 0x00000080 +#define PSR_J_BIT 0x01000000 +#define PSR_Q_BIT 0x08000000 +#define PSR_V_BIT 0x10000000 +#define PSR_C_BIT 0x20000000 +#define PSR_Z_BIT 0x40000000 +#define PSR_N_BIT 0x80000000 +#define PCMASK 0 + +/* + * Groups of PSR bits + */ +#define PSR_f 0xff000000 /* Flags */ +#define PSR_s 0x00ff0000 /* Status */ +#define PSR_x 0x0000ff00 /* Extension */ +#define PSR_c 0x000000ff /* Control */ + +#ifndef __ASSEMBLY__ + +/* + * This struct defines the way the registers are stored on the + * stack during a system call. Note that sizeof(struct pt_regs) + * has to be a multiple of 8. + */ +struct pt_regs { + long uregs[18]; +}; + +#define ARM_cpsr uregs[16] +#define ARM_pc uregs[15] +#define ARM_lr uregs[14] +#define ARM_sp uregs[13] +#define ARM_ip uregs[12] +#define ARM_fp uregs[11] +#define ARM_r10 uregs[10] +#define ARM_r9 uregs[9] +#define ARM_r8 uregs[8] +#define ARM_r7 uregs[7] +#define ARM_r6 uregs[6] +#define ARM_r5 uregs[5] +#define ARM_r4 uregs[4] +#define ARM_r3 uregs[3] +#define ARM_r2 uregs[2] +#define ARM_r1 uregs[1] +#define ARM_r0 uregs[0] +#define ARM_ORIG_r0 uregs[17] + +#define user_mode(regs) \ + (((regs)->ARM_cpsr & 0xf) == 0) + +#ifdef CONFIG_ARM_THUMB +#define thumb_mode(regs) \ + (((regs)->ARM_cpsr & PSR_T_BIT)) +#else +#define thumb_mode(regs) (0) +#endif + +#define processor_mode(regs) \ + ((regs)->ARM_cpsr & MODE_MASK) + +#define interrupts_enabled(regs) \ + (!((regs)->ARM_cpsr & PSR_I_BIT)) + +#define fast_interrupts_enabled(regs) \ + (!((regs)->ARM_cpsr & PSR_F_BIT)) + +#define condition_codes(regs) \ + ((regs)->ARM_cpsr & (PSR_V_BIT|PSR_C_BIT|PSR_Z_BIT|PSR_N_BIT)) + +/* Are the current registers suitable for user mode? + * (used to maintain security in signal handlers) + */ +static inline int valid_user_regs(struct pt_regs *regs) +{ + if (user_mode(regs) && + (regs->ARM_cpsr & (PSR_F_BIT|PSR_I_BIT)) == 0) + return 1; + + /* + * Force CPSR to something logical... + */ + regs->ARM_cpsr &= PSR_f | PSR_s | PSR_x | PSR_T_BIT | MODE32_BIT; + + return 0; +} + +#define pc_pointer(v) \ + ((v) & ~PCMASK) + +#define instruction_pointer(regs) \ + (pc_pointer((regs)->ARM_pc)) + +#define profile_pc(regs) instruction_pointer(regs) + +#endif /* __ASSEMBLY__ */ + +#endif + diff --git a/src/target/firmware/include/asm/swab.h b/src/target/firmware/include/asm/swab.h new file mode 100644 index 00000000..4640e271 --- /dev/null +++ b/src/target/firmware/include/asm/swab.h @@ -0,0 +1,45 @@ +/* + * arch/arm/include/asm/byteorder.h + * + * ARM Endian-ness. In little endian mode, the data bus is connected such + * that byte accesses appear as: + * 0 = d0...d7, 1 = d8...d15, 2 = d16...d23, 3 = d24...d31 + * and word accesses (data or instruction) appear as: + * d0...d31 + * + * When in big endian mode, byte accesses appear as: + * 0 = d24...d31, 1 = d16...d23, 2 = d8...d15, 3 = d0...d7 + * and word accesses (data or instruction) appear as: + * d0...d31 + */ +#ifndef __ASM_ARM_SWAB_H +#define __ASM_ARM_SWAB_H + +#include <stdint.h> +#include <defines.h> + +static inline uint32_t __arch_swab32(uint32_t x) +{ + uint32_t t; + +#ifndef __thumb__ + if (!__builtin_constant_p(x)) { + /* + * The compiler needs a bit of a hint here to always do the + * right thing and not screw it up to different degrees + * depending on the gcc version. + */ + asm ("eor\t%0, %1, %1, ror #16" : "=r" (t) : "r" (x)); + } else +#endif + t = x ^ ((x << 16) | (x >> 16)); /* eor r1,r0,r0,ror #16 */ + + x = (x << 24) | (x >> 8); /* mov r0,r0,ror #8 */ + t &= ~0x00FF0000; /* bic r1,r1,#0x00FF0000 */ + x ^= (t >> 8); /* eor r0,r0,r1,lsr #8 */ + + return x; +} +#define __arch_swab32 __arch_swab32 + +#endif diff --git a/src/target/firmware/include/asm/system.h b/src/target/firmware/include/asm/system.h new file mode 100644 index 00000000..3db0dc7a --- /dev/null +++ b/src/target/firmware/include/asm/system.h @@ -0,0 +1,123 @@ +#ifndef __ASM_ARM_SYSTEM_H +#define __ASM_ARM_SYSTEM_H + +/* Generic ARM7TDMI (ARMv4T) synchronisation primitives, mostly + * taken from Linux kernel source, licensed under GPL */ + +#define local_irq_save(x) \ + ({ \ + unsigned long temp; \ + (void) (&temp == &x); \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_save\n" \ +" orr %1, %0, #128\n" \ +" msr cpsr_c, %1" \ + : "=r" (x), "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* Save IRQ flags and disable FIQ + IRQ */ +#define local_firq_save(x) \ + ({ \ + unsigned long temp; \ + (void) (&temp == &x); \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_firq_save\n" \ +" orr %1, %0, #0xC0\n" \ +" msr cpsr_c, %1" \ + : "=r" (x), "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Enable IRQs + */ +#define local_irq_enable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_enable\n" \ +" bic %0, %0, #128\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Disable IRQs + */ +#define local_irq_disable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_disable\n" \ +" orr %0, %0, #128\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Enable FIQs + */ +#define local_fiq_enable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ stf\n" \ +" bic %0, %0, #64\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Disable FIQs + */ +#define local_fiq_disable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ clf\n" \ +" orr %0, %0, #64\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Save the current interrupt enable state. + */ +#define local_save_flags(x) \ + ({ \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_save_flags" \ + : "=r" (x) : : "memory", "cc"); \ + }) + +/* + * restore saved IRQ & FIQ state + */ +#define local_irq_restore(x) \ + __asm__ __volatile__( \ + "msr cpsr_c, %0 @ local_irq_restore\n" \ + : \ + : "r" (x) \ + : "memory", "cc") + +#define irqs_disabled() \ +({ \ + unsigned long flags; \ + local_save_flags(flags); \ + (int)(flags & PSR_I_BIT); \ +}) + +#define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" + +#endif diff --git a/src/target/firmware/include/board.h b/src/target/firmware/include/board.h new file mode 100644 index 00000000..9783ef3e --- /dev/null +++ b/src/target/firmware/include/board.h @@ -0,0 +1,8 @@ +#ifndef _BOARD_H +#define _BOARD_H + +extern const char *target_board; + +void board_init(void); + +#endif /* _BOARD_H */ diff --git a/src/target/firmware/include/byteorder.h b/src/target/firmware/include/byteorder.h new file mode 100644 index 00000000..41edb93d --- /dev/null +++ b/src/target/firmware/include/byteorder.h @@ -0,0 +1,79 @@ +#ifndef _LINUX_BYTEORDER_LITTLE_ENDIAN_H +#define _LINUX_BYTEORDER_LITTLE_ENDIAN_H + +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN 1234 +#endif +#ifndef __LITTLE_ENDIAN_BITFIELD +#define __LITTLE_ENDIAN_BITFIELD +#endif + +#include <stdint.h> +#include <swab.h> + +#define __constant_htonl(x) ___constant_swab32(x) +#define __constant_ntohl(x) ___constant_swab32(x) +#define __constant_htons(x) ___constant_swab16(x) +#define __constant_ntohs(x) ___constant_swab16(x) +#define __constant_cpu_to_le64(x) (x) +#define __constant_le64_to_cpu(x) (x) +#define __constant_cpu_to_le32(x) (x) +#define __constant_le32_to_cpu(x) (x) +#define __constant_cpu_to_le16(x) (x) +#define __constant_le16_to_cpu(x) (x) +#define __constant_cpu_to_be64(x) ___constant_swab64(x) +#define __constant_be64_to_cpu(x) ___constant_swab64(x) +#define __constant_cpu_to_be32(x) ___constant_swab32(x) +#define __constant_be32_to_cpu(x) ___constant_swab32(x) +#define __constant_cpu_to_be16(x) ___constant_swab16(x) +#define __constant_be16_to_cpu(x) ___constant_swab16(x) +#define __cpu_to_le64(x) (x) +#define __le64_to_cpu(x) (x) +#define __cpu_to_le32(x) (x) +#define __le32_to_cpu(x) (x) +#define __cpu_to_le16(x) (x) +#define __le16_to_cpu(x) (x) +#define __cpu_to_be64(x) __swab64(x) +#define __be64_to_cpu(x) __swab64(x) +#define __cpu_to_be32(x) __swab32(x) +#define __be32_to_cpu(x) __swab32(x) +#define __cpu_to_be16(x) __swab16(x) +#define __be16_to_cpu(x) __swab16(x) + +/* from include/linux/byteorder/generic.h */ +#define cpu_to_le64 __cpu_to_le64 +#define le64_to_cpu __le64_to_cpu +#define cpu_to_le32 __cpu_to_le32 +#define le32_to_cpu __le32_to_cpu +#define cpu_to_le16 __cpu_to_le16 +#define le16_to_cpu __le16_to_cpu +#define cpu_to_be64 __cpu_to_be64 +#define be64_to_cpu __be64_to_cpu +#define cpu_to_be32 __cpu_to_be32 +#define be32_to_cpu __be32_to_cpu +#define cpu_to_be16 __cpu_to_be16 +#define be16_to_cpu __be16_to_cpu + +/* + * They have to be macros in order to do the constant folding + * correctly - if the argument passed into a inline function + * it is no longer constant according to gcc.. + */ + +#undef ntohl +#undef ntohs +#undef htonl +#undef htons + +#define ___htonl(x) __cpu_to_be32(x) +#define ___htons(x) __cpu_to_be16(x) +#define ___ntohl(x) __be32_to_cpu(x) +#define ___ntohs(x) __be16_to_cpu(x) + +#define htonl(x) ___htonl(x) +#define ntohl(x) ___ntohl(x) +#define htons(x) ___htons(x) +#define ntohs(x) ___ntohs(x) + + +#endif /* _LINUX_BYTEORDER_LITTLE_ENDIAN_H */ diff --git a/src/target/firmware/include/calypso/backlight.h b/src/target/firmware/include/calypso/backlight.h new file mode 100644 index 00000000..3a6abd55 --- /dev/null +++ b/src/target/firmware/include/calypso/backlight.h @@ -0,0 +1,10 @@ +#ifndef _CAL_BACKLIGHT_H +#define _CAL_BACKLIGHT_H + +/* Switch backlight to PWL mode (or back) */ +void bl_mode_pwl(int on); + +/* Set the backlight level */ +void bl_level(uint8_t level); + +#endif /* CAL_BACKLIGHT_H */ diff --git a/src/target/firmware/include/calypso/clock.h b/src/target/firmware/include/calypso/clock.h new file mode 100644 index 00000000..abcfde1d --- /dev/null +++ b/src/target/firmware/include/calypso/clock.h @@ -0,0 +1,67 @@ +#ifndef _CALYPSO_CLK_H +#define _CALYPSO_CLK_H + +#include <stdint.h> + +#define CALYPSO_PLL26_52_MHZ ((2 << 8) | 0) +#define CALYPSO_PLL26_86_7_MHZ ((10 << 8) | 2) +#define CALYPSO_PLL26_87_MHZ ((3 << 8) | 0) +#define CALYPSO_PLL13_104_MHZ ((8 << 8) | 0) + +enum mclk_div { + _ARM_MCLK_DIV_1 = 0, + ARM_MCLK_DIV_1 = 1, + ARM_MCLK_DIV_2 = 2, + ARM_MCLK_DIV_3 = 3, + ARM_MCLK_DIV_4 = 4, + ARM_MCLK_DIV_5 = 5, + ARM_MCLK_DIV_6 = 6, + ARM_MCLK_DIV_7 = 7, + ARM_MCLK_DIV_1_5 = 0x80 | 1, + ARM_MCLK_DIV_2_5 = 0x80 | 2, +}; + +void calypso_clock_set(uint8_t vtcxo_div2, uint16_t inp, enum mclk_div mclk_div); +void calypso_pll_set(uint16_t inp); +void calypso_clk_dump(void); + +/* CNTL_RST */ +enum calypso_rst { + RESET_DSP = (1 << 1), + RESET_EXT = (1 << 2), + RESET_WDOG = (1 << 3), +}; + +void calypso_reset_set(enum calypso_rst calypso_rst, int active); +int calypso_reset_get(enum calypso_rst); + +enum calypso_bank { + CALYPSO_nCS0 = 0, + CALYPSO_nCS1 = 2, + CALYPSO_nCS2 = 4, + CALYPSO_nCS3 = 6, + CALYPSO_nCS7 = 8, + CALYPSO_CS4 = 0xa, + CALYPSO_nCS6 = 0xc, +}; + +enum calypso_mem_width { + CALYPSO_MEM_8bit = 0, + CALYPSO_MEM_16bit = 1, + CALYPSO_MEM_32bit = 2, +}; + +void calypso_mem_cfg(enum calypso_bank bank, uint8_t ws, + enum calypso_mem_width width, int we); + +/* Enable or disable the internal bootrom mapped to 0x0000'0000 */ +void calypso_bootrom(int enable); + +/* Enable or disable the debug unit */ +void calypso_debugunit(int enable); + +/* configure the RHEA bus bridge[s] */ +void calypso_rhea_cfg(uint8_t fac0, uint8_t fac1, uint8_t timeout, + uint8_t ws_h, uint8_t ws_l, uint8_t w_en0, uint8_t w_en1); + +#endif /* _CALYPSO_CLK_H */ diff --git a/src/target/firmware/include/calypso/dma.h b/src/target/firmware/include/calypso/dma.h new file mode 100644 index 00000000..00b9bde7 --- /dev/null +++ b/src/target/firmware/include/calypso/dma.h @@ -0,0 +1,6 @@ +#ifndef _CALYPSO_DMA_H +#define _CALYPSO_DMA_H + +void dma_init(void); + +#endif /* _CALYPSO_DMA_H */ diff --git a/src/target/firmware/include/calypso/dsp.h b/src/target/firmware/include/calypso/dsp.h new file mode 100644 index 00000000..e4801cbf --- /dev/null +++ b/src/target/firmware/include/calypso/dsp.h @@ -0,0 +1,41 @@ +#ifndef _CALYPSO_DSP_H +#define _CALYPSO_DSP_H + +#include <calypso/dsp_api.h> + +#define CAL_DSP_TGT_BB_LVL 80 + +struct gsm_time; + +struct dsp_api { + T_NDB_MCU_DSP *ndb; + T_DB_DSP_TO_MCU *db_r; + T_DB_MCU_TO_DSP *db_w; + T_PARAM_MCU_DSP *param; + int r_page; + int w_page; + int r_page_used; + int frame_ctr; +}; + +extern struct dsp_api dsp_api; + +void dsp_power_on(void); +void dsp_dump_version(void); +void dsp_dump(void); +void dsp_checksum_task(void); +void dsp_api_memset(uint16_t *ptr, int octets); +void dsp_memcpy_to_api(volatile uint16_t *dsp_buf, const uint8_t *mcu_buf, int n, int be); +void dsp_memcpy_from_api(uint8_t *mcu_buf, const volatile uint16_t *dsp_buf, int n, int be); +void dsp_load_afc_dac(uint16_t afc); +void dsp_load_apc_dac(uint16_t apc); +void dsp_load_tch_param(struct gsm_time *next_time, + uint8_t chan_mode, uint8_t chan_type, uint8_t chan_sub, + uint8_t tch_loop, uint8_t sync_tch, uint8_t tn); +void dsp_load_ciph_param(int mode, uint8_t *key); +void dsp_end_scenario(void); + +void dsp_load_rx_task(uint16_t task, uint8_t burst_id, uint8_t tsc); +void dsp_load_tx_task(uint16_t task, uint8_t burst_id, uint8_t tsc); + +#endif diff --git a/src/target/firmware/include/calypso/dsp_api.h b/src/target/firmware/include/calypso/dsp_api.h new file mode 100644 index 00000000..f9751f37 --- /dev/null +++ b/src/target/firmware/include/calypso/dsp_api.h @@ -0,0 +1,1560 @@ +#ifndef _CAL_DSP_API_H +#define _CAL_DSP_API_H + +/* This is a header file with structures imported from the TSM30 source code (l1_defty.h) + * + * As this header file only is a list of definitions and data structures, it is + * not ocnsidered to be a copyrightable work itself. + * + * Nonetheless, it might be good to rewrite it (without ugly typedefs!) */ + +#if(L1_DYN_DSP_DWNLD == 1) + #include "l1_dyn_dwl_defty.h" +#endif + +/* Include a header file that defines everything this l1_defty.h needs */ +#include "l1_environment.h" + +#define BASE_API_NDB 0xFFD001A8L /* 268 words */ +#define BASE_API_PARAM 0xFFD00862L /* 57 words */ +#define BASE_API_R_PAGE_0 0xFFD00050L /* 20 words */ +#define BASE_API_R_PAGE_1 0xFFD00078L /* 20 words */ +#define BASE_API_W_PAGE_0 0xFFD00000L /* 20 words */ +#define BASE_API_W_PAGE_1 0xFFD00028L /* 20 words */ + + +/***********************************************************/ +/* */ +/* Data structure for global info components. */ +/* */ +/***********************************************************/ + +typedef struct +{ + API d_task_d; // (0) Downlink task command. + API d_burst_d; // (1) Downlink burst identifier. + API d_task_u; // (2) Uplink task command. + API d_burst_u; // (3) Uplink burst identifier. + API d_task_md; // (4) Downlink Monitoring (FB/SB) command. +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API d_background; // (5) Background tasks +#else + API d_reserved; // (5) Reserved +#endif + API d_debug; // (6) Debug/Acknowledge/general purpose word. + API d_task_ra; // (7) RA task command. + API d_fn; // (8) FN, in Rep. period and FN%104, used for TRAFFIC/TCH only. + // bit [0..7] -> b_fn_report, FN in the normalized reporting period. + // bit [8..15] -> b_fn_sid, FN % 104, used for SID positionning. + API d_ctrl_tch; // (9) Tch channel description. + // bit [0..3] -> b_chan_mode, channel mode. + // bit [4..5] -> b_chan_type, channel type. + // bit [6] -> reset SACCH + // bit [7] -> vocoder ON + // bit [8] -> b_sync_tch_ul, synchro. TCH/UL. + // bit [9] -> b_sync_tch_dl, synchro. TCH/DL. + // bit [10] -> b_stop_tch_ul, stop TCH/UL. + // bit [11] -> b_stop_tch_dl, stop TCH/DL. + // bit [12.13] -> b_tch_loop, tch loops A/B/C. + API hole; // (10) unused hole. + +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_ctrl_abb; // (11) Bit field indicating the analog baseband register to send. + // bit [0] -> b_ramp: the ramp information(a_ramp[]) is located in NDB + // bit [1.2] -> unused + // bit [3] -> b_apcdel: delays-register in NDB + // bit [4] -> b_afc: freq control register in DB + // bit [5..15] -> unused +#endif + API a_a5fn[2]; // (12..13) Encryption Frame number. + // word 0, bit [0..4] -> T2. + // word 0, bit [5..10] -> T3. + // word 1, bit [0..11] -> T1. + API d_power_ctl; // (14) Power level control. + API d_afc; // (15) AFC value (enabled by "b_afc" in "d_ctrl_TCM4400 or in d_ctrl_abb"). + API d_ctrl_system; // (16) Controle Register for RESET/RESUME. + // bit [0..2] -> b_tsq, training sequence. + // bit [3] -> b_bcch_freq_ind, BCCH frequency indication. + // bit [15] -> b_task_abort, DSP task abort command. +} +T_DB_MCU_TO_DSP; + +typedef struct +{ + API d_task_d; // (0) Downlink task command. + API d_burst_d; // (1) Downlink burst identifier. + API d_task_u; // (2) Uplink task command. + API d_burst_u; // (3) Uplink burst identifier. + API d_task_md; // (4) Downlink Monitoring (FB/SB) task command. +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API d_background; // (5) Background tasks +#else + API d_reserved; // (5) Reserved +#endif + API d_debug; // (6) Debug/Acknowledge/general purpose word. + API d_task_ra; // (7) RA task command. + +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API a_serv_demod[4]; // ( 8..11) Serv. cell demod. result, array of 4 words (D_TOA,D_PM,D_ANGLE,D_SNR). + API a_pm[3]; // (12..14) Power measurement results, array of 3 words. + API a_sch[5]; // (15..19) Header + SB information, array of 5 words. +#else + API a_pm[3]; // ( 8..10) Power measurement results, array of 3 words. + API a_serv_demod[4]; // (11..14) Serv. cell demod. result, array of 4 words (D_TOA,D_PM,D_ANGLE,D_SNR). + API a_sch[5]; // (15..19) Header + SB information, array of 5 words. +#endif +} +T_DB_DSP_TO_MCU; + +#if (DSP == 34) || (DSP == 35) || (DSP == 36) // NDB GSM + typedef struct + { + // MISC Tasks + API d_dsp_page; + + // DSP status returned (DSP --> MCU). + API d_error_status; + + // RIF control (MCU -> DSP). + API d_spcx_rif; + + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + + API d_dsp_test; + + // Words dedicated to Software version (DSP code + Patch) + API d_version_number1; + API d_version_number2; + + API d_debug_ptr; + API d_debug_bk; + + API d_pll_config; + + // GSM/GPRS DSP Debug trace support + API p_debug_buffer; + API d_debug_buffer_size; + API d_debug_trace_type; + + #if (W_A_DSP_IDLE3 == 1) + // DSP report its state: 0 run, 1 Idle1, 2 Idle2, 3 Idle3. + API d_dsp_state; + // 5 words are reserved for any possible mapping modification + API d_hole1_ndb[2]; + #else + // 6 words are reserved for any possible mapping modification + API d_hole1_ndb[3]; + #endif + + #if (AMR == 1) + API p_debug_amr; + #else + API d_hole_debug_amr; + #endif + + #if (CHIPSET == 12) + #if (DSP == 35) || (DSP == 36) + API d_hole2_ndb[1]; + API d_mcsi_select; + #else + API d_hole2_ndb[2]; + #endif + #else + API d_hole2_ndb[2]; + #endif + + // New words APCDEL1 and APCDEL2 for 2TX: TX/PRACH combinations + API d_apcdel1_bis; + API d_apcdel2_bis; + + + // New registers due to IOTA analog base band + API d_apcdel2; + API d_vbctrl2; + API d_bulgcal; + + // Analog Based Band + API d_afcctladd; + + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_vbctrl1; + #endif + + API d_bbctrl; + + // Monitoring tasks control (MCU <- DSP) + // FB task + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // SB Task + API a_sch26[5]; // Header + SB information, array of 5 words. + + API d_audio_gain_ul; + API d_audio_gain_dl; + + // Controller of the melody E2 audio compressor + API d_audio_compressor_ctrl; + + // AUDIO module + API d_audio_init; + API d_audio_status; + + // Audio tasks + // TONES (MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + API d_shiftdl; + API d_shiftul; + + API d_aec_ctrl; + + API d_es_level_api; + API d_mu_api; + + // Melody Ringer module + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + + // selection of the melody format + API d_melody_selection; + + // Holes due to the format melody E1 + API a_melo_holes[3]; + + // Speech Recognition module + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + + // Audio buffer + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // V42bis module + API d_v42b_nego0; + API d_v42b_nego1; + API d_v42b_control; + API d_v42b_ratio_ind; + API d_mcu_control; + API d_mcu_control_sema; + + // Background tasks + API d_background_enable; + API d_background_abort; + API d_background_state; + API d_max_background; + API a_background_tasks[16]; + API a_back_task_io[16]; + + // GEA module defined in l1p_deft.h (the following section is overlaid with GPRS NDB memory) + API d_gea_mode_ovly; + API a_gea_kc_ovly[4]; + +#if (ANLG_FAM == 3) + // SYREN specific registers + API d_vbpop; + API d_vau_delay_init; + API d_vaud_cfg; + API d_vauo_onoff; + API d_vaus_vol; + API d_vaud_pll; + API d_hole3_ndb[1]; +#elif ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + + API d_hole3_ndb[7]; + +#endif + + // word used for the init of USF threshold + API d_thr_usf_detect; + + // Encryption module + API d_a5mode; // Encryption Mode. + + API d_sched_mode_gprs_ovly; + + // 7 words are reserved for any possible mapping modification + API d_hole4_ndb[5]; + + // Ramp definition for Omega device + API a_ramp[16]; + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + + // Traffic downlink data frames......(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API a_kc[4]; // Encryption Key Code. + + // Integrated Data Services module + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + // GTT API mapping for DSP code 34 (for test only) + #if (L1_GTT == 1) + API d_tty_status; + API d_tty_detect_thres; + API d_ctm_detect_shift; + API d_tty_fa_thres; + API d_tty_mod_norm; + API d_tty_reset_buffer_ul; + API d_tty_loop_ctrl; + API p_tty_loop_buffer; + #else + API a_tty_holes[8]; + #endif + + API a_sr_holes0[414]; + + #if (L1_NEW_AEC) + // new AEC + API d_cont_filter; + API d_granularity_att; + API d_coef_smooth; + API d_es_level_max; + API d_fact_vad; + API d_thrs_abs; + API d_fact_asd_fil; + API d_fact_asd_mut; + API d_far_end_pow_h; + API d_far_end_pow_l; + API d_far_end_noise_h; + API d_far_end_noise_l; + #else + API a_new_aec_holes[12]; + #endif // L1_NEW_AEC + + // Speech recognition model + API a_sr_holes1[145]; + API d_cport_init; + API d_cport_ctrl; + API a_cport_cfr[2]; + API d_cport_tcl_tadt; + API d_cport_tdat; + API d_cport_tvs; + API d_cport_status; + API d_cport_reg_value; + + API a_cport_holes[1011]; + + API a_model[1041]; + + // EOTD buffer +#if (L1_EOTD==1) + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; +#else + API a_eotd_holes[22]; +#endif + // AMR ver 1.0 buffers + API a_amr_config[4]; + API a_ratscch_ul[6]; + API a_ratscch_dl[6]; + API d_amr_snr_est; // estimation of the SNR of the AMR speech block + #if (L1_VOICE_MEMO_AMR) + API d_amms_ul_voc; + #else + API a_voice_memo_amr_holes[1]; + #endif + API d_thr_onset_afs; // thresh detection ONSET AFS + API d_thr_sid_first_afs; // thresh detection SID_FIRST AFS + API d_thr_ratscch_afs; // thresh detection RATSCCH AFS + API d_thr_update_afs; // thresh detection SID_UPDATE AFS + API d_thr_onset_ahs; // thresh detection ONSET AHS + API d_thr_sid_ahs; // thresh detection SID frames AHS + API d_thr_ratscch_marker;// thresh detection RATSCCH MARKER + API d_thr_sp_dgr; // thresh detection SPEECH DEGRADED/NO_DATA + API d_thr_soft_bits; + #if (MELODY_E2) + API d_melody_e2_osc_stop; + API d_melody_e2_osc_active; + API d_melody_e2_semaphore; + API a_melody_e2_osc[16][3]; + API d_melody_e2_globaltimefactor; + API a_melody_e2_instrument_ptr[8]; + API d_melody_e2_deltatime; + + #if (AMR_THRESHOLDS_WORKAROUND) + API a_d_macc_thr_afs[8]; + API a_d_macc_thr_ahs[6]; + #else + API a_melody_e2_holes0[14]; + #endif + + API a_melody_e2_holes1[693]; + API a_dsp_trace[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_DSP_TRACE]; + API a_melody_e2_instrument_wave[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_INSTRUMENT]; + #else + API d_holes[61]; + #if (AMR_THRESHOLDS_WORKAROUND) + API a_d_macc_thr_afs[8]; + API a_d_macc_thr_ahs[6]; + #endif + #endif + + } + T_NDB_MCU_DSP; +#elif (DSP == 33) // NDB GSM + typedef struct + { + // MISC Tasks + API d_dsp_page; + + // DSP status returned (DSP --> MCU). + API d_error_status; + + // RIF control (MCU -> DSP). + API d_spcx_rif; + + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + + API d_dsp_test; + + // Words dedicated to Software version (DSP code + Patch) + API d_version_number1; + API d_version_number2; + + API d_debug_ptr; + API d_debug_bk; + + API d_pll_config; + + // GSM/GPRS DSP Debug trace support + API p_debug_buffer; + API d_debug_buffer_size; + API d_debug_trace_type; + + #if (W_A_DSP_IDLE3 == 1) + // DSP report its state: 0 run, 1 Idle1, 2 Idle2, 3 Idle3. + API d_dsp_state; + // 10 words are reserved for any possible mapping modification + API d_hole1_ndb[5]; + #else + // 11 words are reserved for any possible mapping modification + API d_hole1_ndb[6]; + #endif + + // New words APCDEL1 and APCDEL2 for 2TX: TX/PRACH combinations + API d_apcdel1_bis; + API d_apcdel2_bis; + + + // New registers due to IOTA analog base band + API d_apcdel2; + API d_vbctrl2; + API d_bulgcal; + + // Analog Based Band + API d_afcctladd; + + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_vbctrl1; + #endif + + API d_bbctrl; + + // Monitoring tasks control (MCU <- DSP) + // FB task + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // SB Task + API a_sch26[5]; // Header + SB information, array of 5 words. + + API d_audio_gain_ul; + API d_audio_gain_dl; + + // Controller of the melody E2 audio compressor + API d_audio_compressor_ctrl; + + // AUDIO module + API d_audio_init; + API d_audio_status; + + // Audio tasks + // TONES (MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + API d_shiftdl; + API d_shiftul; + + API d_aec_ctrl; + + API d_es_level_api; + API d_mu_api; + + // Melody Ringer module + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + + // selection of the melody format + API d_melody_selection; + + // Holes due to the format melody E1 + API a_melo_holes[3]; + + // Speech Recognition module + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + + // Audio buffer + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // V42bis module + API d_v42b_nego0; + API d_v42b_nego1; + API d_v42b_control; + API d_v42b_ratio_ind; + API d_mcu_control; + API d_mcu_control_sema; + + // Background tasks + API d_background_enable; + API d_background_abort; + API d_background_state; + API d_max_background; + API a_background_tasks[16]; + API a_back_task_io[16]; + + // GEA module defined in l1p_deft.h (the following section is overlaid with GPRS NDB memory) + API d_gea_mode_ovly; + API a_gea_kc_ovly[4]; + + API d_hole3_ndb[8]; + + // Encryption module + API d_a5mode; // Encryption Mode. + + API d_sched_mode_gprs_ovly; + + // 7 words are reserved for any possible mapping modification + API d_hole4_ndb[5]; + + // Ramp definition for Omega device + API a_ramp[16]; + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + + // Traffic downlink data frames......(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API a_kc[4]; // Encryption Key Code. + + // Integrated Data Services module + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + #if (L1_NEW_AEC) + // new AEC + API a_new_aec_holes[422]; + API d_cont_filter; + API d_granularity_att; + API d_coef_smooth; + API d_es_level_max; + API d_fact_vad; + API d_thrs_abs; + API d_fact_asd_fil; + API d_fact_asd_mut; + API d_far_end_pow_h; + API d_far_end_pow_l; + API d_far_end_noise_h; + API d_far_end_noise_l; + #endif + + // Speech recognition model + #if (L1_NEW_AEC) + API a_sr_holes[1165]; + #else + API a_sr_holes[1599]; + #endif // L1_NEW_AEC + API a_model[1041]; + + // EOTD buffer + #if (L1_EOTD==1) + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; + #else + API a_eotd_holes[22]; + #endif + + #if (MELODY_E2) + API a_melody_e2_holes0[27]; + API d_melody_e2_osc_used; + API d_melody_e2_osc_active; + API d_melody_e2_semaphore; + API a_melody_e2_osc[16][3]; + API d_melody_e2_globaltimefactor; + API a_melody_e2_instrument_ptr[8]; + API a_melody_e2_holes1[708]; + API a_dsp_trace[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_DSP_TRACE]; + API a_melody_e2_instrument_wave[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_INSTRUMENT]; + #endif + } + T_NDB_MCU_DSP; + +#elif ((DSP == 32) || (DSP == 31)) + typedef struct + { + // Monitoring tasks control..........(MCU <- DSP) + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + #if (SPEECH_RECO) + // FACCH downlink information........(!!) + API a_fu[3]; // Header + FACCH uplink information + // The size of this buffer is 15 word but some speech reco words + // are overlayer with this buffer. This is the reason why the size is 3 instead of 15. + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API sr_hole1; // hole + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API sr_holes_1[4]; // hole + #else + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + #endif + + // Traffic uplink data frames........(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API d_a5mode; // Encryption Mode. + API a_kc[4]; // Encryption Key Code. + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + // OMEGA...........................(MCU -> DSP). + #if ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + API a_ramp[16]; + #if (MELODY_E1) + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + #if (DSP==31) + // selection of the melody format + API d_melody_selection; + API holes[9]; + #else // DSP==32 + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #endif // DSP 32 + + #else // NO MELODY E1 + #if (DSP==31) + // selection of the melody format + API d_melody_selection; + API holes[43]; // 43 unused holes. + #else // DSP==32 + API holes[34]; // 34 unused holes. + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #endif //DSP == 32 + #endif // NO MELODY E1 + + API d_debug3; + API d_debug2; + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + API d_afcctladd; + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_aec_ctrl; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif (ANLG_FAM == 2) + API d_vbctrl1; + #endif + + API d_bbctrl; + #else + #error DSPCODE not supported with given ANALOG + #endif //(ANALOG)1, 2 + //...................................(MCU -> DSP). + API a_sch26[5]; // Header + SB information, array of 5 words. + + // TONES.............................(MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + + // PLL...............................(MCU -> DSP). + API d_pll_clkmod1; + API d_pll_clkmod2; + + // DSP status returned..........(DSP --> MCU). + API d_error_status; + + // RIF control.......................(MCU -> DSP). + API d_spcx_rif; + + API d_shiftdl; + API d_shiftul; + + API p_saec_prog; + API p_aec_prog; + API p_spenh_prog; + + API a_ovly[75]; + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + #if (SPEECH_RECO) + API a_data_buf_ul[3]; + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + API sr_holes_2[6]; + API a_data_buf_dl[37]; + + API a_hole[24]; + + API d_sched_mode_gprs_ovly; + + API fir_holes1[384]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + + API a_model[1041]; // array of the speech reco model + #else + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + API a_hole[24]; + + API d_sched_mode_gprs_ovly; + + API fir_holes1[384]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + +#if (L1_EOTD ==1) + API a_eotd_hole[369]; + + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; +#endif + #endif + } + T_NDB_MCU_DSP; + + +#else // OTHER DSP CODE like 17 + +typedef struct +{ + // Monitoring tasks control..........(MCU <- DSP) + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + #if (DATA14_4 == 0) + API a_dd_0[20]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[20]; // Header + DATA traffic downlink information, sub. chan. 1. + #endif + #if (DATA14_4 == 1) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + #endif + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + #if (SPEECH_RECO) + // FACCH downlink information........(!!) + API a_fu[3]; // Header + FACCH uplink information + // The size of this buffer is 15 word but some speech reco words + // are overlayer with this buffer. This is the reason why the size is 3 instead of 15. + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API sr_hole1; // hole + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API sr_holes_1[4]; // hole + #else + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + #endif + + // Traffic uplink data frames........(!!) + #if (DATA14_4 == 0) + API a_du_0[20]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[20]; // Header + DATA traffic uplink information, sub. chan. 1. + #endif + #if (DATA14_4 == 1) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + #endif + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API d_a5mode; // Encryption Mode. + API a_kc[4]; // Encryption Key Code. + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + // OMEGA...........................(MCU -> DSP). + +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + API a_ramp[16]; + #if (MELODY_E1) + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + #if (DSP == 17) + // selection of the melody format + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #else + API d_melody_selection; + API holes[9]; + #endif + #else // NO MELODY E1 + // selection of the melody format + #if (DSP == 17) + API holes[34]; // 34 unused holes. + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4] + #else + // selection of the melody format + API d_melody_selection; + API holes[43]; // 43 unused holes. + #endif + #endif + API d_debug3; + API d_debug2; + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + API d_afcctladd; + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_aec_ctrl; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif (ANLG_FAM == 2) + API d_vbctrl1; + #endif + API d_bbctrl; + + #else + #error DSPCODE not supported with given ANALOG + #endif //(ANALOG)1, 2 + //...................................(MCU -> DSP). + API a_sch26[5]; // Header + SB information, array of 5 words. + + // TONES.............................(MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + + // PLL...............................(MCU -> DSP). + API d_pll_clkmod1; + API d_pll_clkmod2; + + // DSP status returned..........(DSP --> MCU). + API d_error_status; + + // RIF control.......................(MCU -> DSP). + API d_spcx_rif; + + API d_shiftdl; + API d_shiftul; + + #if (AEC == 1) + // AEC control.......................(MCU -> DSP). + #if (VOC == FR_EFR) + API p_aec_init; + API p_aec_prog; + API p_spenh_init; + API p_spenh_prog; + #endif + + #if (VOC == FR_HR_EFR) + API p_saec_prog; + API p_aec_prog; + API p_spenh_prog; + #endif + #endif + + API a_ovly[75]; + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + #if (SPEECH_RECO) + API a_data_buf_ul[3]; + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + API sr_holes_2[6]; + API a_data_buf_dl[37]; + + API fir_holes1[409]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + API a_model[1041]; // array of the speech reco model + #else + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + API fir_holes1[409]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + #endif +} +T_NDB_MCU_DSP; +#endif + +#if (DSP == 34) || (DSP == 35) || (DSP == 36) +typedef struct +{ + API_SIGNED d_transfer_rate; + + // Common GSM/GPRS + // These words specified the latencies to applies on some peripherics + API_SIGNED d_lat_mcu_bridge; + API_SIGNED d_lat_mcu_hom2sam; + API_SIGNED d_lat_mcu_bef_fast_access; + API_SIGNED d_lat_dsp_after_sam; + + // DSP Start address + API_SIGNED d_gprs_install_address; + + API_SIGNED d_misc_config; + + API_SIGNED d_cn_sw_workaround; + + API_SIGNED d_hole2_param[4]; + + //...................................Frequency Burst. + API_SIGNED d_fb_margin_beg; + API_SIGNED d_fb_margin_end; + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + // V42Bis module + API_SIGNED d_v42b_switch_hyst; + API_SIGNED d_v42b_switch_min; + API_SIGNED d_v42b_switch_max; + API_SIGNED d_v42b_reset_delay; + + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + + //...................................CHED + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + + API_SIGNED d_sd_min_thr_tchefs; + API_SIGNED d_ma_min_thr_tchefs; + API_SIGNED d_md_max_thr_tchefs; + API_SIGNED d_md1_max_thr_tchefs; + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + + // FACCH module + API_SIGNED d_facch_thr; + + // IDS module + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + API_SIGNED d_gsm_bgd_mgt; + + // FIR coefficients + API a_fir_holes[4]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; +} +T_PARAM_MCU_DSP; +#elif (DSP == 33) +typedef struct +{ + API_SIGNED d_transfer_rate; + + // Common GSM/GPRS + // These words specified the latencies to applies on some peripherics + API_SIGNED d_lat_mcu_bridge; + API_SIGNED d_lat_mcu_hom2sam; + API_SIGNED d_lat_mcu_bef_fast_access; + API_SIGNED d_lat_dsp_after_sam; + + // DSP Start address + API_SIGNED d_gprs_install_address; + + API_SIGNED d_misc_config; + + API_SIGNED d_cn_sw_workaround; + + #if DCO_ALGO + API_SIGNED d_cn_dco_param; + + API_SIGNED d_hole2_param[3]; + #else + API_SIGNED d_hole2_param[4]; + #endif + + //...................................Frequency Burst. + API_SIGNED d_fb_margin_beg; + API_SIGNED d_fb_margin_end; + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + // V42Bis module + API_SIGNED d_v42b_switch_hyst; + API_SIGNED d_v42b_switch_min; + API_SIGNED d_v42b_switch_max; + API_SIGNED d_v42b_reset_delay; + + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + + //...................................CHED + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + + API_SIGNED d_sd_min_thr_tchefs; + API_SIGNED d_ma_min_thr_tchefs; + API_SIGNED d_md_max_thr_tchefs; + API_SIGNED d_md1_max_thr_tchefs; + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + + // FACCH module + API_SIGNED d_facch_thr; + + // IDS module + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + API_SIGNED d_gsm_bgd_mgt; + + // FIR coefficients + API a_fir_holes[4]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; +} +T_PARAM_MCU_DSP; + +#else + +typedef struct +{ + //...................................Frequency Burst. + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + API_SIGNED hole[1]; + API_SIGNED d_transfer_rate; + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + #if (VOC == FR_HR) || (VOC == FR_HR_EFR) + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + #endif + + #if (VOC == FR_EFR) || (VOC == FR_HR_EFR) + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + #endif + + //...................................TCH Full Speech. + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + #if (VOC == FR) || (VOC == FR_HR) || (VOC == FR_HR_EFR) + //...................................TCH Half Speech. + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + #endif + + #if (VOC == FR_EFR) || (VOC == FR_HR_EFR) + //...................................TCH Enhanced FR Speech. + API_SIGNED d_sd_min_thr_tchefs; //(24L *C_POND_RED) + API_SIGNED d_ma_min_thr_tchefs; //(1200L *C_POND_RED) + API_SIGNED d_md_max_thr_tchefs; //(2000L *C_POND_RED) + API_SIGNED d_md1_max_thr_tchefs; //(160L *C_POND_RED) + API_SIGNED d_hole1; + #endif + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + API_SIGNED d_facch_thr; + API_SIGNED d_dsp_test; + + + #if (DATA14_4 == 0 ) || (VOC == FR_HR_EFR) + API_SIGNED d_patch_addr1; + API_SIGNED d_patch_data1; + API_SIGNED d_patch_addr2; + API_SIGNED d_patch_data2; + API_SIGNED d_patch_addr3; + API_SIGNED d_patch_data3; + API_SIGNED d_patch_addr4; + API_SIGNED d_patch_data4; + #endif + + //................................... + API_SIGNED d_version_number; // DSP patch version + API_SIGNED d_ti_version; // customer number. No more used since 1.5 + + API_SIGNED d_dsp_page; + + #if IDS + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + #endif + + +} +T_PARAM_MCU_DSP; +#endif + +#if (DSP_DEBUG_TRACE_ENABLE == 1) +typedef struct +{ + API d_debug_ptr_begin; + API d_debug_ptr_end; +} +T_DB2_DSP_TO_MCU; +#endif + +/* DSP error as per ndb->d_error_status */ +enum dsp_error { + DSP_ERR_RHEA = 0x0001, + DSP_ERR_IQ_SAMPLES = 0x0004, + DSP_ERR_DMA_PROG = 0x0008, + DSP_ERR_DMA_TASK = 0x0010, + DSP_ERR_DMA_PEND = 0x0020, + DSP_ERR_VM = 0x0080, + DSP_ERR_DMA_UL_TASK = 0x0100, + DSP_ERR_DMA_UL_PROG = 0x0200, + DSP_ERR_DMA_UL_PEND = 0x0400, + DSP_ERR_STACK_OV = 0x0800, +}; + +/* How an ABB register + value is expressed in the API RAM */ +#define ABB_VAL(reg, val) ( (((reg) & 0x1F) << 1) | (((val) & 0x3FF) << 6) ) + +/* How an ABB register + value | TRUE is expressed in the API RAM */ +#define ABB_VAL_T(reg, val) (ABB_VAL(reg, val) | 1) + +#endif /* _CAL_DSP_API_H */ diff --git a/src/target/firmware/include/calypso/du.h b/src/target/firmware/include/calypso/du.h new file mode 100644 index 00000000..f2eae091 --- /dev/null +++ b/src/target/firmware/include/calypso/du.h @@ -0,0 +1,32 @@ +/* Calypso DU (Debug Unit) Driver */ + +/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _CALYPSO_DU_H +#define _CALYPSO_DU_H + +#include <calypso/clock.h> + +void calypso_du_init(); +void calypso_du_stop(); +void calypsu_du_dump(); + +#endif /* _CALYPSO_DU_H */ diff --git a/src/target/firmware/include/calypso/irq.h b/src/target/firmware/include/calypso/irq.h new file mode 100644 index 00000000..5ea59797 --- /dev/null +++ b/src/target/firmware/include/calypso/irq.h @@ -0,0 +1,49 @@ +#ifndef _CALYPSO_IRQ_H +#define _CALYPSO_IRQ_H + +enum irq_nr { + IRQ_WATCHDOG = 0, + IRQ_TIMER1 = 1, + IRQ_TIMER2 = 2, + IRQ_TSP_RX = 3, + IRQ_TPU_FRAME = 4, + IRQ_TPU_PAGE = 5, + IRQ_SIMCARD = 6, + IRQ_UART_MODEM = 7, + IRQ_KEYPAD_GPIO = 8, + IRQ_RTC_TIMER = 9, + IRQ_RTC_ALARM_I2C = 10, + IRQ_ULPD_GAUGING = 11, + IRQ_EXTERNAL = 12, + IRQ_SPI = 13, + IRQ_DMA = 14, + IRQ_API = 15, + IRQ_SIM_DETECT = 16, + IRQ_EXTERNAL_FIQ = 17, + IRQ_UART_IRDA = 18, + IRQ_ULPD_GSM_TIMER = 19, + IRQ_GEA = 20, + _NR_IRQ +}; + +typedef void irq_handler(enum irq_nr nr); + +/* initialize IRQ driver and enable interrupts */ +void irq_init(void); + +/* enable a certain interrupt */ +void irq_enable(enum irq_nr nr); + +/* disable a certain interrupt */ +void irq_disable(enum irq_nr nr); + +/* configure a certain interrupt */ +void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio); + +/* register an interrupt handler */ +void irq_register_handler(enum irq_nr nr, irq_handler *handler); + +/* Install the exception handlers to where the ROM loader jumps */ +void calypso_exceptions_install(void); + +#endif /* _CALYPSO_IRQ_H */ diff --git a/src/target/firmware/include/calypso/l1_environment.h b/src/target/firmware/include/calypso/l1_environment.h new file mode 100644 index 00000000..d4d442cc --- /dev/null +++ b/src/target/firmware/include/calypso/l1_environment.h @@ -0,0 +1,385 @@ +#include <stdint.h> + +typedef unsigned short API; +typedef signed short API_SIGNED; + +#define FAR + +#define CHIPSET 12 +#define DSP 36 +#define ANLG_FAM 2 /* Iota */ + +/* MFTAB */ +#define L1_MAX_FCT 5 /* Max number of fctions in a frame */ +#define MFTAB_SIZE 20 + +#define NBMAX_CARRIER 174+374 /* Number of carriers (GSM-Ext + DCS */ + +#define DPAGC_FIFO_LEN 4 + +#define SIZE_HIST 10 + +#if !L1_GPRS +# define NBR_DL_L1S_TASKS 32 +#else +# define NBR_DL_L1S_TASKS 45 +#endif + +#define NBR_L1A_PROCESSES 46 + +#define W_A_DSP_IDLE3 1 + + + +// Identifier for all DSP tasks. +// ...RX & TX tasks identifiers. +#define NO_DSP_TASK 0 // No task. +#define NP_DSP_TASK 21 // Normal Paging reading task. +#define EP_DSP_TASK 22 // Extended Paging reading task. +#define NBS_DSP_TASK 19 // Normal BCCH serving reading task. +#define EBS_DSP_TASK 20 // Extended BCCH serving reading task. +#define NBN_DSP_TASK 17 // Normal BCCH neighbour reading task. +#define EBN_DSP_TASK 18 // Extended BCCH neighbour reading task. +#define ALLC_DSP_TASK 24 // CCCH reading task while performing FULL BCCH/CCCH reading task. +#define CB_DSP_TASK 25 // CBCH reading task. +#define DDL_DSP_TASK 26 // SDCCH/D (data) reading task. +#define ADL_DSP_TASK 27 // SDCCH/A (SACCH) reading task. +#define DUL_DSP_TASK 12 // SDCCH/D (data) transmit task. +#define AUL_DSP_TASK 11 // SDCCH/A (SACCH) transmit task. +#define RACH_DSP_TASK 10 // RACH transmit task. +#define TCHT_DSP_TASK 13 // TCH Traffic data DSP task id (RX or TX) +#define TCHA_DSP_TASK 14 // TCH SACCH data DSP task id (RX or TX) +#define TCHD_DSP_TASK 28 // TCH Traffic data DSP task id (RX or TX) + +#define TCH_DTX_UL 15 // Replace UL task in DSP->MCU com. to say "burst not transmitted". + +#if (L1_GPRS) + // Identifier for DSP tasks Packet dedicated. + // ...RX & TX tasks identifiers. + //------------------------------------------------------------------------ + // WARNING ... Need to aligned following macro with MCU/DSP GPRS Interface + //------------------------------------------------------------------------ + #define PNP_DSP_TASK 30 + #define PEP_DSP_TASK 31 + #define PALLC_DSP_TASK 32 + #define PBS_DSP_TASK 33 + + #define PTCCH_DSP_TASK 33 + +#endif + +// Identifier for measurement, FB / SB search tasks. +// Values 1,2,3 reserved for "number of measurements". +#define FB_DSP_TASK 5 // Freq. Burst reading task in Idle mode. +#define SB_DSP_TASK 6 // Sync. Burst reading task in Idle mode. +#define TCH_FB_DSP_TASK 8 // Freq. Burst reading task in Dedicated mode. +#define TCH_SB_DSP_TASK 9 // Sync. Burst reading task in Dedicated mode. +#define IDLE1 1 + +// Debug tasks +#define CHECKSUM_DSP_TASK 33 +#define TST_NDB 35 // Checksum DSP->MCU +#define TST_DB 36 // DB communication check +#define INIT_VEGA 37 +#define DSP_LOOP_C 38 + +// Identifier for measurement, FB / SB search tasks. +// Values 1,2,3 reserved for "number of measurements". +#define TCH_LOOP_A 31 +#define TCH_LOOP_B 32 + +// bits in d_gsm_bgd_mgt - background task management +#define B_DSPBGD_RECO 1 // start of reco in dsp background +#define B_DSPBGD_UPD 2 // start of alignement update in dsp background +#define B_DSPBGD_STOP_RECO 256 // stop of reco in dsp background +#define B_DSPBGD_STOP_UPD 512 // stop of alignement update in dsp background + +// bit in d_pll_config +#define B_32KHZ_CALIB (1 << 14) // force DSP in Idle1 during 32 khz calibration +// **************************************************************** +// NDB AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS +// **************************************************************** +// bits in d_tch_mode +#define B_EOTD (1 << 0) // EOTD mode +#define B_PLAY_UL (1 << 3) // Play UL +#define B_DCO_ON (1 << 4) // DCO ON/OFF +#define B_AUDIO_ASYNC (1 << 1) // WCP reserved + +// **************************************************************** +// PARAMETER AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS +// **************************************************************** +#define C_POND_RED 1L +// below values are defined in the file l1_time.h +//#define D_NSUBB_IDLE 296L +//#define D_NSUBB_DEDIC 30L +#define D_FB_THR_DET_IACQ 0x3333L +#define D_FB_THR_DET_TRACK 0x28f6L +#define D_DC_OFF_THRES 0x7fffL +#define D_DUMMY_THRES 17408L +#define D_DEM_POND_GEWL 26624L +#define D_DEM_POND_RED 20152L +#define D_HOLE 0L +#define D_TRANSFER_RATE 0x6666L + +// Full Rate vocoder definitions. +#define D_MACCTHRESH1 7872L +#define D_MLDT -4L +#define D_MACCTHRESH 7872L +#define D_GU 5772L +#define D_GO 7872L +#define D_ATTMAX 53L +#define D_SM -892L +#define D_B 208L +#define D_SD_MIN_THR_TCHFS 15L //(24L *C_POND_RED) +#define D_MA_MIN_THR_TCHFS 738L //(1200L *C_POND_RED) +#define D_MD_MAX_THR_TCHFS 1700L //(2000L *C_POND_RED) +#define D_MD1_MAX_THR_TCHFS 99L //(160L *C_POND_RED) + +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + // Frequency burst definitions + #define D_FB_MARGIN_BEG 24 + #define D_FB_MARGIN_END 22 + + // V42bis definitions + #define D_V42B_SWITCH_HYST 16L + #define D_V42B_SWITCH_MIN 64L + #define D_V42B_SWITCH_MAX 250L + #define D_V42B_RESET_DELAY 10L + + // Latencies definitions + #if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + // C.f. BUG1404 + #define D_LAT_MCU_BRIDGE 0x000FL + #else + #define D_LAT_MCU_BRIDGE 0x0009L + #endif + + #define D_LAT_MCU_HOM2SAM 0x000CL + + #define D_LAT_MCU_BEF_FAST_ACCESS 0x0005L + #define D_LAT_DSP_AFTER_SAM 0x0004L + + // Background Task in GSM mode: Initialization. + #define D_GSM_BGD_MGT 0L + +#if (CHIPSET == 4) + #define D_MISC_CONFIG 0L +#elif (CHIPSET == 7) || (CHIPSET == 8) || (CHIPSET == 10) || (CHIPSET == 11) || (CHIPSET == 12) + #define D_MISC_CONFIG 1L +#else + #define D_MISC_CONFIG 0L +#endif + +#endif + +// Hall Rate vocoder and ched definitions. + +#define D_SD_MIN_THR_TCHHS 37L +#define D_MA_MIN_THR_TCHHS 344L +#define D_MD_MAX_THR_TCHHS 2175L +#define D_MD1_MAX_THR_TCHHS 138L +#define D_SD_AV_THR_TCHHS 1845L +#define D_WED_FIL_TC 0x7c00L +#define D_WED_FIL_INI 4650L +#define D_X_MIN 15L +#define D_X_MAX 23L +#define D_Y_MIN 703L +#define D_Y_MAX 2460L +#define D_SLOPE 135L +#define D_WED_DIFF_THRESHOLD 406L +#define D_MABFI_MIN_THR_TCHHS 5320L +#define D_LDT_HR -5 +#define D_MACCTRESH_HR 6500 +#define D_MACCTRESH1_HR 6500 +#define D_GU_HR 2620 +#define D_GO_HR 3700 +#define D_B_HR 182 +#define D_SM_HR -1608 +#define D_ATTMAX_HR 53 + +// Enhanced Full Rate vocoder and ched definitions. + +#define C_MLDT_EFR -4 +#define C_MACCTHRESH_EFR 8000 +#define C_MACCTHRESH1_EFR 8000 +#define C_GU_EFR 4522 +#define C_GO_EFR 6500 +#define C_B_EFR 174 +#define C_SM_EFR -878 +#define C_ATTMAX_EFR 53 +#define D_SD_MIN_THR_TCHEFS 15L //(24L *C_POND_RED) +#define D_MA_MIN_THR_TCHEFS 738L //(1200L *C_POND_RED) +#define D_MD_MAX_THR_TCHEFS 1230L //(2000L *C_POND_RED) +#define D_MD1_MAX_THR_TCHEFS 99L //(160L *C_POND_RED) + + +// Integrated Data Services definitions. +#define D_MAX_OVSPD_UL 8 +// Detect frames containing 90% of 1s as synchro frames +#define D_SYNC_THRES 0x3f50 +// IDLE frames are only frames with 100 % of 1s +#define D_IDLE_THRES 0x4000 +#define D_M1_THRES 5 +#define D_MAX_OVSP_DL 8 + +// d_ra_act: bit field definition +#define B_F48BLK 5 + +// Mask for b_itc information (d_ra_conf) +#define CE_MASK 0x04 + +#define D_FACCH_THR 0 +#define D_DSP_TEST 0 +#define D_VERSION_NUMBER 0 +#define D_TI_VERSION 0 + + +/*------------------------------------------------------------------------------*/ +/* */ +/* DEFINITIONS FOR DSP <-> MCU COMMUNICATION. */ +/* ++++++++++++++++++++++++++++++++++++++++++ */ +/* */ +/*------------------------------------------------------------------------------*/ +// COMMUNICATION Interrupt definition +//------------------------------------ +#define ALL_16BIT 0xffffL +#define B_GSM_PAGE (1 << 0) +#define B_GSM_TASK (1 << 1) +#define B_MISC_PAGE (1 << 2) +#define B_MISC_TASK (1 << 3) + +#define B_GSM_PAGE_MASK (ALL_16BIT ^ B_GSM_PAGE) +#define B_GSM_TASK_MASK (ALL_16BIT ^ B_GSM_TASK) +#define B_MISC_PAGE_MASK (ALL_16BIT ^ B_MISC_PAGE) +#define B_MISC_TASK_MASK (ALL_16BIT ^ B_MISC_TASK) + +// Common definition +//---------------------------------- +// Index to *_DEMOD* arrays. +#define D_TOA 0 // Time Of Arrival. +#define D_PM 1 // Power Measurement. +#define D_ANGLE 2 // Angle (AFC correction) +#define D_SNR 3 // Signal / Noise Ratio. + +// Bit name/position definitions. +#define B_FIRE0 5 // Fire result bit 0. (00 -> NO ERROR) (01 -> ERROR CORRECTED) +#define B_FIRE1 6 // Fire result bit 1. (10 -> ERROR) (11 -> unused) +#define B_SCH_CRC 8 // CRC result for SB decoding. (1 for ERROR). +#define B_BLUD 15 // Uplink,Downlink data block Present. (1 for PRESENT). +#define B_AF 14 // Activity bit: 1 if data block is valid. +#define B_BFI 2 // Bad Frame Indicator +#define B_UFI 0 // UNRELIABLE FRAME Indicator +#define B_ECRC 9 // Enhanced full rate CRC bit +#define B_EMPTY_BLOCK 10 // for voice memo purpose, this bit is used to determine + +#if (DEBUG_DEDIC_TCH_BLOCK_STAT == 1) + #define FACCH_GOOD 10 + #define FACCH_BAD 11 +#endif + +#if (AMR == 1) + // Place of the RX type in the AMR block header + #define RX_TYPE_SHIFT 3 + #define RX_TYPE_MASK 0x0038 + + // Place of the vocoder type in the AMR block header + #define VOCODER_TYPE_SHIFT 0 + #define VOCODER_TYPE_MASK 0x0007 + + // List of the possible RX types in a_dd block + #define SPEECH_GOOD 0 + #define SPEECH_DEGRADED 1 + #define ONSET 2 + #define SPEECH_BAD 3 + #define SID_FIRST 4 + #define SID_UPDATE 5 + #define SID_BAD 6 + #define AMR_NO_DATA 7 + #define AMR_INHIBIT 8 + + // List of possible RX types in RATSCCH block + #define C_RATSCCH_GOOD 5 + + // List of the possible AMR channel rate + #define AMR_CHANNEL_4_75 0 + #define AMR_CHANNEL_5_15 1 + #define AMR_CHANNEL_5_9 2 + #define AMR_CHANNEL_6_7 3 + #define AMR_CHANNEL_7_4 4 + #define AMR_CHANNEL_7_95 5 + #define AMR_CHANNEL_10_2 6 + #define AMR_CHANNEL_12_2 7 + + // Types of RATSCCH blocks + #define C_RATSCCH_UNKNOWN 0 + #define C_RATSCCH_CMI_PHASE_REQ 1 + #define C_RATSCCH_AMR_CONFIG_REQ_MAIN 2 + #define C_RATSCCH_AMR_CONFIG_REQ_ALT 3 + #define C_RATSCCH_AMR_CONFIG_REQ_ALT_IGNORE 4 // Alternative AMR_CONFIG_REQ with updates coming in the next THRES_REQ block + #define C_RATSCCH_THRES_REQ 5 + + // These flags define a bitmap that indicates which AMR parameters are being modified by a RATSCCH + #define C_AMR_CHANGE_CMIP 0 + #define C_AMR_CHANGE_ACS 1 + #define C_AMR_CHANGE_ICM 2 + #define C_AMR_CHANGE_THR1 3 + #define C_AMR_CHANGE_THR2 4 + #define C_AMR_CHANGE_THR3 5 + #define C_AMR_CHANGE_HYST1 6 + #define C_AMR_CHANGE_HYST2 7 + #define C_AMR_CHANGE_HYST3 8 + + // CMIP default value + #define C_AMR_CMIP_DEFAULT 1 // According to ETSI specification 05.09, cmip is always 1 by default (new channel, handover...) + +#endif +// "d_ctrl_tch" bits positions for TCH configuration. +#define B_CHAN_MODE 0 +#define B_CHAN_TYPE 4 +#define B_RESET_SACCH 6 +#define B_VOCODER_ON 7 +#define B_SYNC_TCH_UL 8 +#if (AMR == 1) + #define B_SYNC_AMR 9 +#else +#define B_SYNC_TCH_DL 9 +#endif +#define B_STOP_TCH_UL 10 +#define B_STOP_TCH_DL 11 +#define B_TCH_LOOP 12 +#define B_SUBCHANNEL 15 + +// "d_ctrl_abb" bits positions for conditionnal loading of abb registers. +#define B_RAMP 0 +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2) || (ANLG_FAM == 3)) + #define B_BULRAMPDEL 3 // Note: this name is changed + #define B_BULRAMPDEL2 2 // Note: this name is changed + #define B_BULRAMPDEL_BIS 9 + #define B_BULRAMPDEL2_BIS 10 +#endif +#define B_AFC 4 + +// "d_ctrl_system" bits positions. +#define B_TSQ 0 +#define B_BCCH_FREQ_IND 3 +#define B_TASK_ABORT 15 // Abort RF tasks for DSP. + +/* Channel type definitions for DEDICATED mode */ +#define INVALID_CHANNEL 0 +#define TCH_F 1 +#define TCH_H 2 +#define SDCCH_4 3 +#define SDCCH_8 4 + +/* Channel mode definitions for DEDICATED mode */ +#define SIG_ONLY_MODE 0 // signalling only +#define TCH_FS_MODE 1 // speech full rate +#define TCH_HS_MODE 2 // speech half rate +#define TCH_96_MODE 3 // data 9,6 kb/s +#define TCH_48F_MODE 4 // data 4,8 kb/s full rate +#define TCH_48H_MODE 5 // data 4,8 kb/s half rate +#define TCH_24F_MODE 6 // data 2,4 kb/s full rate +#define TCH_24H_MODE 7 // data 2,4 kb/s half rate +#define TCH_EFR_MODE 8 // enhanced full rate +#define TCH_144_MODE 9 // data 14,4 kb/s half rate + diff --git a/src/target/firmware/include/calypso/misc.h b/src/target/firmware/include/calypso/misc.h new file mode 100644 index 00000000..4e480938 --- /dev/null +++ b/src/target/firmware/include/calypso/misc.h @@ -0,0 +1,8 @@ +#ifndef _CAL_MISC_H +#define _CAL_MISC_H + +void memdump_range(unsigned int *ptr, unsigned int len); +void dump_mem(void); +void dump_dev_id(void); + +#endif /* _CAL_MISC_H */ diff --git a/src/target/firmware/include/calypso/rtc.h b/src/target/firmware/include/calypso/rtc.h new file mode 100644 index 00000000..17528d00 --- /dev/null +++ b/src/target/firmware/include/calypso/rtc.h @@ -0,0 +1,6 @@ +#ifndef _CALYPSO_RTC_H +#define _CALYPSO_RTC_H + +void rtc_init(void); + +#endif /* _CALYPSO_RTC_H */ diff --git a/src/target/firmware/include/calypso/sim.h b/src/target/firmware/include/calypso/sim.h new file mode 100755 index 00000000..b2a21642 --- /dev/null +++ b/src/target/firmware/include/calypso/sim.h @@ -0,0 +1,191 @@ +/* Driver for Simcard Controller inside TI Calypso/Iota */ + +/* (C) 2010 by Philipp Fabian Benedikt Maier <philipp-maier@runningserver.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _CALYPSO_SIM_H +#define _CALYPSO_SIM_H + +/* == REGISTERS IN THE IOTA BASEBAND == */ + +/* SimCard Control Register */ +#define VRPCSIM_SIMLEN (1 << 3) /* Enable level shifter */ +#define VRPCSIM_SIMRSU (1 << 2) /* voltage regulator output status */ +#define VRPCSIM_RSIMEN (1 << 1) /* Voltage regulator enable */ +#define VRPCSIM_SIMSEL 1 /* Select the VRSIM output voltage 1=2.9V, 0=1.8V */ + + + +/* == REGISTERS IN THE CALYPSO CPU == */ + +/* Reg_sim_cmd register (R/W) - FFFE:0000 */ +#define REG_SIM_CMD 0xFFFE0000 /* register address */ +#define REG_SIM_CMD_CMDCARDRST 1 /* SIM card reset sequence */ +#define REG_SIM_CMD_CMDIFRST (1 << 1) /* SIM interface software reset */ +#define REG_SIM_CMD_CMDSTOP (1 << 2) /* SIM card stop procedure */ +#define REG_SIM_CMD_CMDSTART (1 << 3) /* SIM card start procedure */ +#define REG_SIM_CMD_MODULE_CLK_EN (1 << 4) /* Clock of the module */ + +/* Reg_sim_stat register (R) - FFFE:0002 */ +#define REG_SIM_STAT 0xFFFE0002 /* register address */ +#define REG_SIM_STAT_STATNOCARD 1 /* card presence, 0 = no card, 1 = card detected */ +#define REG_SIM_STAT_STATTXPAR (1 << 1) /* parity check for transmit byte, 0 = parity error, 1 = parity OK */ +#define REG_SIM_STAT_STATFIFOFULL (1 << 2) /* FIFO content, 1 = FIFO full */ +#define REG_SIM_STAT_STATFIFOEMPTY (1 << 3) /* FIFO content, 1 = FIFO empty */ + +/* Reg_sim_conf1 register (R/W) - FFFE:0004 */ +#define REG_SIM_CONF1 0xFFFE0004 /* register address */ +#define REG_SIM_CONF1_CONFCHKPAR 1 /* enable parity check on reception */ +#define REG_SIM_CONF1_CONFCODCONV (1 << 1) /* coding convention: (TS character) */ +#define REG_SIM_CONF1_CONFTXRX (1 << 2) /* SIO line direction */ +#define REG_SIM_CONF1_CONFSCLKEN (1 << 3) /* SIM clock */ +#define REG_SIM_CONF1_reserved (1 << 4) /* ETU period */ +#define REG_SIM_CONF1_CONFSCLKDIV (1 << 5) /* SIM clock frequency */ +#define REG_SIM_CONF1_CONFSCLKLEV (1 << 6) /* SIM clock idle level */ +#define REG_SIM_CONF1_CONFETUPERIOD (1 << 7) /* ETU period */ +#define REG_SIM_CONF1_CONFBYPASS (1 << 8) /* bypass hardware timers and start and stop sequences */ +#define REG_SIM_CONF1_CONFSVCCLEV (1 << 9) /* logic level on SVCC (used if CONFBYPASS = 1) */ +#define REG_SIM_CONF1_CONFSRSTLEV (1 << 10) /* logic level on SRST (used if CONFBYPASS = 1) */ +#define REG_SIM_CONF1_CONFTRIG 11 /* FIFO trigger level */ +#define REG_SIM_CONF1_CONFTRIG_0 (1 << 11) +#define REG_SIM_CONF1_CONFTRIG_1 (1 << 12) +#define REG_SIM_CONF1_CONFTRIG_2 (1 << 13) +#define REG_SIM_CONF1_CONFTRIG_3 (1 << 14) +#define REG_SIM_CONF1_CONFTRIG_MASK 0xF +#define REG_SIM_CONF1_CONFSIOLOW (1 << 15) /* SIO - 0 = no effect, 1 = force low */ + +/* Reg_sim_conf2 register (R/W) - FFFE:0006 */ +#define REG_SIM_CONF2 0xFFFE0006 /* register address */ +#define REG_SIM_CONF2_CONFTFSIM 0 /* time delay for filtering of SIM_CD */ +#define REG_SIM_CONF2_CONFTFSIM_0 1 /* time-unit = 1024 * TCK13M (card extraction) */ +#define REG_SIM_CONF2_CONFTFSIM_1 (1 << 1) /* or */ +#define REG_SIM_CONF2_CONFTFSIM_2 (1 << 2) /* time-unit = 8192 * TCK13M (card insertion) */ +#define REG_SIM_CONF2_CONFTFSIM_3 (1 << 3) +#define REG_SIM_CONF2_CONFTFSIM_MASK 0xF +#define REG_SIM_CONF2_CONFTDSIM 4 /* time delay for contact activation/deactivation */ +#define REG_SIM_CONF2_CONFTDSIM_0 (1 << 4) /* time unit = 8 * TCKETU */ +#define REG_SIM_CONF2_CONFTDSIM_1 (1 << 5) +#define REG_SIM_CONF2_CONFTDSIM_2 (1 << 6) +#define REG_SIM_CONF2_CONFTDSIM_3 (1 << 7) +#define REG_SIM_CONF2_CONFTDSIM_MASK 0xF +#define REG_SIM_CONF2_CONFWAITI 8 /* CONFWAITI overflow wait time between two received */ +#define REG_SIM_CONF2_CONFWAITI_0 (1 << 8) /* character time unit = 960 *D * TCKETU */ +#define REG_SIM_CONF2_CONFWAITI_1 (1 << 9) /* with D parameter = 1 or 8 (TA1 character) */ +#define REG_SIM_CONF2_CONFWAITI_2 (1 << 10) +#define REG_SIM_CONF2_CONFWAITI_3 (1 << 11) +#define REG_SIM_CONF2_CONFWAITI_4 (1 << 12) +#define REG_SIM_CONF2_CONFWAITI_5 (1 << 13) +#define REG_SIM_CONF2_CONFWAITI_6 (1 << 14) +#define REG_SIM_CONF2_CONFWAITI_7 (1 << 15) +#define REG_SIM_CONF2_CONFWAITI_MASK 0xFF + +/* Reg_sim_it register (R) - FFFE:0008 */ +#define REG_SIM_IT 0xFFFE0008 /* register address */ +#define REG_SIM_IT_SIM_NATR 1 /* 0 = on read access to REG_SIM_IT, 1 = no answer to reset */ +#define REG_SIM_IT_SIM_WT (1 << 1) /* 0 = on read access to REG_SIM_IT, 1 = character underflow */ +#define REG_SIM_IT_SIM_OV (1 << 2) /* 0 = on read access to REG_SIM_IT, 1 = receive overflow */ +#define REG_SIM_IT_SIM_TX (1 << 3) /* 0 = on write access to REG_SIM_DTX or */ + /* on switching from transmit to receive, mode (CONFTXRX bit) */ + /* 1 = waiting for character to transmit */ +#define REG_SIM_IT_SIM_RX (1 << 4) /* 0 = on read access to REG_SIM_DRX */ + /* 1 = waiting characters to be read */ + +/* Reg_sim_drx register (R) - FFFE:000A */ +#define REG_SIM_DRX 0xFFFE000A /* register address */ +#define REG_SIM_DRX_SIM_DRX 0 /* next data byte in FIFO available for reading */ +#define REG_SIM_DRX_SIM_DRX_0 1 +#define REG_SIM_DRX_SIM_DRX_1 (1 << 1) +#define REG_SIM_DRX_SIM_DRX_2 (1 << 2) +#define REG_SIM_DRX_SIM_DRX_3 (1 << 3) +#define REG_SIM_DRX_SIM_DRX_4 (1 << 4) +#define REG_SIM_DRX_SIM_DRX_5 (1 << 5) +#define REG_SIM_DRX_SIM_DRX_6 (1 << 6) +#define REG_SIM_DRX_SIM_DRX_7 (1 << 7) +#define REG_SIM_DRX_SIM_DRX_MASK 0xFF +#define REG_SIM_DRX_STATRXPAR (1 << 8) /* parity-check for received byte */ + +/* Reg_sim_dtx register (R/W) - FFFE:000C */ +#define REG_SIM_DTX 0xFFFE000C /* register address */ +#define REG_SIM_DTX_SIM_DTX_0 /* next data byte to be transmitted */ +#define REG_SIM_DTX_SIM_DTX_1 +#define REG_SIM_DTX_SIM_DTX_2 +#define REG_SIM_DTX_SIM_DTX_3 +#define REG_SIM_DTX_SIM_DTX_4 +#define REG_SIM_DTX_SIM_DTX_5 +#define REG_SIM_DTX_SIM_DTX_6 +#define REG_SIM_DTX_SIM_DTX_7 + +/* Reg_sim_maskit register (R/W) - FFFE:000E */ +#define REG_SIM_MASKIT 0xFFFE000E /* register address */ +#define REG_SIM_MASKIT_MASK_SIM_NATR 1 /* No-answer-to-reset interrupt */ +#define REG_SIM_MASKIT_MASK_SIM_WT (1 << 1) /* Character wait-time overflow interrupt */ +#define REG_SIM_MASKIT_MASK_SIM_OV (1 << 2) /* Receive overflow interrupt */ +#define REG_SIM_MASKIT_MASK_SIM_TX (1 << 3) /* Waiting character to transmit interrupt */ +#define REG_SIM_MASKIT_MASK_SIM_RX (1 << 4) /* Waiting characters to be read interrupt */ +#define REG_SIM_MASKIT_MASK_SIM_CD (1 << 5) /* SIM card insertion/extraction interrupt */ + +/* Reg_sim_it_cd register (R) - FFFE:0010 */ +#define REG_SIM_IT_CD 0xFFFE0010 /* register address */ +#define REG_SIM_IT_CD_IT_CD 1 /* 0 = on read access to REG_SIM_IT_CD, */ + /* 1 = SIM card insertion/extraction */ + + +#define SIM_DEBUG_OUTPUTDELAY 200 /* Output delay to minimize stress with some uart bugs */ +#define SIM_DEBUG 0 /* 0=Debug messages are off / 1=Debug messages are on */ +#define SIM_OPERATION_DELAY 100 /* Time between operations like reset, vcc apply ect... */ + + +void calypso_sim_regdump(void); /* Display Register dump */ + +int calypso_sim_powerup(uint8_t *atr); /* Apply power to the simcard (see note 1) */ +int calypso_sim_reset(uint8_t *atr); /* reset the simcard (see note 1) */ + + +void calypso_sim_powerdown(void); /* Powerdown simcard */ + +/* APDU transmission modes */ +#define SIM_APDU_PUT 0 /* Transmit a data body to the card */ +#define SIM_APDU_GET 1 /* Fetch data from the card eg. GET RESOPNSE */ + +/* Transceive T0 Apdu to sim acording to GSM 11.11 Page 34 */ +int calypso_sim_transceive(uint8_t cla, /* Class (in GSM context mostly 0xA0 */ + uint8_t ins, /* Instruction */ + uint8_t p1, /* First parameter */ + uint8_t p2, /* Second parameter */ + uint8_t p3le, /* Length of the data that should be transceived */ + uint8_t *data, /* Data payload */ + uint8_t *status, /* Status word (2 byte array, see note 1) */ + uint8_t mode); /* Mode of operation: 1=GET, 0=PUT */ + + /* Note 1: You can use a null-pointer (0) if you are not interested in + the status word */ + +/* Transmission of raw data */ +int calypso_sim_receive(uint8_t *data); /* Receive raw data through the sim interface */ +int calypso_sim_transmit(uint8_t *data, int length); /* Transmit raw data through the sim interface */ + +void calypso_sim_init(void); /* Initialize simcard interface */ + + +/* Known Bugs: + 1.) After powering down the simcard communication stops working +*/ + +#endif /* _CALYPSO_SIM_H */ diff --git a/src/target/firmware/include/calypso/timer.h b/src/target/firmware/include/calypso/timer.h new file mode 100644 index 00000000..694e4ebc --- /dev/null +++ b/src/target/firmware/include/calypso/timer.h @@ -0,0 +1,25 @@ +#ifndef _CAL_TIMER_H +#define _CAL_TIMER_H + +/* Enable or Disable a timer */ +void hwtimer_enable(int num, int on); + +/* Configure pre-scaler and if timer is auto-reload */ +void hwtimer_config(int num, uint8_t pre_scale, int auto_reload); + +/* Load a timer with the given value */ +void hwtimer_load(int num, uint16_t val); + +/* Read the current timer value */ +uint16_t hwtimer_read(int num); + +/* Enable or disable the watchdog */ +void wdog_enable(int on); + +/* Reset cpu using watchdog */ +void wdog_reset(void); + +/* power up the timers */ +void hwtimer_init(void); + +#endif /* _CAL_TIMER_H */ diff --git a/src/target/firmware/include/calypso/tpu.h b/src/target/firmware/include/calypso/tpu.h new file mode 100644 index 00000000..2db95aa1 --- /dev/null +++ b/src/target/firmware/include/calypso/tpu.h @@ -0,0 +1,122 @@ +#ifndef _CALYPSO_TPU_H +#define _CALYPSO_TPU_H + +#define BITS_PER_TDMA 1250 +#define QBITS_PER_TDMA (BITS_PER_TDMA * 4) /* 5000 */ +#define TPU_RANGE QBITS_PER_TDMA +#define SWITCH_TIME (TPU_RANGE-10) + +/* Assert or de-assert TPU reset */ +void tpu_reset(int active); +/* Enable or Disable a new scenario loaded into the TPU */ +void tpu_enable(int active); +/* Enable or Disable the clock of teh TPU Module */ +void tpu_clk_enable(int active); +/* Enable Frame Interrupt generation on next frame. DSP will reset it */ +void tpu_dsp_frameirq_enable(void); +/* Is a Frame interrupt still pending for the DSP ? */ +int tpu_dsp_fameirq_pending(void); +/* Rewind the TPU, i.e. restart enqueueing instructions at the base addr */ +void tpu_rewind(void); +/* Enqueue a raw TPU instruction */ +void tpu_enqueue(uint16_t instr); +/* Initialize TPU and TPU driver */ +void tpu_init(void); +/* (Busy)Wait until TPU is idle */ +void tpu_wait_idle(void); +/* Enable FRAME interrupt generation */ +void tpu_frame_irq_en(int mcu, int dsp); +/* Force the generation of a DSP interrupt */ +void tpu_force_dsp_frame_irq(void); + +/* Get the current TPU SYNCHRO register */ +uint16_t tpu_get_synchro(void); +/* Get the current TPU OFFSET register */ +uint16_t tpu_get_offset(void); + +enum tpu_instr { + TPU_INSTR_AT = (1 << 13), + TPU_INSTR_OFFSET = (2 << 13), + TPU_INSTR_SYNCHRO = (3 << 13), /* Loading delta synchro value in TPU synchro register */ + TPU_INSTR_WAIT = (5 << 13), /* Wait a certain period (in GSM qbits) */ + TPU_INSTR_SLEEP = (0 << 13), /* Stop the sequencer by disabling TPU ENABLE bit in ctrl reg */ + /* data processing */ + TPU_INSTR_MOVE = (4 << 13), +}; + +/* Addresses internal to the TPU, only accessible via MOVE */ +enum tpu_reg_int { + TPUI_TSP_CTRL1 = 0x00, + TPUI_TSP_CTRL2 = 0x01, + TPUI_TX_1 = 0x04, + TPUI_TX_2 = 0x03, + TPUI_TX_3 = 0x02, + TPUI_TX_4 = 0x05, + TPUI_TSP_ACT_L = 0x06, + TPUI_TSP_ACT_U = 0x07, + TPUI_TSP_SET1 = 0x09, + TPUI_TSP_SET2 = 0x0a, + TPUI_TSP_SET3 = 0x0b, + TPUI_DSP_INT_PG = 0x10, + TPUI_GAUGING_EN = 0x11, +}; + +enum tpui_ctrl2_bits { + TPUI_CTRL2_RD = (1 << 0), + TPUI_CTRL2_WR = (1 << 1), +}; + +static inline uint16_t tpu_mod5000(int16_t time) +{ + if (time < 0) + return time + 5000; + if (time >= 5000) + return time - 5000; + return time; +} + +/* Enqueue a SLEEP operation (stop sequencer by disabling TPU ENABLE bit) */ +static inline void tpu_enq_sleep(void) +{ + tpu_enqueue(TPU_INSTR_SLEEP); +} + +/* Enqueue a MOVE operation */ +static inline void tpu_enq_move(uint8_t addr, uint8_t data) +{ + tpu_enqueue(TPU_INSTR_MOVE | (data << 5) | (addr & 0x1f)); +} + +/* Enqueue an AT operation */ +static inline void tpu_enq_at(int16_t time) +{ + tpu_enqueue(TPU_INSTR_AT | tpu_mod5000(time)); +} + +/* Enqueue a SYNC operation */ +static inline void tpu_enq_sync(int16_t time) +{ + tpu_enqueue(TPU_INSTR_SYNCHRO | time); +} + +/* Enqueue a WAIT operation */ +static inline void tpu_enq_wait(int16_t time) +{ + tpu_enqueue(TPU_INSTR_WAIT | time); +} + +/* Enqueue an OFFSET operation */ +static inline void tpu_enq_offset(int16_t time) +{ + tpu_enqueue(TPU_INSTR_OFFSET | time); +} + +static inline void tpu_enq_dsp_irq(void) +{ + tpu_enq_move(TPUI_DSP_INT_PG, 0x0001); +} + +/* add two numbers, modulo 5000, and ensure the result is positive */ +uint16_t add_mod5000(int16_t a, int16_t b); + +#endif /* _CALYPSO_TPU_H */ diff --git a/src/target/firmware/include/calypso/tsp.h b/src/target/firmware/include/calypso/tsp.h new file mode 100644 index 00000000..0252f36e --- /dev/null +++ b/src/target/firmware/include/calypso/tsp.h @@ -0,0 +1,30 @@ +#ifndef _CALYPSO_TSP_H +#define _CALYPSO_TSP_H + +#define TSPACT(x) (1 << x) + +/* initiate a TSP write through the TPU */ +void tsp_write(uint8_t dev_idx, uint8_t bitlen, uint32_t dout); + +/* Configure clock edge and chip enable polarity for a device */ +void tsp_setup(uint8_t dev_idx, int clk_rising, int en_positive, int en_edge); + +/* Obtain the current tspact state */ +uint16_t tsp_act_state(void); + +/* Update the TSPACT state, including enable and disable */ +void tsp_act_update(uint16_t new_act); + +/* Enable one or multiple TSPACT signals */ +void tsp_act_enable(uint16_t bitmask); + +/* Disable one or multiple TSPACT signals */ +void tsp_act_disable(uint16_t bitmask); + +/* Toggle one or multiple TSPACT signals */ +void tsp_act_toggle(uint16_t bitmask); + +/* Initialize TSP driver */ +void tsp_init(void); + +#endif /* _CALYPSO_TSP_H */ diff --git a/src/target/firmware/include/calypso/uart.h b/src/target/firmware/include/calypso/uart.h new file mode 100644 index 00000000..7eb925ed --- /dev/null +++ b/src/target/firmware/include/calypso/uart.h @@ -0,0 +1,32 @@ +#ifndef _CAL_UART_H +#define _CAL_UART_H + +#include <stdint.h> + +enum uart_baudrate { + UART_38400, + UART_57600, + UART_115200, + UART_230400, + UART_460800, + UART_614400, + UART_921600, +}; + +void uart_init(uint8_t uart, uint8_t interrupts); +void uart_putchar_wait(uint8_t uart, int c); +int uart_putchar_nb(uint8_t uart, int c); +int uart_getchar_nb(uint8_t uart, uint8_t *ch); +int uart_tx_busy(uint8_t uart); +int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt); + +enum uart_irq { + UART_IRQ_TX_EMPTY, + UART_IRQ_RX_CHAR, +}; + +void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on); + +void uart_poll(uint8_t uart); + +#endif /* _CAL_UART_H */ diff --git a/src/target/firmware/include/comm/msgb.h b/src/target/firmware/include/comm/msgb.h new file mode 100644 index 00000000..3113fa2c --- /dev/null +++ b/src/target/firmware/include/comm/msgb.h @@ -0,0 +1,161 @@ +#ifndef _MSGB_H +#define _MSGB_H + +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocore/linuxlist.h> +#include <console.h> + +struct msgb { + struct llist_head list; + + /* the layer 1 header, if any */ + unsigned char *l1h; + /* the A-bis layer 2 header: OML, RSL(RLL), NS */ + unsigned char *l2h; + /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */ + unsigned char *l3h; + + uint16_t data_len; + uint16_t len; + + unsigned char *head; /* start of buffer */ + unsigned char *tail; /* end of message */ + unsigned char *data; /* start of message */ + unsigned char _data[0]; +}; + +extern struct msgb *msgb_alloc(uint16_t size, const char *name); +extern void msgb_free(struct msgb *m); +extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg); +extern struct msgb *msgb_dequeue(struct llist_head *queue); +extern void msgb_reset(struct msgb *m); + +#define msgb_l1(m) ((void *)(m->l1h)) +#define msgb_l2(m) ((void *)(m->l2h)) +#define msgb_l3(m) ((void *)(m->l3h)) + +static inline unsigned int msgb_l1len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l1(msgb); +} + +static inline unsigned int msgb_l2len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l2(msgb); +} + +static inline unsigned int msgb_l3len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l3(msgb); +} + +static inline unsigned int msgb_headlen(const struct msgb *msgb) +{ + return msgb->len - msgb->data_len; +} +static inline int msgb_tailroom(const struct msgb *msgb) +{ + return (msgb->head + msgb->data_len) - msgb->tail; +} +static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len) +{ + unsigned char *tmp = msgb->tail; + + /* we intentionally call cons_puts() here to display an allocation + * failure on the _other_ serial port (i.e. the one that doesn't + * have the HDLC layer on it */ + if (msgb_tailroom(msgb) < len) + cons_puts("msgb_tailroom insufficient!\n"); + + msgb->tail += len; + msgb->len += len; + return tmp; +} +static inline void msgb_put_u8(struct msgb *msgb, uint8_t word) +{ + uint8_t *space = msgb_put(msgb, 1); + space[0] = word & 0xFF; +} +static inline void msgb_put_u16(struct msgb *msgb, uint16_t word) +{ + uint8_t *space = msgb_put(msgb, 2); + space[0] = word >> 8 & 0xFF; + space[1] = word & 0xFF; +} +static inline void msgb_put_u32(struct msgb *msgb, uint32_t word) +{ + uint8_t *space = msgb_put(msgb, 4); + space[0] = word >> 24 & 0xFF; + space[1] = word >> 16 & 0xFF; + space[2] = word >> 8 & 0xFF; + space[3] = word & 0xFF; +} +static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len) +{ + unsigned char *tmp = msgb->data; + msgb->data += len; + msgb->len -= len; + return tmp; +} +static inline uint8_t msgb_get_u8(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 1); + return space[0]; +} +static inline uint16_t msgb_get_u16(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 2); + return space[0] << 8 | space[1]; +} +static inline uint32_t msgb_get_u32(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 4); + return space[0] << 24 | space[1] << 16 | space[2] << 8 | space[3]; +} +static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len) +{ + msgb->data -= len; + msgb->len += len; + return msgb->data; +} +static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len) +{ + msgb->len -= len; + return msgb->data += len; +} + +/* increase the headroom of an empty msgb, reducing the tailroom */ +static inline void msgb_reserve(struct msgb *msg, int len) +{ + msg->data += len; + msg->tail += len; +} + +static inline struct msgb *msgb_alloc_headroom(int size, int headroom, + const char *name) +{ + struct msgb *msg = msgb_alloc(size, name); + if (msg) + msgb_reserve(msg, headroom); + return msg; +} + +#endif /* _MSGB_H */ diff --git a/src/target/firmware/include/comm/sercomm.h b/src/target/firmware/include/comm/sercomm.h new file mode 100644 index 00000000..8fbbff97 --- /dev/null +++ b/src/target/firmware/include/comm/sercomm.h @@ -0,0 +1,57 @@ +#ifndef _SERCOMM_H +#define _SERCOMM_H + +/* SERCOMM layer on UART1 (modem UART) */ + +#include <osmocore/msgb.h> + +#define SERCOMM_UART_NR 1 + +#define HDLC_FLAG 0x7E +#define HDLC_ESCAPE 0x7D + +#define HDLC_C_UI 0x03 +#define HDLC_C_P_BIT (1 << 4) +#define HDLC_C_F_BIT (1 << 4) + +/* a low sercomm_dlci means high priority. A high DLCI means low priority */ +enum sercomm_dlci { + SC_DLCI_HIGHEST = 0, + SC_DLCI_DEBUG = 4, + SC_DLCI_L1A_L23 = 5, + SC_DLCI_LOADER = 9, + SC_DLCI_CONSOLE = 10, + SC_DLCI_ECHO = 128, + _SC_DLCI_MAX +}; + +void sercomm_init(void); +int sercomm_initialized(void); + +/* User Interface: Tx */ + +/* user interface for transmitting messages for a given DLCI */ +void sercomm_sendmsg(uint8_t dlci, struct msgb *msg); +/* how deep is the Tx queue for a given DLCI */ +unsigned int sercomm_tx_queue_depth(uint8_t dlci); + +/* User Interface: Rx */ + +/* receiving messages for a given DLCI */ +typedef void (*dlci_cb_t)(uint8_t dlci, struct msgb *msg); +int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb); + +/* Driver Interface */ + +/* fetch one octet of to-be-transmitted serial data. returns 0 if no more data */ +int sercomm_drv_pull(uint8_t *ch); +/* the driver has received one byte, pass it into sercomm layer. + returns 1 in case of success, 0 in case of unrecognized char */ +int sercomm_drv_rx_char(uint8_t ch); + +static inline struct msgb *sercomm_alloc_msgb(unsigned int len) +{ + return msgb_alloc_headroom(len+4, 4, "sercomm_tx"); +} + +#endif /* _SERCOMM_H */ diff --git a/src/target/firmware/include/comm/sercomm_cons.h b/src/target/firmware/include/comm/sercomm_cons.h new file mode 100644 index 00000000..11f66545 --- /dev/null +++ b/src/target/firmware/include/comm/sercomm_cons.h @@ -0,0 +1,10 @@ +#ifndef _SERCOMM_CONS_H +#define _SERCOMM_CONS_H + +/* how large buffers do we allocate? */ +#define SERCOMM_CONS_ALLOC 256 + +int sercomm_puts(const char *s); +int sercomm_putchar(int c); + +#endif /* _SERCOMM_CONS_H */ diff --git a/src/target/firmware/include/comm/timer.h b/src/target/firmware/include/comm/timer.h new file mode 100644 index 00000000..814d2c60 --- /dev/null +++ b/src/target/firmware/include/comm/timer.h @@ -0,0 +1,76 @@ +/* + * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef TIMER_H +#define TIMER_H + +#include <sys/time.h> + +#include <osmocore/linuxlist.h> + +/** + * Timer management: + * - Create a struct timer_list + * - Fill out timeout and use add_timer or + * use schedule_timer to schedule a timer in + * x seconds and microseconds from now... + * - Use del_timer to remove the timer + * + * Internally: + * - We hook into select.c to give a timeval of the + * nearest timer. On already passed timers we give + * it a 0 to immediately fire after the select + * - update_timers will call the callbacks and remove + * the timers. + * + */ +struct timer_list { + struct llist_head entry; + unsigned long expires; + + unsigned int active : 1; + unsigned int handled : 1; + unsigned int in_list : 1; + + void (*cb)(void*); + void *data; +}; + +extern unsigned long volatile jiffies; + +/** + * timer management + */ +void add_timer(struct timer_list *timer); +void schedule_timer(struct timer_list *timer, int miliseconds); +void del_timer(struct timer_list *timer); +int timer_pending(struct timer_list *timer); + + +/** + * internal timer list management + */ +void prepare_timers(void); +int update_timers(void); +int timer_check(void); + +void timer_init(void); + +#endif diff --git a/src/target/firmware/include/console.h b/src/target/firmware/include/console.h new file mode 100644 index 00000000..7146e990 --- /dev/null +++ b/src/target/firmware/include/console.h @@ -0,0 +1,20 @@ +#ifndef _CONSOLE_H +#define _CONSOLE_H + +/* This is the direct (IRQ driven) UART console, bypassing the HDLC layer. + * You should not need to call those functions unless you've decided to + * not use the HLDC layer or have a device with two UARTs */ + +int cons_rb_append(const char *data, int len); +int cons_puts(const char *s); +int cons_putchar(char c); +int cons_rb_flush(void); +void cons_init(void); + +/* We want the console on UART 0 (IRDA UART) */ +#define CONS_UART_NR 0 + +/* Size of the static ring-buffer that we keep for console print messages */ +#define CONS_RB_SIZE 4096 + +#endif /* _CONSOLE_H */ diff --git a/src/target/firmware/include/ctors.h b/src/target/firmware/include/ctors.h new file mode 100644 index 00000000..ee4c7b3e --- /dev/null +++ b/src/target/firmware/include/ctors.h @@ -0,0 +1,16 @@ +#ifndef _CTORS_H +#define _CTORS_H + +#if 0 +/* only supported by gcc 3.4 or later */ +#define __ctor_data __attribute__ ((constructor) (100)) +#define __ctor_board __attribute__ ((constructor) (200)) +#else +#define __ctor_data __attribute__ ((constructor)) +#define __ctor_board __attribute__ ((constructor)) +#endif + +/* iterate over list of constructor functions and call each element */ +void do_global_ctors(const char *ctors_start, const char *ctors_end); + +#endif diff --git a/src/target/firmware/include/ctype.h b/src/target/firmware/include/ctype.h new file mode 100644 index 00000000..afa36392 --- /dev/null +++ b/src/target/firmware/include/ctype.h @@ -0,0 +1,54 @@ +#ifndef _LINUX_CTYPE_H +#define _LINUX_CTYPE_H + +/* + * NOTE! This ctype does not handle EOF like the standard C + * library is required to. + */ + +#define _U 0x01 /* upper */ +#define _L 0x02 /* lower */ +#define _D 0x04 /* digit */ +#define _C 0x08 /* cntrl */ +#define _P 0x10 /* punct */ +#define _S 0x20 /* white space (space/lf/tab) */ +#define _X 0x40 /* hex digit */ +#define _SP 0x80 /* hard space (0x20) */ + +extern unsigned char _ctype[]; + +#define __ismask(x) (_ctype[(int)(unsigned char)(x)]) + +#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0) +#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0) +#define iscntrl(c) ((__ismask(c)&(_C)) != 0) +#define isdigit(c) ((__ismask(c)&(_D)) != 0) +#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0) +#define islower(c) ((__ismask(c)&(_L)) != 0) +#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0) +#define ispunct(c) ((__ismask(c)&(_P)) != 0) +#define isspace(c) ((__ismask(c)&(_S)) != 0) +#define isupper(c) ((__ismask(c)&(_U)) != 0) +#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0) + +#define isascii(c) (((unsigned char)(c))<=0x7f) +#define toascii(c) (((unsigned char)(c))&0x7f) + +static inline unsigned char __tolower(unsigned char c) +{ + if (isupper(c)) + c -= 'A'-'a'; + return c; +} + +static inline unsigned char __toupper(unsigned char c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + +#define tolower(c) __tolower(c) +#define toupper(c) __toupper(c) + +#endif diff --git a/src/target/firmware/include/debug.h b/src/target/firmware/include/debug.h new file mode 100644 index 00000000..27c4185d --- /dev/null +++ b/src/target/firmware/include/debug.h @@ -0,0 +1,31 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/* + * Check at compile time that something is of a particular type. + * Always evaluates to 1 so you may use it easily in comparisons. + */ +#define typecheck(type,x) \ +({ type __dummy; \ + typeof(x) __dummy2; \ + (void)(&__dummy == &__dummy2); \ + 1; \ +}) + +#ifdef DEBUG +#define dputchar(x) putchar(x) +#define dputs(x) puts(x) +#define dphex(x,y) phex(x,y) +#define printd(x, args ...) printf(x, ## args) +#else +#define dputchar(x) +#define dputs(x) +#define dphex(x,y) +#define printd(x, args ...) +#endif + +#endif /* _DEBUG_H */ diff --git a/src/target/firmware/include/defines.h b/src/target/firmware/include/defines.h new file mode 100644 index 00000000..3c8732f9 --- /dev/null +++ b/src/target/firmware/include/defines.h @@ -0,0 +1,18 @@ + +#ifndef _DEFINES_H +#define _DEFINES_H + +#define __attribute_const__ __attribute__((__const__)) + +/* type properties */ +#define __packed __attribute__((packed)) +#define __aligned(alignment) __attribute__((aligned(alignment))) +#define __unused __attribute__((unused)) + +/* linkage */ +#define __section(name) __attribute__((section(name))) + +/* force placement in zero-waitstate memory */ +#define __ramtext __section(".ramtext") + +#endif /* !_DEFINES_H */ diff --git a/src/target/firmware/include/delay.h b/src/target/firmware/include/delay.h new file mode 100644 index 00000000..0d6f3efd --- /dev/null +++ b/src/target/firmware/include/delay.h @@ -0,0 +1,7 @@ +#ifndef delay_h +#define delay_h + +void delay_ms(unsigned int ms); +void delay_us(unsigned int us); + +#endif diff --git a/src/target/firmware/include/display.h b/src/target/firmware/include/display.h new file mode 100644 index 00000000..89d02a57 --- /dev/null +++ b/src/target/firmware/include/display.h @@ -0,0 +1,48 @@ +#ifndef _DISPLAY_DRIVER_H +#define _DISPLAY_DRIVER_H + +enum display_attr { + DISP_ATTR_INVERT = 0x0001, +}; + +struct display_driver { + char *name; + void (*init)(void); + void (*set_attr)(unsigned long attr); + void (*unset_attr)(unsigned long attr); + void (*clrscr)(void); + void (*goto_xy)(int xpos, int ypos); + void (*set_color)(int fgcolor, int bgcolor); + int (*putc)(unsigned char c); + int (*puts)(const char *str); +}; + +extern struct display_driver *display; + +static inline void display_init(void) +{ + display->init(); +} +static inline void display_set_attr(unsigned long attr) +{ + display->set_attr(attr); +} +static inline void display_unset_attr(unsigned long attr) +{ + display->unset_attr(attr); +} +static inline void display_clrscr(void) +{ + display->clrscr(); +} +static inline int display_putchar(unsigned char c) +{ + return display->putc(c); +} +int display_puts(const char *s); + +extern const struct display_driver st7558_display; +extern const struct display_driver ssd1783_display; +extern const struct display_driver td014_display; + +#endif diff --git a/src/target/firmware/include/display/ssd1783.h b/src/target/firmware/include/display/ssd1783.h new file mode 100644 index 00000000..c72eebac --- /dev/null +++ b/src/target/firmware/include/display/ssd1783.h @@ -0,0 +1,56 @@ +#ifndef _SSD1783_H +#define _SSD1783_H + +/* Some basic colors */ +#define RED 0x0f00 +#define GREEN 0x00f0 +#define BLUE 0x000f +#define YELLOW 0x0ff0 +#define MAGENTA 0x0f0f +#define CYAN 0x00ff +#define BLACK 0x0000 +#define WHITE 0x0fff + +/* Epson S1D15G10D08B000 commandset */ +#define CMD_DISON 0xaf // Display on +#define CMD_DISOFF 0xae // Display off +#define CMD_DISNOR 0xa6 // Normal display +#define CMD_DISINV 0xa7 // Inverse display +#define CMD_COMSCN 0xbb // Common scan direction +#define CMD_DISCTL 0xca // Display control +#define CMD_SLPIN 0x95 // Sleep in +#define CMD_SLPOUT 0x94 // Sleep out +#define CMD_PASET 0x75 // Page address set +#define CMD_CASET 0x15 // Column address set +#define CMD_DATCTL 0xbc // Data scan direction, etc. +#define CMD_RGBSET8 0xce // 256-color position set +#define CMD_RAMWR 0x5c // Writing to memory +#define CMD_RAMRD 0x5d // Reading from memory +#define CMD_PTLIN 0xa8 // Partial display in +#define CMD_PTLOUT 0xa9 // Partial display out +#define CMD_RMWIN 0xe0 // Read and modify write +#define CMD_RMWOUT 0xee // End +#define CMD_ASCSE 0xaa // Area scroll set +#define CMD_SCSTART 0xab // Scroll start set +#define CMD_OSCON 0xd1 // Internal oscillation on +#define CMD_OSCOFF 0xd2 // Internal oscillation off +#define CMD_PWRCTR 0x20 // Power control +#define CMD_VOLCTR 0x81 // Electronic volume control +#define CMD_VOLUP 0xd6 // Increment electronic control by 1 +#define CMD_VOLDOWN 0xd7 // Decrement electronic control by 1 +#define CMD_TMPGRD 0x82 // Temperature gradient set +#define CMD_EPCTIN 0xcd // Control EEPROM +#define CMD_EPCOUT 0xcc // Cancel EEPROM control +#define CMD_EPMWR 0xfc // Write into EEPROM +#define CMD_EPMRD 0xfd // Read from EEPROM +#define CMD_EPSRRD1 0x7c // Read register 1 +#define CMD_EPSRRD2 0x7d // Read register 2 +#define CMD_NOP 0x25 // NOP instruction + +/* Extended SSD1783 commandset, partly (also has HW graphic functionalities) */ +#define CMD_BIASSET 0xfb // Set bias ratio +#define CMD_FREQSET 0xf2 // Set frequency and n-line inversion +#define CMD_RESCMD 0xa2 // reserved command +#define CMD_PWMSEL 0xf7 // Select PWM/FRC, Full/8 color mode + +#endif diff --git a/src/target/firmware/include/flash/cfi_flash.h b/src/target/firmware/include/flash/cfi_flash.h new file mode 100644 index 00000000..9d8b33ae --- /dev/null +++ b/src/target/firmware/include/flash/cfi_flash.h @@ -0,0 +1,41 @@ + +#ifndef _CFI_FLASH_H +#define _CFI_FLASH_H + +#include <stdint.h> + +#define FLASH_MAX_REGIONS 4 + +typedef struct { + void *fr_base; + size_t fr_bnum; + size_t fr_bsize; +} flash_region_t; + +typedef struct { + void *f_base; + size_t f_size; + + size_t f_nregions; + flash_region_t f_regions[FLASH_MAX_REGIONS]; +} flash_t; + +typedef enum { + FLASH_UNLOCKED = 0, + FLASH_LOCKED, + FLASH_LOCKED_DOWN +} flash_lock_t; + +int flash_init(flash_t *flash, void *base_addr); + +flash_lock_t flash_block_getlock(flash_t *flash, uint32_t block_offset); + +int flash_block_unlock(flash_t *flash, uint32_t block_offset); +int flash_block_lock(flash_t *flash, uint32_t block_offset); +int flash_block_lockdown(flash_t *flash, uint32_t block_offset); + +int flash_block_erase(flash_t *flash, uint32_t block_offset); + +int flash_program(flash_t *flash, uint32_t dst_offset, void *src, uint32_t nbytes); + +#endif diff --git a/src/target/firmware/include/i2c.h b/src/target/firmware/include/i2c.h new file mode 100644 index 00000000..37097a85 --- /dev/null +++ b/src/target/firmware/include/i2c.h @@ -0,0 +1,7 @@ +#ifndef _I2C_H +#define _I2C_H + +int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len); +void i2c_init(int speed, int slaveadd); + +#endif /* I2C_H */ diff --git a/src/target/firmware/include/keypad.h b/src/target/firmware/include/keypad.h new file mode 100644 index 00000000..e2e6519f --- /dev/null +++ b/src/target/firmware/include/keypad.h @@ -0,0 +1,66 @@ +#ifndef _KEYPAD_H +#define _KEYPAD_H + +enum buttons { + BTN_0 = 0x00002000, + BTN_1 = 0x00008000, + BTN_2 = 0x00000400, + BTN_3 = 0x00000020, + BTN_4 = 0x00010000, + BTN_5 = 0x00000800, + BTN_6 = 0x00000040, + BTN_7 = 0x00020000, + BTN_8 = 0x00001000, + BTN_9 = 0x00000080, + BTN_STAR = 0x00040000, + BTN_HASH = 0x00000100, + BTN_MENU = 0x00004000, + BTN_LEFT_SB = 0x00080000, + BTN_RIGHT_SB = 0x00000200, + BTN_UP = 0x00000002, + BTN_DOWN = 0x00000004, + BTN_LEFT = 0x00000008, + BTN_RIGHT = 0x00000010, + BTN_OK = 0x00000001, + BTN_POWER = 0x01000000, +}; + +enum key_codes { + KEY_0 = 0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_STAR, //* + KEY_HASH, //# + KEY_MENU, //center of directional keys + KEY_LEFT_SB, //softbutton + KEY_RIGHT_SB, //softbutton + KEY_UP, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_OK, //green off-hook + KEY_POWER, //red on-hook + KEY_INV = 0xFF +}; + +enum key_states { + PRESSED, + RELEASED, +}; + +void keypad_init(uint8_t interrupts); + +void keypad_poll(); + +typedef void (*key_handler_t)(enum key_codes code, enum key_states state); + +void keypad_set_handler(key_handler_t handler); + +#endif /* KEYPAD_H */ diff --git a/src/target/firmware/include/layer1/afc.h b/src/target/firmware/include/layer1/afc.h new file mode 100644 index 00000000..8b43f8aa --- /dev/null +++ b/src/target/firmware/include/layer1/afc.h @@ -0,0 +1,18 @@ +#ifndef _L1_AFC_H +#define _L1_AFC_H + +#define AFC_SNR_THRESHOLD 2560 /* 2.5 dB in fx6.10 */ + +/* Input a frequency error sample into the AFC averaging */ +void afc_input(int32_t freq_error, uint16_t arfcn, int valid); + +/* Update the AFC with a frequency error, bypassing averaging */ +void afc_correct(int16_t freq_error, uint16_t arfcn); + +/* Update DSP with new AFC DAC value to be used for next TDMA frame */ +void afc_load_dsp(void); + +/* Reset the AFC to its initial DAC value */ +void afc_reset(void); + +#endif diff --git a/src/target/firmware/include/layer1/agc.h b/src/target/firmware/include/layer1/agc.h new file mode 100644 index 00000000..2b7e46e9 --- /dev/null +++ b/src/target/firmware/include/layer1/agc.h @@ -0,0 +1,7 @@ +#ifndef _L1_AGC_H +#define _L1_AGC_H + +#define to_dbm8(x) ((x)*8) +int16_t agc_inp_dbm8_by_pm(int16_t pm); + +#endif /* _L1_AGC_H */ diff --git a/src/target/firmware/include/layer1/apc.h b/src/target/firmware/include/layer1/apc.h new file mode 100644 index 00000000..3d73c23e --- /dev/null +++ b/src/target/firmware/include/layer1/apc.h @@ -0,0 +1,10 @@ +#ifndef _L1_APC_H +#define _L1_APC_H + +/* determine the AUXAPC value by the Tx Power Level */ +int16_t apc_tx_dbm2auxapc(enum gsm_band band, int8_t dbm); + +/* determine the AUXAPC value by the Tx Power Level */ +int16_t apc_tx_pwrlvl2auxapc(enum gsm_band band, uint8_t lvl); + +#endif diff --git a/src/target/firmware/include/layer1/async.h b/src/target/firmware/include/layer1/async.h new file mode 100644 index 00000000..f8d6b716 --- /dev/null +++ b/src/target/firmware/include/layer1/async.h @@ -0,0 +1,53 @@ +#ifndef _L1_ASYNC_H +#define _L1_ASYNC_H + +#include <osmocore/msgb.h> + +#include <layer1/mframe_sched.h> + +#if 0 +NOTE: Re-enabling interrupts causes an IRQ while processing the same IRQ. + Use local_firq_save and local_irq_restore instead! + +/* When altering data structures used by L1 Sync part, we need to + * make sure to temporarily disable IRQ/FIQ to keep data consistent */ +static inline void l1a_lock_sync(void) +{ + arm_disable_interrupts(); +} + +static inline void l1a_unlock_sync(void) +{ + arm_enable_interrupts(); +} +#endif + +/* safely enable a message into the L1S TX queue */ +void l1a_txq_msgb_enq(struct llist_head *queue, struct msgb *msg); +void l1a_meas_msgb_set(struct msgb *msg); + +/* flush all pending msgb */ +void l1a_txq_msgb_flush(struct llist_head *queue); + +/* request a RACH */ +void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra); + +/* schedule frequency change */ +void l1a_freq_req(uint32_t fn_sched); + +/* Enable a repeating multiframe task */ +void l1a_mftask_enable(enum mframe_task task); + +/* Disable a repeating multiframe task */ +void l1a_mftask_disable(enum mframe_task task); + +/* Set TCH mode */ +uint8_t l1a_tch_mode_set(uint8_t mode); + +/* Execute pending L1A completions */ +void l1a_compl_execute(void); + +/* Initialize asynchronous part of Layer1 */ +void l1a_init(void); + +#endif diff --git a/src/target/firmware/include/layer1/avg.h b/src/target/firmware/include/layer1/avg.h new file mode 100644 index 00000000..6c5de172 --- /dev/null +++ b/src/target/firmware/include/layer1/avg.h @@ -0,0 +1,23 @@ +#ifndef _L1_AVG_H +#define _L1_AVG_H + +struct running_avg { + /* configuration */ + uint16_t period; /* over how many samples to average */ + uint16_t min_valid; + + int32_t acc_val; + uint16_t num_samples; /* how often did we try to sample? */ + uint16_t num_samples_valid; /* how often did we receive valid samples? */ + + void (*outfn)(struct running_avg *, int32_t avg); + void *priv; +}; + +/* input a new sample into the averaging process */ +void runavg_input(struct running_avg *ravg, int32_t val, int valid); + +/* check if sufficient samples have been obtained, and call outfn() */ +int runavg_check_output(struct running_avg *ravg); + +#endif /* _AVG_H */ diff --git a/src/target/firmware/include/layer1/l23_api.h b/src/target/firmware/include/layer1/l23_api.h new file mode 100644 index 00000000..b1602705 --- /dev/null +++ b/src/target/firmware/include/layer1/l23_api.h @@ -0,0 +1,15 @@ +#ifndef _L1_L23_API_H +#define _L1_L23_API_H + +#include <stdint.h> +#include <osmocore/msgb.h> +#include <l1ctl_proto.h> + +void l1a_l23api_init(void); +void l1_queue_for_l2(struct msgb *msg); +struct msgb *l1ctl_msgb_alloc(uint8_t msg_type); +struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr, uint16_t arfcn); + +void l1ctl_tx_reset(uint8_t msg_type, uint8_t reset_type); + +#endif /* _L1_L23_API_H */ diff --git a/src/target/firmware/include/layer1/mframe_sched.h b/src/target/firmware/include/layer1/mframe_sched.h new file mode 100644 index 00000000..fa944221 --- /dev/null +++ b/src/target/firmware/include/layer1/mframe_sched.h @@ -0,0 +1,63 @@ +#ifndef _L1_MFRAME_SCHED_H +#define _L1_MFRAME_SCHED_H + +#include <stdint.h> + +enum mframe_task { + MF_TASK_BCCH_NORM, + MF_TASK_BCCH_EXT, + MF_TASK_CCCH, + MF_TASK_CCCH_COMB, + + MF_TASK_SDCCH4_0, + MF_TASK_SDCCH4_1, + MF_TASK_SDCCH4_2, + MF_TASK_SDCCH4_3, + + MF_TASK_SDCCH8_0, + MF_TASK_SDCCH8_1, + MF_TASK_SDCCH8_2, + MF_TASK_SDCCH8_3, + MF_TASK_SDCCH8_4, + MF_TASK_SDCCH8_5, + MF_TASK_SDCCH8_6, + MF_TASK_SDCCH8_7, + + MF_TASK_TCH_F_EVEN, + MF_TASK_TCH_F_ODD, + MF_TASK_TCH_H_0, + MF_TASK_TCH_H_1, + + /* Test task: send Normal Burst in all timeslots */ + MF_TASK_UL_ALL_NB, +}; + +enum mf_sched_item_flag { + MF_F_SACCH = (1 << 0), +}; + +/* The scheduler itself */ +struct mframe_scheduler { + uint32_t tasks; + uint32_t tasks_tgt; + uint32_t safe_fn; +}; + +uint8_t mframe_task2chan_nr(enum mframe_task mft, uint8_t ts); + +/* Enable a specific task */ +void mframe_enable(enum mframe_task task_id); + +/* Disable a specific task */ +void mframe_disable(enum mframe_task task_id); + +/* Replace the current active set by the new one */ +void mframe_set(uint32_t tasks); + +/* Schedule mframe_sched_items according to current MF TASK list */ +void mframe_schedule(void); + +/* reset the scheduler, disabling all tasks */ +void mframe_reset(void); + +#endif /* _MFRAME_SCHED_H */ diff --git a/src/target/firmware/include/layer1/prim.h b/src/target/firmware/include/layer1/prim.h new file mode 100644 index 00000000..44a01cbc --- /dev/null +++ b/src/target/firmware/include/layer1/prim.h @@ -0,0 +1,33 @@ +#ifndef _L1_PRIM_H +#define _L1_PRIM_H + +#include <stdint.h> + +#include <layer1/tdma_sched.h> + +struct l1ctl_fbsb_req; + +/* Utils */ +const uint8_t *pu_get_idle_frame(void); +void pu_update_rx_level(uint8_t rx_level); +const uint8_t *pu_get_meas_frame(void); + +/* Primitives tests/requests */ +void l1s_fb_test(uint8_t base_fn, uint8_t fb_mode); +void l1s_sb_test(uint8_t base_fn); +void l1s_pm_test(uint8_t base_fn, uint16_t arfcn); +void l1s_nb_test(uint8_t base_fn); + +void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req); +void l1a_freq_req(uint32_t fn_sched); +void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra); + +/* Primitives raw scheduling sets */ +extern const struct tdma_sched_item nb_sched_set[]; +extern const struct tdma_sched_item nb_sched_set_ul[]; + +extern const struct tdma_sched_item tch_sched_set[]; +extern const struct tdma_sched_item tch_a_sched_set[]; +extern const struct tdma_sched_item tch_d_sched_set[]; + +#endif /* _L1_PRIM_H */ diff --git a/src/target/firmware/include/layer1/rfch.h b/src/target/firmware/include/layer1/rfch.h new file mode 100644 index 00000000..344523c3 --- /dev/null +++ b/src/target/firmware/include/layer1/rfch.h @@ -0,0 +1,9 @@ +#ifndef _L1_RFCH_H +#define _L1_RFCH_H + +struct gsm_time; + +void rfch_get_params(struct gsm_time *t, + uint16_t *arfcn_p, uint8_t *tsc_p, uint8_t *tn_p); + +#endif /* _L1_RFCH_H */ diff --git a/src/target/firmware/include/layer1/sched_gsmtime.h b/src/target/firmware/include/layer1/sched_gsmtime.h new file mode 100644 index 00000000..630c6163 --- /dev/null +++ b/src/target/firmware/include/layer1/sched_gsmtime.h @@ -0,0 +1,24 @@ +#ifndef _L1_SCHED_GSMTIME_H +#define _L1_SCHED_GSMTIME_H + +#include <stdint.h> +#include <osmocore/linuxlist.h> + +struct sched_gsmtime_event { + struct llist_head list; + const struct tdma_sched_item *si; + uint32_t fn; + uint16_t p3; /* parameter for TDMA scheduler */ +}; + +/* initialize the GSMTIME scheduler */ +void sched_gsmtime_init(void); + +/* Scheduling of a single event at a givnen GSM time */ +int sched_gsmtime(const struct tdma_sched_item *si, uint32_t fn, uint16_t p3); + +/* execute all GSMTIME one-shot events pending for 'current_fn' */ +int sched_gsmtime_execute(uint32_t current_fn); + +void sched_gsmtime_reset(void); +#endif diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h new file mode 100644 index 00000000..06ce6a00 --- /dev/null +++ b/src/target/firmware/include/layer1/sync.h @@ -0,0 +1,191 @@ +#ifndef _L1_SYNC_H +#define _L1_SYNC_H + +#include <osmocore/linuxlist.h> +#include <osmocore/gsm_utils.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> +#include <l1ctl_proto.h> + +/* structure representing L1 sync information about a cell */ +struct l1_cell_info { + /* on which ARFCN (+band) is the cell? */ + uint16_t arfcn; + /* what's the BSIC of the cell (from SCH burst decoding) */ + uint8_t bsic; + /* Combined or non-combined CCCH */ + uint8_t ccch_mode; /* enum ccch_mode */ + /* whats the delta of the cells current GSM frame number + * compared to our current local frame number */ + int32_t fn_offset; + /* how much does the TPU need adjustment (delta) to synchronize + * with the cells burst */ + uint32_t time_alignment; + /* FIXME: should we also store the AFC value? */ +}; + +enum l1s_chan { + L1S_CHAN_MAIN, + L1S_CHAN_SACCH, + _NUM_L1S_CHAN +}; + +enum l1_compl { + L1_COMPL_FB, + L1_COMPL_RACH, + L1_COMPL_TX_NB, + L1_COMPL_TX_TCH, +}; + +typedef void l1_compl_cb(enum l1_compl c); + +#define L1S_NUM_COMPL 32 +#define L1S_NUM_NEIGH_CELL 6 + +struct l1s_h0 { + uint16_t arfcn; +}; + +struct l1s_h1 { + uint8_t hsn; + uint8_t maio; + uint8_t n; + uint16_t ma[64]; +}; + +struct l1s_state { + struct gsm_time current_time; /* current GSM time */ + struct gsm_time next_time; /* GSM time at next TMDMA irq */ + + /* the cell on which we are camping right now */ + struct l1_cell_info serving_cell; + + /* neighbor cell sync info */ + struct l1_cell_info neigh_cell[L1S_NUM_NEIGH_CELL]; + + /* TDMA scheduler */ + struct tdma_scheduler tdma_sched; + + /* Multiframe scheduler */ + struct mframe_scheduler mframe_sched; + + /* The current TPU offset register */ + uint32_t tpu_offset; + + /* TX parameters */ + int8_t ta; + uint8_t tx_power; + + /* TCH */ + uint8_t tch_mode; + uint8_t tch_sync; + + /* Transmit queues of pending packets for main DCCH and ACCH */ + struct llist_head tx_queue[_NUM_L1S_CHAN]; + struct msgb *tx_meas; + + /* Which L1A completions are scheduled right now */ + uint32_t scheduled_compl; + /* callbacks for each of the completions */ + l1_compl_cb *completion[L1S_NUM_COMPL]; + + /* Structures below are for L1-task specific parameters, used + * to communicate between l1-sync and l1-async (l23_api) */ + struct { + uint8_t mode; /* FB_MODE 0/1 */ + } fb; + + struct { + /* power measurement l1 task */ + unsigned int mode; + union { + struct { + uint16_t arfcn_start; + uint16_t arfcn_next; + uint16_t arfcn_end; + } range; + }; + struct msgb *msg; + } pm; + + struct { + uint8_t ra; + } rach; + + struct { + enum { + GSM_DCHAN_NONE = 0, + GSM_DCHAN_SDCCH_4, + GSM_DCHAN_SDCCH_8, + GSM_DCHAN_TCH_H, + GSM_DCHAN_TCH_F, + GSM_DCHAN_UNKNOWN, + } type; + + uint8_t scn; + uint8_t tsc; + uint8_t tn; + uint8_t h; + + union { + struct l1s_h0 h0; + struct l1s_h1 h1; + }; + + uint8_t st_tsc; + uint8_t st_tn; + uint8_t st_h; + + union { + struct l1s_h0 st_h0; + struct l1s_h1 st_h1; + }; + } dedicated; +}; + +extern struct l1s_state l1s; + +struct l1s_meas_hdr { + uint16_t snr; /* signal/noise ratio */ + int16_t toa_qbit; /* time of arrival (qbits) */ + int16_t pm_dbm8; /* power level in dbm/8 */ + int16_t freq_err; /* Frequency error in Hz */ +}; + +int16_t l1s_snr_int(uint16_t snr); +uint16_t l1s_snr_fract(uint16_t snr); + +void l1s_dsp_abort(void); + +void l1s_tx_apc_helper(uint16_t arfcn); + +/* schedule a completion */ +void l1s_compl_sched(enum l1_compl c); + +void l1s_init(void); + +/* reset the layer1 as part of synchronizing to a new cell */ +void l1s_reset(void); + +/* init.c */ +void layer1_init(void); + +/* A debug macro to print every TDMA frame */ +#ifdef DEBUG_EVERY_TDMA +#define putchart(x) putchar(x) +#else +#define putchart(x) +#endif + +/* Convert an angle in fx1.15 notatinon into Hz */ +#define BITFREQ_DIV_2PI 43104 /* 270kHz / 2 * pi */ +#define BITFREQ_DIV_PI 86208 /* 270kHz / pi */ +#define ANG2FREQ_SCALING (2<<15) /* 2^15 scaling factor for fx1.15 */ +#define ANGLE_TO_FREQ(angle) ((int16_t)angle * BITFREQ_DIV_PI / ANG2FREQ_SCALING) + +void l1s_reset_hw(void); +void synchronize_tdma(struct l1_cell_info *cinfo); +void l1s_time_inc(struct gsm_time *time, uint32_t delta_fn); +void l1s_time_dump(const struct gsm_time *time); + +#endif /* _L1_SYNC_H */ diff --git a/src/target/firmware/include/layer1/tdma_sched.h b/src/target/firmware/include/layer1/tdma_sched.h new file mode 100644 index 00000000..f58d59bb --- /dev/null +++ b/src/target/firmware/include/layer1/tdma_sched.h @@ -0,0 +1,73 @@ +#ifndef _L1_TDMA_SCHED_H +#define _L1_TDMA_SCHED_H + +#include <stdint.h> + +/* TDMA scheduler */ + +/* The idea of this scheduler is that we have a circular buffer of buckets, + * where each bucket corresponds to one future TDMA frame [interrupt]. Each + * bucket contains of a list of callbacks which are executed when the bucket + * index reaches that particular bucket. */ + +#define TDMASCHED_NUM_FRAMES 25 +#define TDMASCHED_NUM_CB 8 + +#define TDMA_IFLG_TPU (1<<0) +#define TDMA_IFLG_DSP (1<<1) + +typedef int tdma_sched_cb(uint8_t p1, uint8_t p2, uint16_t p3); + +/* A single item in a TDMA scheduler bucket */ +struct tdma_sched_item { + tdma_sched_cb *cb; + uint8_t p1; + uint8_t p2; + uint16_t p3; + int16_t prio; + uint16_t flags; /* TDMA_IFLG_xxx */ +}; + +/* A bucket inside the TDMA scheduler */ +struct tdma_sched_bucket { + struct tdma_sched_item item[TDMASCHED_NUM_CB]; + uint8_t num_items; +}; + +/* The scheduler itself, consisting of buckets and a current index */ +struct tdma_scheduler { + struct tdma_sched_bucket bucket[TDMASCHED_NUM_FRAMES]; + uint8_t cur_bucket; +}; + +/* Schedule an item at 'frame_offset' TDMA frames in the future */ +int tdma_schedule(uint8_t frame_offset, tdma_sched_cb *cb, + uint8_t p1, uint8_t p2, uint16_t p3, int16_t prio); + +/* Schedule a set of items starting from 'frame_offset' TDMA frames in the future */ +int tdma_schedule_set(uint8_t frame_offset, const struct tdma_sched_item *item_set, uint16_t p3); + +/* Scan current frame scheduled items for flags */ +uint16_t tdma_sched_flag_scan(void); + +/* Execute pre-scheduled events for current frame */ +int tdma_sched_execute(void); + +/* Advance TDMA scheduler to the next bucket */ +void tdma_sched_advance(void); + +/* reset the scheduler; erase all scheduled items */ +void tdma_sched_reset(void); + +/* debug function: print number of entries of all TDMA buckets */ +void tdma_sched_dump(void); + + +extern int tdma_end_set(uint8_t p1, uint8_t p2, uint16_t p3); +#define SCHED_ITEM(x, p, y, z) { .cb = x, .p1 = y, .p2 = z, .prio = p, .flags = 0 } +#define SCHED_ITEM_DT(x, p, y, z) { .cb = x, .p1 = y, .p2 = z, .prio = p, \ + .flags = TDMA_IFLG_TPU | TDMA_IFLG_DSP } +#define SCHED_END_FRAME() { .cb = NULL, .p1 = 0, .p2 = 0 } +#define SCHED_END_SET() { .cb = &tdma_end_set, .p1 = 0, .p2 = 0 } + +#endif /* _L1_TDMA_SCHED_H */ diff --git a/src/target/firmware/include/layer1/tpu_window.h b/src/target/firmware/include/layer1/tpu_window.h new file mode 100644 index 00000000..7b146f12 --- /dev/null +++ b/src/target/firmware/include/layer1/tpu_window.h @@ -0,0 +1,24 @@ +#ifndef _L1_TPU_CTRL_H +#define _L1_TPU_CTRL_H + +enum l1_rxwin_type { + L1_RXWIN_PW, /* power measurement */ + L1_RXWIN_FB, /* FCCH burst detection */ + L1_RXWIN_SB, /* SCH burst detection */ + L1_RXWIN_NB, /* Normal burst decoding */ + _NUM_L1_RXWIN +}; + +enum l1_txwin_type { + L1_TXWIN_NB, /* Normal burst sending */ + L1_TXWIN_AB, /* RACH burst sending */ + _NUM_L1_TXWIN +}; + +void l1s_win_init(void); +void l1s_rx_win_ctrl(uint16_t arfcn, enum l1_rxwin_type wtype, uint8_t tn_ofs); +void l1s_tx_win_ctrl(uint16_t arfcn, enum l1_txwin_type wtype, uint8_t pwr, uint8_t tn_ofs); + +void tpu_end_scenario(void); + +#endif /* _L1_TPU_CTRL_H */ diff --git a/src/target/firmware/include/manifest.h b/src/target/firmware/include/manifest.h new file mode 100644 index 00000000..6c1b2026 --- /dev/null +++ b/src/target/firmware/include/manifest.h @@ -0,0 +1,10 @@ + +#ifndef _MANIFEST_H +#define _MANIFEST_H + +extern const char *manifest_application; +extern const char *manifest_revision; +extern const char *manifest_board; +extern const char *manifest_environment; + +#endif /* !_MANIFEST_H */ diff --git a/src/target/firmware/include/memory.h b/src/target/firmware/include/memory.h new file mode 100644 index 00000000..b0a0490c --- /dev/null +++ b/src/target/firmware/include/memory.h @@ -0,0 +1,28 @@ +#ifndef _MEMORY_H +#define _MEMORY_H + +#define __arch_getb(a) (*(volatile unsigned char *)(a)) +#define __arch_getw(a) (*(volatile unsigned short *)(a)) +#define __arch_getl(a) (*(volatile unsigned int *)(a)) + +#define __arch_putb(v,a) (*(volatile unsigned char *)(a) = (v)) +#define __arch_putw(v,a) (*(volatile unsigned short *)(a) = (v)) +#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v)) + +#define __raw_writeb(v,a) __arch_putb(v,a) +#define __raw_writew(v,a) __arch_putw(v,a) +#define __raw_writel(v,a) __arch_putl(v,a) + +#define __raw_readb(a) __arch_getb(a) +#define __raw_readw(a) __arch_getw(a) +#define __raw_readl(a) __arch_getl(a) + +#define writeb(v,a) __arch_putb(v,a) +#define writew(v,a) __arch_putw(v,a) +#define writel(v,a) __arch_putl(v,a) + +#define readb(a) __arch_getb(a) +#define readw(a) __arch_getw(a) +#define readl(a) __arch_getl(a) + +#endif /* _MEMORY_H */ diff --git a/src/target/firmware/include/mtk/bfe.h b/src/target/firmware/include/mtk/bfe.h new file mode 100644 index 00000000..b07f620d --- /dev/null +++ b/src/target/firmware/include/mtk/bfe.h @@ -0,0 +1,107 @@ +#ifndef _MTK_BFE_H +#define _MTK_BFE_H + +/* MTK Baseband Frontend */ + +/* MT6235 Chapter 10 */ + +enum mtk_bfe_reg { + BFE_CON = 0x0000, + BFE_STA = 0x0004, + /* Rx Configuration Register */ + RX_CFG = 0x0010, + /* Rx Control Register */ + RX_CON = 0x0014, + /* RX Interference Detection Power Measurement Control Register */ + RX_PM_CON = 0x0018, + /* RX FIR Coefficient Set ID Control Register */ + RX_FIR_CSID_CON = 0x001c, + /* RX Ram0 Coefficient Set 0 Register */ + RX_RAM0_CS0 = 0x0070, + /* RX Ram1 Coefficient Set 0 Register */ + RX_RAM1_CS0 = 0x0020, + /* Rx Interference Detection HPF Power Register */ + RX_HPWR_STS = 0x00b0, + /* Rx Interference Detection BPF Power Register */ + RX_BPWR_STS = 0x00b4, + + TX_CFG = 0x0060, + TX_CON = 0x0064, + TX_OFF = 0x0068, +}; + +#define RX_RAM0_CS(n) (RX_RAM0_CS0 + (n)*4) +#define RX_RAM1_CS(n) (RX_RAM0_CS1 + (n)*4) + +/* SWAP I/Q before input to baesband frontend */ +#define RX_CFG_SWAP_IQ 0x0001 +/* Bypass RX FIR filter control */ +#define RX_CFG_BYPFLTR 0x0002 +/* Number of RX FIR filter taps */ +#define RX_CFG_FIRTPNO(n) (((n) & 0x3f) << 4) + +#define RX_CON_BLPEN_NORMAL (0 << 0) +#define RX_CON_BLPEN_LOOPB (1 << 0) +#define RX_CON_BLPEN_LOOPB_FILT (2 << 0) + +/* Phase de-rotation in wide FIR data path */ +#define RX_CON_PH_ROEN_W (1 << 2) +/* Phase de-rotation in narrow FIR data path */ +#define RX_CON_PH_ROEN_N (1 << 3) +/* RX I-data gain compenstation select (+/- 1.5dB */ +#define RX_CON_IGAINSEL_00dB (0 << 4) +#define RX_CON_IGAINSEL_03dB (1 << 4) +#define RX_CON_IGAINSEL_06dB (2 << 4) +#define RX_CON_IGAINSEL_09dB (3 << 4) +#define RX_CON_IGAINSEL_12dB (4 << 4) +#define RX_CON_IGAINSEL_15dB (5 << 4) +#define RX_CON_IGAINSEL_n03dB (9 << 4) +#define RX_CON_IGAINSEL_n06dB (10 << 4) +#define RX_CON_IGAINSEL_n09dB (11 << 4) +#define RX_CON_IGAINSEL_n12dB (12 << 4) +#define RX_CON_IGAINSEL_n15dB (13 << 4) + +/* TX_CFG */ +/* Appending Bits enable */ +#define TX_CFG_APNDEN (1 << 0) +/* Ramp Profile Select for 8PSK */ +#define TX_CFG_RPSEL_I (0 << 1) /* 50 kHz sine tone */ +#define TX_CFG_RPSEL_II (1 << 1) /* null DC I/Q */ +#define TX_CFG_RPSEL_III (3 << 1) +#define TX_CFG_INTEN (1 << 3) /* Interpolate between bursts */ +#define TX_CFG_MDBYP (1 << 4) /* Modulator Bypass */ +#define TX_CFG_SGEN (1 << 5) /* 540 kHz sine tone */ +#define TX_CFG_ALL_10GEN_ZERO (1 << 6) +#define TX_CFG_ALL_10GEN_ONE (2 << 6) +#define TX_CFG_SW_QBCNT(n) (((n) & 0x1f) << 8) +#define TX_CFG_GMSK_DTAP_SYM_1 (0 << 13) +#define TX_CFG_GMSK_DTAP_SYM_0 (1 << 13) +#define TX_CFG_GMSK_DTAP_SYM_2 (2 << 13) + +#define TX_CON_IQSWP (1 << 0) /* Swap I/Q */ +/* GMSK or 8PSK modulation for 1st through 4th burst */ +#define TX_CON_MDSEL1_8PSK (1 << 2) +#define TX_CON_MDSEL2_8PSK (1 << 3) +#define TX_CON_MDSEL3_8PSK (1 << 4) +#define TX_CON_MDSEL4_8PSK (1 << 5) +/* Quadratur phase compensation select */ +#define TX_CON_PHSEL_0deg (0 << 8) +#define TX_CON_PHSEL_1deg (1 << 8) +#define TX_CON_PHSEL_2deg (2 << 8) +#define TX_CON_PHSEL_3deg (3 << 8) +#define TX_CON_PHSEL_4deg (4 << 8) +#define TX_CON_PHSEL_5deg (5 << 8) +#define TX_CON_PHSEL_n5deg (10 << 8) +#define TX_CON_PHSEL_n4deg (11 << 8) +#define TX_CON_PHSEL_n3deg (12 << 8) +#define TX_CON_PHSEL_n2deg (13 << 8) +#define TX_CON_PHSEL_n1deg (14 << 8) +/* GMSK modulator output latenct */ +#define TX_CON_GMSK_DTAP_QB(n) (((n) & 3) << 12) + +#define TX_OFF_I(n) (((n) & 0x3f) << 0) +#define TX_OFF_Q(n) (((n) & 0x3f) << 8) +/* Double Buffering */ +#define TX_OFF_TYP_DB 0x8000 + +#endif /* _MTK_BFE_H */ diff --git a/src/target/firmware/include/mtk/bpi.h b/src/target/firmware/include/mtk/bpi.h new file mode 100644 index 00000000..8aa8ee54 --- /dev/null +++ b/src/target/firmware/include/mtk/bpi.h @@ -0,0 +1,20 @@ +#ifndef _MTK_BPI_H +#define _MTK_BPI_H + +/* MTK Baseband Parallel Interface */ + +/* Chapter 9.2 of MT6235 Data Sheet */ + +#define BPI_BUF(n) (BPI_BUF0 + ((n) * 4)) + +#define MTK_BPI(n) (n) + +enum mtk_bpi_reg { + BPI_CON = 0x0000, + BPI_BUF0 = 0x0004, + BPI_ENA0 = 0x00b0, + BPI_ENA1 = 0x00b4, + BPI_ENA2 = 0x00b8, +}; + +#endif /* _MTK_BPI_H */ diff --git a/src/target/firmware/include/mtk/bsi.h b/src/target/firmware/include/mtk/bsi.h new file mode 100644 index 00000000..6f381ce3 --- /dev/null +++ b/src/target/firmware/include/mtk/bsi.h @@ -0,0 +1,41 @@ +#ifndef _MTK_BSI_H +#define _MTK_BSI_H + +/* MTK Baseband Serial Interface */ + +enum bsi_reg { + BSI_CON = 0x0000, + BSI_D0_CON = 0x0004, + BSI_D0_DAT = 0x0008, + + BSI_ENA_0 = 0x0190, + BSI_ENA_1 = 0x0194, + BSI_IO_CON = 0x0198, + BSI_DOUT = 0x019c, + BSI_DIN = 0x01a0, + BSI_PAIR_NUM = 0x01a4, + +}; + +/* Compute offset of BSI_D0_CON / BSI_D0_DAT registers */ +#define BSI_Dn_CON(x) (BSI_D0_CON + (x * 8)) +#define BSI_Dn_CON(x) (BSI_D0_DAT + (x * 8)) + +/* MT6235 Section 9.1.1 */ +#define BSI_CON_CLK_POL_INV (1 << 0) +#define BSI_CON_CLK_SPD_52_2 (0 << 1) /* 26 MHz */ +#define BSI_CON_CLK_SPD_52_4 (1 << 1) /* 13 MHz */ +#define BSI_CON_CLK_SPD_52_6 (2 << 1) /* 8.67 MHz */ +#define BSI_CON_CLK_SPD_52_8 (3 << 1) /* 6.50 MHz */ +#define BSI_CON_IMOD (1 << 3) +#define BSI_CON_EN0_LEN_SHORT (1 << 4) +#define BSI_CON_EN0_POL_INV (1 << 5) +#define BSI_CON_EN0_LEN_SHORT (1 << 6) +#define BSI_CON_EN0_POL_INV (1 << 7) +#define BSI_CON_SETENV (1 << 8) + +/* how the length is encoded in BSI_Dx_CON */ +#define BSI_Dx_LEN(n) ((n & 0x7f) << 8) +#define BSI_Dx_ISB 0x8000 /* select device 1 */ + +#endif /* _MTK_BSI_H */ diff --git a/src/target/firmware/include/mtk/mt6139.h b/src/target/firmware/include/mtk/mt6139.h new file mode 100644 index 00000000..35458b5d --- /dev/null +++ b/src/target/firmware/include/mtk/mt6139.h @@ -0,0 +1,60 @@ +#ifndef _MTK_MT6139_H +#define _MTK_MT6139_H + +enum mt6139_band { + MTRF_BAND_GSM850 = 0, + MTRF_BAND_GSM900 = 1, + MTRF_BAND_GSM1800 = 2, + MTRF_BAND_GSM1900 = 3, +}; + +#define MT6139_CW0_SYNCP_SHIFT 0 +#define MT6139_CW0_SYNCPW (1 << 2) +#define MT6139_CW0_DIEN (1 << 3) +#define MT6139_CW0_FLT (1 << 4) +#define MT6139_CW0_AFC_SHIFT 5 +#define MT6139_CW0_VCO_SEL (1 << 11) +#define MT6139_CW0_GPO (1 << 12) +#define MT6139_CW0_POR (1 << 13) + +#define MT6139_CW1_NFRACT_SHIFT 0 +#define MT6139_CW1_NINT_SHIFT 8 +#define MT6139_CW1_BAND_SHIFT 16 +#define MT6139_CW1_TRX_850 (1 << 18) + +#define MT6139_CW2_GAINTBL_SHIFT 0 +#define MT6139_CW2_MODE_SHIFT 6 +#define MT6139_CW2_AUTO_CAL (1 << 9) +#define MT6139_CW2_DCD_AQ_SHIFT 10 +#define MT6139_CW2_DCD_AI_SHIFT 16 + +#define MT6139_CW9_DCD_CQ_SHIFT 0 +#define MT6139_CW9_DCD_BQ_SHIFT 7 +#define MT6139_CW9_PWR_DAC_C (1 << 14) +#define MT6139_CW9_PWR_DAC_B (1 << 15) +#define MT6139_CW9_PWR_DAC_A (1 << 16) +#define MT6139_CW9_AM_ENABLE (1 << 17) + +enum mt6139_cw2_mode { + MODE_SLEEP = 0x0, + MODE_WARM_UP = 0x1, + MODE_RECEIVE = 0x3, + MODE_TRANSMIT = 0x4, +}; + +#define MT6139_CW11_TX_CTL (1 << 0) +#define MT6139_CW11_TXG_IQM (1 << 1) +#define MT6139_CW11_TXD_IQM (1 << 2) +#define MT6139_CW11_TX_DIV2 (1 << 3) +#define MT6139_CW11_TX_DIV4 (1 << 4) +#define MT6139_CW11_TXG_BUF (1 << 5) +#define MT6139_CW11_TXD_BUF (1 << 6) +#define MT6139_CW11_TXMODGAIN_SHIFT 7 +#define MT6139_CW11_TX_FLT_SHIFT 10 +#define MT6139_CW11_TXAPC_SHIFT 14 +#define MT6139_CW11_TXPW_SHIFT 16 +#define MT6139_CW11_TXBIAST_SHIFT 18 +#define MT6139_CW11_TXDIV_GC0 (1 << 20) +#define MT6139_CW11_TXDIV_GC1 (1 << 21) + +#endif /* _MTK_MT6139_H */ diff --git a/src/target/firmware/include/mtk/mt6235_sciphone_g2.h b/src/target/firmware/include/mtk/mt6235_sciphone_g2.h new file mode 100644 index 00000000..74d9e7b8 --- /dev/null +++ b/src/target/firmware/include/mtk/mt6235_sciphone_g2.h @@ -0,0 +1,38 @@ +#ifndef _SCIPHONE_G2_H +#define _SCIPHONE_G2_H +/* Bluelans Sciphone G2 support */ + +/* Use of the Baseband Parallel Interface by the G2 board */ +#define HB_TX MTK_BPI(0) +#define PCS_RX MTK_BPI(1) +#define LB_TX MTK_BPI(2) +#define PA_EN MTK_BPI(4) +#define BAND_SW MTK_BPI(5) +#define MODE_PA MTK_BPI(7) +#define RF_VCO_EN MTK_BPI(9) + +#define GPIO_GPS_PWR_EN MTK_GPIO(19) +#define GPIO_WIFI_EN MTK_GPIO(20) +#define GPIO_OP1_EN MTK_GPIO(22) +#define GPIO_BT_PWR_EN MTK_GPIO(39) +#define GPIO_BT_RST MTK_GPIO(62) +#define GPIO_USB_CHR_ID MTK_GPIO(73) +#define GPIO_FM_SCL MTK_GPIO(46) +#define GPIO_FM_SDA MTK_GPIO(47) +#define GPIO_GS_SCL MTK_GPIO(48) +#define GPIO_GS_SDA MTK_GPIO(58) +#define GPIO_GS_EN MTK_GPIO(26) + +#define GPIO_GPS_EINT MTK_GPIO(42) + +#define EINT_HEADSET MTK_EINT(0) +#define EINT_BT MTK_EINT(1) +#define EINT_GPS2GSM MTK_EINT(2) +#define EINT_WIFI MTK_EINT(3) + +#define CLKM_BT_32k MTK_CLKM(2) +#define CLKM_WIFI_32k MTK_CLKM(3) +#define CLKM_FM_32k MTK_CLKM(4) + + +#endif /* _SCIPHONE_G2_H */ diff --git a/src/target/firmware/include/mtk/tdma_timer.h b/src/target/firmware/include/mtk/tdma_timer.h new file mode 100644 index 00000000..dec0a8a4 --- /dev/null +++ b/src/target/firmware/include/mtk/tdma_timer.h @@ -0,0 +1,60 @@ +#ifndef _MTK_TDMA_H +#define _MTK_TDMA_H + +/* MTK TDMA Timer */ + +/* MT6235 Section 11 */ + +enum mtk_tdma_reg { + /* Read current quarter bit count */ + TDMA_TQCNT = 0x0000, + /* Latched Qbit counter reset position */ + TDMA_WRAP = 0x0004, + /* Direct Qbit counter reset position */ + TDMA_WRAPIMD = 0x0008, + /* Event latch position */ + TDMA_EVTVAL = 0x000c, + /* DSP software control */ + TDMA_DTIRQ = 0x0010, + /* MCU software control */ + TDMA_CTIRQ1 = 0x0014, + TDMA_CTIRQ2 = 0x0018, + /* AFC control */ + TDMA_AFC0 = 0x0020, + TDMA_AFC1 = 0x0024, + TDMA_AFC2 = 0x0028, + TDMA_AFC3 = 0x002c, + + /* BSI event */ + TDMA_BSI0 = 0x00b0, + /* BPI event */ + TDMA_BPI0 = 0x0100, + /* Auxiliary ADC event */ + TDMA_AUXEV0 = 0x0400, + TDMA_AUXEV1 = 0x0404, + /* Event Control */ + TDMA_EVTENA0 = 0x0150, + TDMA_EVTENA1 = 0x0154, + TDMA_EVTENA2 = 0x0158, + TDMA_EVTENA3 = 0x015c, + TDMA_EVTENA4 = 0x0160, + TDMA_EVTENA5 = 0x0164, + TDMA_EVTENA6 = 0x0168, + TDMA_EVTENA6 = 0x016c, + TDMA_WRAPOFS = 0x0170, + TDMA_REGBIAS = 0x0174, + TDMA_DTXCON = 0x0180, + TDMA_RXCON = 0x0184, + TDMA_BDLCON = 0x0188, + TDMA_BULCON1 = 0x018c, + TDMA_BULCON2 = 0x0190, + TDMA_FB_FLAG = 0x0194, + TDMA_FB_CLRI = 0x0198, +}; + +#define TDMA_BSI(n) (TDMA_BSI0 + (n)*4) +#define TDMA_BPI(n) (TDMA_BPI0 + (n)*4) + + + +#endif /* _MTK_TDMA_H */ diff --git a/src/target/firmware/include/rf/trf6151.h b/src/target/firmware/include/rf/trf6151.h new file mode 100644 index 00000000..0e27abb9 --- /dev/null +++ b/src/target/firmware/include/rf/trf6151.h @@ -0,0 +1,43 @@ +#ifndef _TRF6151_H +#define _TRF6151_H + +#include <osmocore/gsm_utils.h> + +/* initialize (reset + power up) */ +void trf6151_init(void); + +/* switch power off or on */ +void trf6151_power(int on); + +/* set the VGA and RF gain */ +int trf6151_set_gain(uint8_t dbm, int high); + +/* obtain the current total gain of the TRF6151 */ +uint8_t trf6151_get_gain(void); + +/* Request the PLL to be tuned to the given frequency */ +void trf6151_set_arfcn(uint16_t arfcn, int uplink); + +enum trf6151_mode { + TRF6151_IDLE, + TRF6151_RX, + TRF6151_TX, +}; + +/* Set the operational mode of the TRF6151 chip */ +void trf6151_set_mode(enum trf6151_mode mode); + +void trf6151_test(uint16_t arfcn); +void trf6151_tx_test(uint16_t arfcn); + +/* prepare a Rx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_rx_window(int16_t start_qbits, uint16_t arfcn); + +/* prepare a Tx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_tx_window(int16_t start_qbits, uint16_t arfcn); + +/* Given the expected input level of exp_inp dBm and the target of target_bb + * dBm, configure the RF Frontend with the respective gain */ +void trf6151_compute_gain(int16_t exp_inp, int16_t target_bb); + +#endif /* TRF6151_H */ diff --git a/src/target/firmware/include/rffe.h b/src/target/firmware/include/rffe.h new file mode 100644 index 00000000..950e597e --- /dev/null +++ b/src/target/firmware/include/rffe.h @@ -0,0 +1,19 @@ +#ifndef _RFFE_H +#define _RFFE_H + +#include <osmocore/gsm_utils.h> + +extern const uint8_t system_inherent_gain; + +/* initialize RF Frontend */ +void rffe_init(void); + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx); + +/* get current gain of RF frontend (anything between antenna and baseband in dBm */ +uint8_t rffe_get_gain(void); + +void rffe_set_gain(int16_t exp_inp, int16_t target_bb); + +#endif diff --git a/src/target/firmware/include/spi.h b/src/target/firmware/include/spi.h new file mode 100644 index 00000000..0925a9a3 --- /dev/null +++ b/src/target/firmware/include/spi.h @@ -0,0 +1,7 @@ +#ifndef _SPI_H +#define _SPI_H + +void spi_init(void); +int spi_xfer(uint8_t dev_idx, uint8_t bitlen, const void *dout, void *din); + +#endif /* _SPI_H */ diff --git a/src/target/firmware/include/stdint.h b/src/target/firmware/include/stdint.h new file mode 100644 index 00000000..627403f9 --- /dev/null +++ b/src/target/firmware/include/stdint.h @@ -0,0 +1,36 @@ +#ifndef OSMO_STDINT_H +#define OSMO_STDINT_H + +/* some older toolchains (like gnuarm-3.x) don't provide a C99 + compliant stdint.h yet, so we define our own here */ + +/* to make matters worse newer gcc with glibc headers have + a incompatible definition of these types. We will use the + gcc'ism of #include_next to include the compiler's libc + header file and then check if it has defined int8_t and + if not we will use our own typedefs */ + +/* another bad criteria. We can not detect __NEWLIB_H__ or + _NEWLIB_VERSION. Assume that older GCCs have a older C library + that did not include a stdint.h yet. This is for gnuarm-3.x + one of the compilers producing working code right now. */ + +#if __GNUC__ > 3 +#include_next <stdint.h> +#endif + +#ifndef __int8_t_defined +typedef signed char int8_t; +typedef unsigned char uint8_t; + +typedef signed short int16_t; +typedef unsigned short uint16_t; + +typedef signed int int32_t; +typedef unsigned int uint32_t; + +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif + +#endif diff --git a/src/target/firmware/include/stdio.h b/src/target/firmware/include/stdio.h new file mode 100644 index 00000000..15ed6688 --- /dev/null +++ b/src/target/firmware/include/stdio.h @@ -0,0 +1,52 @@ +#ifndef _STDIO_H +#define _STDIO_H + +#ifndef NULL +#define NULL 0 +#endif /* NULL */ + +#include <sys/types.h> + +int printf(const char *format, ...); +int sprintf(char *str, const char *format, ...); +int snprintf(char *str, size_t size, const char *format, ...); + +#include <stdarg.h> + +int vprintf(const char *format, va_list ap); +int vsprintf(char *str, const char *format, va_list ap); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +int puts(const char *s); + +#if 0 +/* start.S based uart console */ +#include <calypso/uart.h> +#define putchar(c) uart_putchar_wait(1, c) +int puts(const char *s); +#endif + +#if 0 +/* regular UART console */ +#include <console.h> +#define putchar(c) cons_putchar(c) +#define _puts(s) cons_puts(s) +#define ARCH_HAS_CONSOLE +#endif + +#if 1 +/* sercomm based console */ +#include <comm/sercomm_cons.h> +#define putchar(c) sercomm_putchar(c) +#define _puts(s) sercomm_puts(s) +#define ARCH_HAS_CONSOLE +#endif + +struct __file { +}; + +typedef struct __file FILE; + +/* non-standard */ +extern void phex(unsigned int c, unsigned int len); + +#endif /* _STDIO_H */ diff --git a/src/target/firmware/include/string.h b/src/target/firmware/include/string.h new file mode 100644 index 00000000..f060659a --- /dev/null +++ b/src/target/firmware/include/string.h @@ -0,0 +1,12 @@ +#ifndef _STRING_H +#define _STRING_H + +#include <sys/types.h> + +size_t strnlen(const char *s, size_t count); +size_t strlen(const char *s); + +void *memset(void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); + +#endif diff --git a/src/target/firmware/include/swab.h b/src/target/firmware/include/swab.h new file mode 100644 index 00000000..61be900d --- /dev/null +++ b/src/target/firmware/include/swab.h @@ -0,0 +1,297 @@ +#ifndef _LINUX_SWAB_H +#define _LINUX_SWAB_H + +#include <stdint.h> +#include <defines.h> +#include <asm/swab.h> + +/* + * casts are necessary for constants, because we never know how for sure + * how U/UL/ULL map to uint16_t, uint32_t, uint64_t. At least not in a portable way. + */ +#define ___constant_swab16(x) ((uint16_t)( \ + (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \ + (((uint16_t)(x) & (uint16_t)0xff00U) >> 8))) + +#define ___constant_swab32(x) ((uint32_t)( \ + (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24))) + +#define ___constant_swab64(x) ((uint64_t)( \ + (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56))) + +#define ___constant_swahw32(x) ((uint32_t)( \ + (((uint32_t)(x) & (uint32_t)0x0000ffffUL) << 16) | \ + (((uint32_t)(x) & (uint32_t)0xffff0000UL) >> 16))) + +#define ___constant_swahb32(x) ((uint32_t)( \ + (((uint32_t)(x) & (uint32_t)0x00ff00ffUL) << 8) | \ + (((uint32_t)(x) & (uint32_t)0xff00ff00UL) >> 8))) + +/* + * Implement the following as inlines, but define the interface using + * macros to allow constant folding when possible: + * ___swab16, ___swab32, ___swab64, ___swahw32, ___swahb32 + */ + +static inline __attribute_const__ uint16_t __fswab16(uint16_t val) +{ +#ifdef __arch_swab16 + return __arch_swab16(val); +#else + return ___constant_swab16(val); +#endif +} + +static inline __attribute_const__ uint32_t __fswab32(uint32_t val) +{ +#ifdef __arch_swab32 + return __arch_swab32(val); +#else + return ___constant_swab32(val); +#endif +} + +static inline __attribute_const__ uint64_t __fswab64(uint64_t val) +{ +#ifdef __arch_swab64 + return __arch_swab64(val); +#elif defined(__SWAB_64_THRU_32__) + uint32_t h = val >> 32; + uint32_t l = val & ((1ULL << 32) - 1); + return (((uint64_t)__fswab32(l)) << 32) | ((uint64_t)(__fswab32(h))); +#else + return ___constant_swab64(val); +#endif +} + +static inline __attribute_const__ uint32_t __fswahw32(uint32_t val) +{ +#ifdef __arch_swahw32 + return __arch_swahw32(val); +#else + return ___constant_swahw32(val); +#endif +} + +static inline __attribute_const__ uint32_t __fswahb32(uint32_t val) +{ +#ifdef __arch_swahb32 + return __arch_swahb32(val); +#else + return ___constant_swahb32(val); +#endif +} + +/** + * __swab16 - return a byteswapped 16-bit value + * @x: value to byteswap + */ +#define __swab16(x) \ + (__builtin_constant_p((uint16_t)(x)) ? \ + ___constant_swab16(x) : \ + __fswab16(x)) + +/** + * __swab32 - return a byteswapped 32-bit value + * @x: value to byteswap + */ +#define __swab32(x) \ + (__builtin_constant_p((uint32_t)(x)) ? \ + ___constant_swab32(x) : \ + __fswab32(x)) + +/** + * __swab64 - return a byteswapped 64-bit value + * @x: value to byteswap + */ +#define __swab64(x) \ + (__builtin_constant_p((uint64_t)(x)) ? \ + ___constant_swab64(x) : \ + __fswab64(x)) + +/** + * __swahw32 - return a word-swapped 32-bit value + * @x: value to wordswap + * + * __swahw32(0x12340000) is 0x00001234 + */ +#define __swahw32(x) \ + (__builtin_constant_p((uint32_t)(x)) ? \ + ___constant_swahw32(x) : \ + __fswahw32(x)) + +/** + * __swahb32 - return a high and low byte-swapped 32-bit value + * @x: value to byteswap + * + * __swahb32(0x12345678) is 0x34127856 + */ +#define __swahb32(x) \ + (__builtin_constant_p((uint32_t)(x)) ? \ + ___constant_swahb32(x) : \ + __fswahb32(x)) + +/** + * __swab16p - return a byteswapped 16-bit value from a pointer + * @p: pointer to a naturally-aligned 16-bit value + */ +static inline uint16_t __swab16p(const uint16_t *p) +{ +#ifdef __arch_swab16p + return __arch_swab16p(p); +#else + return __swab16(*p); +#endif +} + +/** + * __swab32p - return a byteswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + */ +static inline uint32_t __swab32p(const uint32_t *p) +{ +#ifdef __arch_swab32p + return __arch_swab32p(p); +#else + return __swab32(*p); +#endif +} + +/** + * __swab64p - return a byteswapped 64-bit value from a pointer + * @p: pointer to a naturally-aligned 64-bit value + */ +static inline uint64_t __swab64p(const uint64_t *p) +{ +#ifdef __arch_swab64p + return __arch_swab64p(p); +#else + return __swab64(*p); +#endif +} + +/** + * __swahw32p - return a wordswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahw32() for details of wordswapping. + */ +static inline uint32_t __swahw32p(const uint32_t *p) +{ +#ifdef __arch_swahw32p + return __arch_swahw32p(p); +#else + return __swahw32(*p); +#endif +} + +/** + * __swahb32p - return a high and low byteswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahb32() for details of high/low byteswapping. + */ +static inline uint32_t __swahb32p(const uint32_t *p) +{ +#ifdef __arch_swahb32p + return __arch_swahb32p(p); +#else + return __swahb32(*p); +#endif +} + +/** + * __swab16s - byteswap a 16-bit value in-place + * @p: pointer to a naturally-aligned 16-bit value + */ +static inline void __swab16s(uint16_t *p) +{ +#ifdef __arch_swab16s + __arch_swab16s(p); +#else + *p = __swab16p(p); +#endif +} +/** + * __swab32s - byteswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + */ +static inline void __swab32s(uint32_t *p) +{ +#ifdef __arch_swab32s + __arch_swab32s(p); +#else + *p = __swab32p(p); +#endif +} + +/** + * __swab64s - byteswap a 64-bit value in-place + * @p: pointer to a naturally-aligned 64-bit value + */ +static inline void __swab64s(uint64_t *p) +{ +#ifdef __arch_swab64s + __arch_swab64s(p); +#else + *p = __swab64p(p); +#endif +} + +/** + * __swahw32s - wordswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahw32() for details of wordswapping + */ +static inline void __swahw32s(uint32_t *p) +{ +#ifdef __arch_swahw32s + __arch_swahw32s(p); +#else + *p = __swahw32p(p); +#endif +} + +/** + * __swahb32s - high and low byteswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahb32() for details of high and low byte swapping + */ +static inline void __swahb32s(uint32_t *p) +{ +#ifdef __arch_swahb32s + __arch_swahb32s(p); +#else + *p = __swahb32p(p); +#endif +} + +# define swab16 __swab16 +# define swab32 __swab32 +# define swab64 __swab64 +# define swahw32 __swahw32 +# define swahb32 __swahb32 +# define swab16p __swab16p +# define swab32p __swab32p +# define swab64p __swab64p +# define swahw32p __swahw32p +# define swahb32p __swahb32p +# define swab16s __swab16s +# define swab32s __swab32s +# define swab64s __swab64s +# define swahw32s __swahw32s +# define swahb32s __swahb32s + +#endif /* _LINUX_SWAB_H */ diff --git a/src/target/firmware/include/uwire.h b/src/target/firmware/include/uwire.h new file mode 100644 index 00000000..6d345534 --- /dev/null +++ b/src/target/firmware/include/uwire.h @@ -0,0 +1,7 @@ +#ifndef _UWIRE_H +#define _UWIRE_H + +void uwire_init(void); +int uwire_xfer(int cs, int bitlen, const void *dout, void *din); + +#endif /* _UWIRE_H */ diff --git a/src/target/firmware/layer1/Makefile b/src/target/firmware/layer1/Makefile new file mode 100644 index 00000000..4f834ea4 --- /dev/null +++ b/src/target/firmware/layer1/Makefile @@ -0,0 +1,9 @@ + +LIBRARIES+=layer1 +layer1_DIR=layer1 +layer1_SRCS=avg.c agc.c afc.c sync.c tdma_sched.c tpu_window.c init.c l23_api.c \ + mframe_sched.c sched_gsmtime.c async.c rfch.c apc.c + +layer1_SRCS += prim_pm.c prim_rach.c prim_tx_nb.c prim_rx_nb.c prim_fbsb.c \ + prim_freq.c prim_utils.c prim_tch.c + diff --git a/src/target/firmware/layer1/afc.c b/src/target/firmware/layer1/afc.c new file mode 100644 index 00000000..3890972d --- /dev/null +++ b/src/target/firmware/layer1/afc.c @@ -0,0 +1,130 @@ +/* AFC (Automatic Frequency Correction) Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <osmocore/gsm_utils.h> + +#include <layer1/afc.h> +#include <layer1/avg.h> +#include <calypso/dsp.h> + +#define AFC_INITIAL_DAC_VALUE -700 + +/* Over how many TDMA frames do we want to average? (this may change in dedicated mode) */ +#define AFC_PERIOD 40 +/* How many of our measurements have to be valid? */ +#define AFC_MIN_MUN_VALID 8 + +/* The actual AFC code */ + +struct afc_state { + struct running_avg ravg; /* running average */ + int16_t dac_value; /* current DAC output value */ + uint16_t arfcn; +}; + +static void afc_ravg_output(struct running_avg *ravg, int32_t avg); + +static struct afc_state afc_state = { + .ravg = { + .outfn = &afc_ravg_output, + .period = AFC_PERIOD, + .min_valid = AFC_MIN_MUN_VALID, + }, + .dac_value = AFC_INITIAL_DAC_VALUE, +}; + +/* The AFC DAC in the ABB has to be configured as follows: + * DAC = 1MHz / 947MHz * FreqErr(Hz) / AFCslop(ppm/LSB) + * where: + * 947 MHz is the center of EGSM + * AFCslope is coded F1.15, thus a normalization factor of 2^15 aplpies + */ + +#define AFC_NORM_FACTOR_GSM ((1<<15) / 947) +#define AFC_NORM_FACTOR_DCS ((1<<15) / 1894) + +/* we assume 8.769ppb per LSB, equals 0.008769 * 32768 == 287 */ +//#define AFC_SLOPE 320 +#define AFC_SLOPE 287 + +/* The DSP can measure the frequency error in the following ranges: + * FB_MODE0: +/- 20 kHz + * FB_MODE1: +/- 4 kHz + * Sync Burst: +/- 1 kHz + * Normal Burst: +/- 400 Hz + */ + +/* Update the AFC with a frequency error, bypassing averaging */ +void afc_correct(int16_t freq_error, uint16_t arfcn) +{ + int32_t afc_norm_factor; + int16_t delta; + + switch (gsm_arfcn2band(arfcn)) { + case GSM_BAND_900: + case GSM_BAND_850: + afc_norm_factor = AFC_NORM_FACTOR_GSM; + break; + default: + afc_norm_factor = AFC_NORM_FACTOR_DCS; + } + + delta = (int16_t) ((afc_norm_factor * (int32_t)freq_error) / AFC_SLOPE); + printd("afc_correct(error=%dHz, arfcn=%u): delta=%d, afc_dac(old=%d,new=%d)\n", + freq_error, arfcn, delta, afc_state.dac_value, afc_state.dac_value+delta); + afc_state.dac_value += delta; + + /* The AFC DAC has only 13 bits */ + if (afc_state.dac_value > 4095) + afc_state.dac_value = 4095; + else if (afc_state.dac_value < -4096) + afc_state.dac_value = -4096; +} + +void afc_reset(void) +{ + afc_state.dac_value = AFC_INITIAL_DAC_VALUE; +} + +void afc_input(int32_t freq_error, uint16_t arfcn, int valid) +{ + afc_state.arfcn = arfcn; + runavg_input(&afc_state.ravg, freq_error, valid); + runavg_check_output(&afc_state.ravg); +} + +/* callback function for runavg */ +static void afc_ravg_output(struct running_avg *ravg, int32_t avg) +{ + afc_correct(avg, afc_state.arfcn); +} + +/* Update DSP with new AFC DAC value to be used for next TDMA frame */ +void afc_load_dsp(void) +{ + dsp_api.db_w->d_afc = afc_state.dac_value; + dsp_api.db_w->d_ctrl_abb |= (1 << B_AFC); +} diff --git a/src/target/firmware/layer1/agc.c b/src/target/firmware/layer1/agc.c new file mode 100644 index 00000000..780e260f --- /dev/null +++ b/src/target/firmware/layer1/agc.c @@ -0,0 +1,62 @@ +/* AFC (Automatic Gain Control) Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <osmocore/gsm_utils.h> +#include <debug.h> +#include <rffe.h> + +#include <layer1/agc.h> +#include <calypso/dsp.h> + +/* compute the input level present at the antenna based on a baseband + * power measurement of the DSP at baseband */ +int16_t agc_inp_dbm8_by_pm(int16_t pm) +{ + /* pm is in 1/8 dBm at baseband */ + int16_t total_gain_dbm8; + + /* compute total current gain */ + total_gain_dbm8 = (system_inherent_gain + rffe_get_gain()) * 8; + + /* subtract gain from power measurement at baseband level */ + return pm - total_gain_dbm8; +} + +uint8_t agc_il_by_dbm8(int16_t dbm8) +{ + uint16_t il; + + /* convert from 1/8 dBm to l1c format: [220..0] in -1/2dBm unit */ + if (dbm8 >= 0) + il = 0; + else + il = -dbm8; + + /* saturate */ + if (il > 4 * 255) + il = 4 * 255; + + return (uint8_t)(il >> 2); +} diff --git a/src/target/firmware/layer1/apc.c b/src/target/firmware/layer1/apc.c new file mode 100644 index 00000000..5cff191b --- /dev/null +++ b/src/target/firmware/layer1/apc.c @@ -0,0 +1,57 @@ +/* APC (Automatic Power Control) Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <errno.h> + +#include <osmocore/utils.h> +#include <osmocore/gsm_utils.h> + +#include <layer1/apc.h> + +/* calibration table defined in board file */ +extern const int16_t dbm2apc_gsm900[]; +extern const int dbm2apc_gsm900_max; + + +/* determine the AUXAPC value by the Tx Power Level */ +int16_t apc_tx_dbm2auxapc(enum gsm_band band, int8_t dbm) +{ + if (dbm < 0) + return -ERANGE; + + /* FIXME: offsets for different bands! */ + if (dbm > dbm2apc_gsm900_max) + dbm = dbm2apc_gsm900_max; + + return dbm2apc_gsm900[dbm]; +} + +/* determine the AUXAPC value by the Tx Power Level */ +int16_t apc_tx_pwrlvl2auxapc(enum gsm_band band, uint8_t lvl) +{ + /* convert tx power level to dBm */ + int dbm = ms_pwr_dbm(band, lvl); + if (dbm < 0) + return dbm; + + return apc_tx_dbm2auxapc(band, dbm); +} diff --git a/src/target/firmware/layer1/async.c b/src/target/firmware/layer1/async.c new file mode 100644 index 00000000..76d0b721 --- /dev/null +++ b/src/target/firmware/layer1/async.c @@ -0,0 +1,137 @@ +/* Asynchronous part of GSM Layer 1 */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +#include <debug.h> +#include <arm.h> +#include <asm/system.h> + +#include <osmocore/msgb.h> +#include <osmocore/protocol/gsm_04_08.h> + +#include <layer1/sync.h> +#include <layer1/async.h> +#include <layer1/mframe_sched.h> +#include <layer1/sched_gsmtime.h> +#include <layer1/l23_api.h> +#include <calypso/l1_environment.h> + +extern const struct tdma_sched_item rach_sched_set_ul[]; + +/* safely enable a message into the L1S TX queue */ +void l1a_txq_msgb_enq(struct llist_head *queue, struct msgb *msg) +{ + unsigned long flags; + + local_firq_save(flags); + msgb_enqueue(queue, msg); + local_irq_restore(flags); +} + +void l1a_meas_msgb_set(struct msgb *msg) +{ + unsigned long flags; + + local_firq_save(flags); + if (l1s.tx_meas) + msgb_free(l1s.tx_meas); + l1s.tx_meas = msg; + local_irq_restore(flags); +} + +/* safely flush all pending msgb */ +void l1a_txq_msgb_flush(struct llist_head *queue) +{ + struct msgb *msg; + unsigned long flags; + + local_firq_save(flags); + while ((msg = msgb_dequeue(queue))) + msgb_free(msg); + local_irq_restore(flags); +} + +/* Enable a repeating multiframe task */ +void l1a_mftask_enable(enum mframe_task task) +{ + /* we don't need locking here as L1S only reads mframe.tasks */ + mframe_enable(task); +} + +/* Disable a repeating multiframe task */ +void l1a_mftask_disable(enum mframe_task task) +{ + /* we don't need locking here as L1S only reads mframe.tasks */ + mframe_disable(task); +} + +/* Set the mask for repeating multiframe tasks */ +void l1a_mftask_set(uint32_t tasks) +{ + /* we don't need locking here as L1S only reads mframe.tasks */ + mframe_set(tasks); +} + +/* Set TCH mode */ +uint8_t l1a_tch_mode_set(uint8_t mode) +{ + switch (mode) { + case GSM48_CMODE_SPEECH_V1: + case GSM48_CMODE_SPEECH_EFR: + l1s.tch_mode = mode; + break; + default: + l1s.tch_mode = GSM48_CMODE_SIGN; + } + + return l1s.tch_mode; +} + +/* Initialize asynchronous part of Layer1 */ +void l1a_init(void) +{ + l1a_l23api_init(); +} + +/* Execute pending L1A completions */ +void l1a_compl_execute(void) +{ + unsigned long flags; + unsigned int scheduled; + unsigned int i; + + /* get and reset the currently scheduled tasks */ + local_firq_save(flags); + scheduled = l1s.scheduled_compl; + l1s.scheduled_compl = 0; + local_irq_restore(flags); + + /* Iterate over list of scheduled completions, call their + * respective completion handler */ + for (i = 0; i < 32; i++) { + if (!(scheduled & (1 << i))) + continue; + /* call completion function */ + l1s.completion[i](i); + } +} diff --git a/src/target/firmware/layer1/avg.c b/src/target/firmware/layer1/avg.c new file mode 100644 index 00000000..a4bf565b --- /dev/null +++ b/src/target/firmware/layer1/avg.c @@ -0,0 +1,57 @@ +/* Averaging Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +#include <layer1/avg.h> + +/* input a new sample into the averaging process */ +void runavg_input(struct running_avg *ravg, int32_t val, int valid) +{ + ravg->num_samples++; + if (valid) { + ravg->acc_val += val; + ravg->num_samples_valid++; + } +} + +/* check if sufficient samples have been obtained, and call outfn() */ +int runavg_check_output(struct running_avg *ravg) +{ + if (ravg->num_samples < ravg->period) + return 0; + + if (ravg->num_samples_valid >= ravg->min_valid) { + int32_t avg = ravg->acc_val / ravg->num_samples_valid; + + ravg->outfn(ravg, avg); + + ravg->num_samples = ravg->num_samples_valid = 0; + ravg->acc_val = 0; + + return 1; + } + + return 0; +} + + diff --git a/src/target/firmware/layer1/init.c b/src/target/firmware/layer1/init.c new file mode 100644 index 00000000..7af327eb --- /dev/null +++ b/src/target/firmware/layer1/init.c @@ -0,0 +1,74 @@ +/* OsmocomBB Layer1 initialization */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <stdint.h> +#include <stdio.h> + +#include <rffe.h> +#include <rf/trf6151.h> +#include <abb/twl3025.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/irq.h> + +#include <layer1/sync.h> +#include <layer1/async.h> +#include <layer1/l23_api.h> + +void layer1_init(void) +{ +#ifndef CONFIG_TX_ENABLE + printf("\n\nTHIS FIRMWARE WAS COMPILED WITHOUT TX SUPPORT!!!\n\n"); +#endif + + /* initialize asynchronous part of L1 */ + l1a_init(); + /* initialize TDMA Frame IRQ driven synchronous L1 */ + l1s_init(); + /* power up the DSP */ + dsp_power_on(); + + /* Initialize TPU, TSP and TRF drivers */ + tpu_init(); + tsp_init(); + trf6151_init(); + + rffe_init(); + +#if 0 /* only if RX TPU window is disabled! */ + /* Put TWL3025 in downlink mode (includes calibration) */ + twl3025_downlink(1, 1000); +#endif + + /* issue the TRF and TWL initialization sequence */ + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); + + /* Disable RTC interrupt as it causes lost TDMA frames */ + irq_disable(IRQ_RTC_TIMER); + + /* inform l2 and upwards that we are ready for orders */ + l1ctl_tx_reset(L1CTL_RESET_IND, L1CTL_RES_T_BOOT); +} diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c new file mode 100644 index 00000000..aef12a59 --- /dev/null +++ b/src/target/firmware/layer1/l23_api.c @@ -0,0 +1,536 @@ +/* Synchronous part of GSM Layer 1: API to Layer2+ */ + +/* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#define DEBUG + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <debug.h> +#include <byteorder.h> + +#include <osmocore/msgb.h> +#include <osmocore/protocol/gsm_04_08.h> +#include <comm/sercomm.h> + +#include <layer1/sync.h> +#include <layer1/async.h> +#include <layer1/mframe_sched.h> +#include <layer1/prim.h> +#include <layer1/tpu_window.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> + +#include <l1ctl_proto.h> + +/* the size we will allocate struct msgb* for HDLC */ +#define L3_MSG_HEAD 4 +#define L3_MSG_SIZE (sizeof(struct l1ctl_hdr)+sizeof(struct l1ctl_info_dl)+sizeof(struct l1ctl_data_ind) + L3_MSG_HEAD) + +void l1_queue_for_l2(struct msgb *msg) +{ + /* forward via serial for now */ + sercomm_sendmsg(SC_DLCI_L1A_L23, msg); +} + +static enum mframe_task chan_nr2mf_task(uint8_t chan_nr) +{ + uint8_t cbits = chan_nr >> 3; + uint8_t tn = chan_nr & 0x7; + uint8_t lch_idx; + + if (cbits == 0x01) { + lch_idx = 0; + return (tn & 1) ? MF_TASK_TCH_F_ODD : MF_TASK_TCH_F_EVEN; + } else if ((cbits & 0x1e) == 0x02) { + lch_idx = cbits & 0x1; + return MF_TASK_TCH_H_0 + lch_idx; + } else if ((cbits & 0x1c) == 0x04) { + lch_idx = cbits & 0x3; + return MF_TASK_SDCCH4_0 + lch_idx; + } else if ((cbits & 0x18) == 0x08) { + lch_idx = cbits & 0x7; + return MF_TASK_SDCCH8_0 + lch_idx; +#if 0 + } else if (cbits == 0x10) { + /* FIXME: when to do extended BCCH? */ + return MF_TASK_BCCH_NORM; + } else if (cbits == 0x11 || cbits == 0x12) { + /* FIXME: how to decide CCCH norm/extd? */ + return MF_TASK_BCCH_CCCH; +#endif + } + return 0; +} + +static int chan_nr2dchan_type(uint8_t chan_nr) +{ + uint8_t cbits = chan_nr >> 3; + + if (cbits == 0x01) { + return GSM_DCHAN_TCH_F; + } else if ((cbits & 0x1e) == 0x02) { + return GSM_DCHAN_TCH_H; + } else if ((cbits & 0x1c) == 0x04) { + return GSM_DCHAN_SDCCH_4; + } else if ((cbits & 0x18) == 0x08) { + return GSM_DCHAN_SDCCH_8; + } + return GSM_DCHAN_UNKNOWN; +} + +static int chan_nr_is_tch(uint8_t chan_nr) +{ + return ((chan_nr >> 3) == 0x01 || /* TCH/F */ + ((chan_nr >> 3) & 0x1e) == 0x02); /* TCH/H */ +} + +static void audio_set_enabled(int enabled) +{ + twl3025_unit_enable(TWL3025_UNIT_VUL, enabled); + twl3025_unit_enable(TWL3025_UNIT_VDL, enabled); +} + +struct msgb *l1ctl_msgb_alloc(uint8_t msg_type) +{ + struct msgb *msg; + struct l1ctl_hdr *l1h; + + msg = msgb_alloc_headroom(L3_MSG_SIZE, L3_MSG_HEAD, "l1ctl"); + if (!msg) { + while (1) { + puts("OOPS. Out of buffers...\n"); + } + + return NULL; + } + l1h = (struct l1ctl_hdr *) msgb_put(msg, sizeof(*l1h)); + l1h->msg_type = msg_type; + l1h->flags = 0; + + msg->l1h = (uint8_t *)l1h; + + return msg; +} + +struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr, + uint16_t arfcn) +{ + struct l1ctl_info_dl *dl; + struct msgb *msg = l1ctl_msgb_alloc(msg_type); + + dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl)); + dl->frame_nr = htonl(fn); + dl->snr = snr; + dl->band_arfcn = htons(arfcn); + + return msg; +} + +/* receive a L1CTL_FBSB_REQ from L23 */ +static void l1ctl_rx_fbsb_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_fbsb_req *sync_req = (struct l1ctl_fbsb_req *) l1h->data; + + if (sizeof(*sync_req) > msg->len) { + printf("Short sync msg. %u\n", msg->len); + return; + } + + printd("L1CTL_FBSB_REQ (arfcn=%u, flags=0x%x)\n", + ntohs(sync_req->band_arfcn), sync_req->flags); + + /* reset scheduler and hardware */ + l1s_reset(); + + /* pre-set the CCCH mode */ + l1s.serving_cell.ccch_mode = sync_req->ccch_mode; + + printd("Starting FCCH Recognition\n"); + l1s_fbsb_req(1, sync_req); +} + +/* receive a L1CTL_DM_EST_REQ from L23 */ +static void l1ctl_rx_dm_est_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; + struct l1ctl_dm_est_req *est_req = (struct l1ctl_dm_est_req *) ul->payload; + + printd("L1CTL_DM_EST_REQ (arfcn=%u, chan_nr=0x%02x, tsc=%u)\n", + ntohs(est_req->h0.band_arfcn), ul->chan_nr, est_req->tsc); + + /* configure dedicated channel state */ + l1s.dedicated.type = chan_nr2dchan_type(ul->chan_nr); + l1s.dedicated.tsc = est_req->tsc; + l1s.dedicated.tn = ul->chan_nr & 0x7; + l1s.dedicated.h = est_req->h; + + if (est_req->h) { + int i; + l1s.dedicated.h1.hsn = est_req->h1.hsn; + l1s.dedicated.h1.maio = est_req->h1.maio; + l1s.dedicated.h1.n = est_req->h1.n; + for (i=0; i<est_req->h1.n; i++) + l1s.dedicated.h1.ma[i] = ntohs(est_req->h1.ma[i]); + } else { + l1s.dedicated.h0.arfcn = ntohs(est_req->h0.band_arfcn); + } + + /* TCH config */ + if (chan_nr_is_tch(ul->chan_nr)) { + /* Mode */ + l1a_tch_mode_set(est_req->tch_mode); + + /* Sync */ + l1s.tch_sync = 1; /* can be set without locking */ + + /* Audio path */ + audio_set_enabled(est_req->tch_mode != GSM48_CMODE_SIGN); + } + + /* figure out which MF tasks to enable */ + l1a_mftask_set(1 << chan_nr2mf_task(ul->chan_nr)); +} + +/* receive a L1CTL_DM_FREQ_REQ from L23 */ +static void l1ctl_rx_dm_freq_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; + struct l1ctl_dm_freq_req *freq_req = + (struct l1ctl_dm_freq_req *) ul->payload; + + printd("L1CTL_DM_FREQ_REQ (arfcn=%u, tsc=%u)\n", + ntohs(freq_req->h0.band_arfcn), freq_req->tsc); + + /* configure dedicated channel state */ + l1s.dedicated.st_tsc = freq_req->tsc; + l1s.dedicated.st_h = freq_req->h; + + if (freq_req->h) { + int i; + l1s.dedicated.st_h1.hsn = freq_req->h1.hsn; + l1s.dedicated.st_h1.maio = freq_req->h1.maio; + l1s.dedicated.st_h1.n = freq_req->h1.n; + for (i=0; i<freq_req->h1.n; i++) + l1s.dedicated.st_h1.ma[i] = ntohs(freq_req->h1.ma[i]); + } else { + l1s.dedicated.st_h0.arfcn = ntohs(freq_req->h0.band_arfcn); + } + + l1a_freq_req(ntohs(freq_req->fn)); +} + +/* receive a L1CTL_CRYPTO_REQ from L23 */ +static void l1ctl_rx_crypto_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; + struct l1ctl_crypto_req *cr = (struct l1ctl_crypto_req *) ul->payload; + uint8_t key_len = msg->len - sizeof(*l1h) - sizeof(*ul) - sizeof(*cr); + + printd("L1CTL_CRYPTO_REQ (algo=A5/%u, len=%u)\n", cr->algo, key_len); + + if (cr->algo && key_len != 8) { + printd("L1CTL_CRYPTO_REQ -> Invalid key\n"); + return; + } + + dsp_load_ciph_param(cr->algo, cr->key); +} + +/* receive a L1CTL_DM_REL_REQ from L23 */ +static void l1ctl_rx_dm_rel_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; + + printd("L1CTL_DM_REL_REQ\n"); + l1a_mftask_set(0); + l1s.dedicated.type = GSM_DCHAN_NONE; + l1a_txq_msgb_flush(&l1s.tx_queue[L1S_CHAN_MAIN]); + l1a_txq_msgb_flush(&l1s.tx_queue[L1S_CHAN_SACCH]); + l1a_meas_msgb_set(NULL); + dsp_load_ciph_param(0, NULL); + l1a_tch_mode_set(GSM48_CMODE_SIGN); + audio_set_enabled(0); +} + +/* receive a L1CTL_PARAM_REQ from L23 */ +static void l1ctl_rx_param_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; + struct l1ctl_par_req *par_req = (struct l1ctl_par_req *) ul->payload; + + printd("L1CTL_PARAM_REQ (ta=%d, tx_power=%d)\n", par_req->ta, + par_req->tx_power); + + l1s.ta = par_req->ta; + l1s.tx_power = par_req->tx_power; +} + +/* receive a L1CTL_RACH_REQ from L23 */ +static void l1ctl_rx_rach_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; + struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *) ul->payload; + + printd("L1CTL_RACH_REQ (ra=0x%02x, offset=%d combined=%d)\n", + rach_req->ra, ntohs(rach_req->offset), rach_req->combined); + + l1a_rach_req(ntohs(rach_req->offset), rach_req->combined, + rach_req->ra); +} + +/* receive a L1CTL_DATA_REQ from L23 */ +static void l1ctl_rx_data_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; + struct l1ctl_data_ind *data_ind = (struct l1ctl_data_ind *) ul->payload; + struct llist_head *tx_queue; + + printd("L1CTL_DATA_REQ (link_id=0x%02x)\n", ul->link_id); + + msg->l3h = data_ind->data; + if (ul->link_id & 0x40) { + struct gsm48_hdr *gh = (struct gsm48_hdr *)(data_ind->data + 5); + if (gh->proto_discr == GSM48_PDISC_RR + && gh->msg_type == GSM48_MT_RR_MEAS_REP) { + printd("updating measurement report\n"); + l1a_meas_msgb_set(msg); + return; + } + tx_queue = &l1s.tx_queue[L1S_CHAN_SACCH]; + } else + tx_queue = &l1s.tx_queue[L1S_CHAN_MAIN]; + + printd("ul=%p, ul->payload=%p, data_ind=%p, data_ind->data=%p l3h=%p\n", + ul, ul->payload, data_ind, data_ind->data, msg->l3h); + + l1a_txq_msgb_enq(tx_queue, msg); +} + +/* receive a L1CTL_PM_REQ from L23 */ +static void l1ctl_rx_pm_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_pm_req *pm_req = (struct l1ctl_pm_req *) l1h->data; + + switch (pm_req->type) { + case 1: + l1s.pm.mode = 1; + l1s.pm.range.arfcn_start = + ntohs(pm_req->range.band_arfcn_from); + l1s.pm.range.arfcn_next = + ntohs(pm_req->range.band_arfcn_from); + l1s.pm.range.arfcn_end = + ntohs(pm_req->range.band_arfcn_to); + printf("L1CTL_PM_REQ start=%u end=%u\n", + l1s.pm.range.arfcn_start, l1s.pm.range.arfcn_end); + break; + } + + l1s_pm_test(1, l1s.pm.range.arfcn_next); +} + +/* Transmit a L1CTL_RESET_IND or L1CTL_RESET_CONF */ +void l1ctl_tx_reset(uint8_t msg_type, uint8_t reset_type) +{ + struct msgb *msg = l1ctl_msgb_alloc(msg_type); + struct l1ctl_reset *reset_resp; + reset_resp = (struct l1ctl_reset *) + msgb_put(msg, sizeof(*reset_resp)); + reset_resp->type = reset_type; + + l1_queue_for_l2(msg); +} + +/* receive a L1CTL_RESET_REQ from L23 */ +static void l1ctl_rx_reset_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_reset *reset_req = + (struct l1ctl_reset *) l1h->data; + + switch (reset_req->type) { + case L1CTL_RES_T_FULL: + printf("L1CTL_RESET_REQ: FULL!\n"); + l1s_reset(); + l1s_reset_hw(); + audio_set_enabled(0); + l1ctl_tx_reset(L1CTL_RESET_CONF, reset_req->type); + break; + case L1CTL_RES_T_SCHED: + printf("L1CTL_RESET_REQ: SCHED!\n"); + l1ctl_tx_reset(L1CTL_RESET_CONF, reset_req->type); + sched_gsmtime_reset(); + break; + default: + printf("unknown L1CTL_RESET_REQ type\n"); + break; + } +} + +/* Transmit a L1CTL_CCCH_MODE_CONF */ +static void l1ctl_tx_ccch_mode_conf(uint8_t ccch_mode) +{ + struct msgb *msg = l1ctl_msgb_alloc(L1CTL_CCCH_MODE_CONF); + struct l1ctl_ccch_mode_conf *mode_conf; + mode_conf = (struct l1ctl_ccch_mode_conf *) + msgb_put(msg, sizeof(*mode_conf)); + mode_conf->ccch_mode = ccch_mode; + + l1_queue_for_l2(msg); +} + +/* receive a L1CTL_CCCH_MODE_REQ from L23 */ +static void l1ctl_rx_ccch_mode_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_ccch_mode_req *ccch_mode_req = + (struct l1ctl_ccch_mode_req *) l1h->data; + uint8_t ccch_mode = ccch_mode_req->ccch_mode; + + /* pre-set the CCCH mode */ + l1s.serving_cell.ccch_mode = ccch_mode; + + /* Update task */ + mframe_disable(MF_TASK_CCCH_COMB); + mframe_disable(MF_TASK_CCCH); + + if (ccch_mode == CCCH_MODE_COMBINED) + mframe_enable(MF_TASK_CCCH_COMB); + else if (ccch_mode == CCCH_MODE_NON_COMBINED) + mframe_enable(MF_TASK_CCCH); + + l1ctl_tx_ccch_mode_conf(ccch_mode); +} + +/* Transmit a L1CTL_TCH_MODE_CONF */ +static void l1ctl_tx_tch_mode_conf(uint8_t tch_mode) +{ + struct msgb *msg = l1ctl_msgb_alloc(L1CTL_TCH_MODE_CONF); + struct l1ctl_tch_mode_conf *mode_conf; + mode_conf = (struct l1ctl_tch_mode_conf *) + msgb_put(msg, sizeof(*mode_conf)); + mode_conf->tch_mode = tch_mode; + + l1_queue_for_l2(msg); +} + +/* receive a L1CTL_TCH_MODE_REQ from L23 */ +static void l1ctl_rx_tch_mode_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_tch_mode_req *tch_mode_req = + (struct l1ctl_tch_mode_req *) l1h->data; + uint8_t tch_mode = tch_mode_req->tch_mode; + + printd("L1CTL_TCH_MODE_REQ (mode=0x%02x)\n", tch_mode); + tch_mode = l1a_tch_mode_set(tch_mode); + + audio_set_enabled(tch_mode != GSM48_CMODE_SIGN); + + l1s.tch_sync = 1; /* Needed for audio to work */ + + l1ctl_tx_tch_mode_conf(tch_mode); +} + +/* callback from SERCOMM when L2 sends a message to L1 */ +static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + +#if 0 + { + int i; + printf("l1a_l23_rx_cb (%u): ", msg->len); + for (i = 0; i < msg->len; i++) + printf("%02x ", msg->data[i]); + puts("\n"); + } +#endif + + msg->l1h = msg->data; + + if (sizeof(*l1h) > msg->len) { + printf("l1a_l23_cb: Short message. %u\n", msg->len); + goto exit_msgbfree; + } + + switch (l1h->msg_type) { + case L1CTL_FBSB_REQ: + l1ctl_rx_fbsb_req(msg); + break; + case L1CTL_DM_EST_REQ: + l1ctl_rx_dm_est_req(msg); + break; + case L1CTL_DM_REL_REQ: + l1ctl_rx_dm_rel_req(msg); + break; + case L1CTL_PARAM_REQ: + l1ctl_rx_param_req(msg); + break; + case L1CTL_DM_FREQ_REQ: + l1ctl_rx_dm_freq_req(msg); + break; + case L1CTL_CRYPTO_REQ: + l1ctl_rx_crypto_req(msg); + break; + case L1CTL_RACH_REQ: + l1ctl_rx_rach_req(msg); + break; + case L1CTL_DATA_REQ: + l1ctl_rx_data_req(msg); + /* we have to keep the msgb, not free it! */ + goto exit_nofree; + case L1CTL_PM_REQ: + l1ctl_rx_pm_req(msg); + break; + case L1CTL_RESET_REQ: + l1ctl_rx_reset_req(msg); + break; + case L1CTL_CCCH_MODE_REQ: + l1ctl_rx_ccch_mode_req(msg); + break; + case L1CTL_TCH_MODE_REQ: + l1ctl_rx_tch_mode_req(msg); + break; + } + +exit_msgbfree: + msgb_free(msg); +exit_nofree: + return; +} + +void l1a_l23api_init(void) +{ + sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb); +} diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c new file mode 100644 index 00000000..326f6603 --- /dev/null +++ b/src/target/firmware/layer1/mframe_sched.c @@ -0,0 +1,462 @@ +/* GSM Multiframe Scheduler Implementation (on top of TDMA sched) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <debug.h> + +#include <osmocore/gsm_utils.h> + +#include <layer1/prim.h> +#include <layer1/sync.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> + +/* A multiframe operation which can be scheduled for a multiframe */ +struct mframe_sched_item { + /* The TDMA scheduler item that shall be scheduled */ + const struct tdma_sched_item *sched_set; + /* Which modulo shall be used on the frame number */ + uint16_t modulo; + /* At which number inside the modulo shall we be scheduled */ + uint16_t frame_nr; + /* bit-mask of flags */ + uint16_t flags; +}; + +/* FIXME: properly clean this up */ +#define NB_QUAD_DL nb_sched_set +#define NB_QUAD_FH_DL NB_QUAD_DL +#define NB_QUAD_UL nb_sched_set_ul +#define NB_QUAD_FH_UL NB_QUAD_UL + +/* BCCH Normal */ +static const struct mframe_sched_item mf_bcch_norm[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 2 }, + { .sched_set = NULL } +}; + +/* BCCH Extended */ +static const struct mframe_sched_item mf_bcch_ext[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 }, + { .sched_set = NULL } +}; + +/* Full CCCH in a pure BCCH + CCCH C0T0 */ +static const struct mframe_sched_item mf_ccch[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 12 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 16 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 22 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 26 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 32 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 36 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 42 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 46 }, + { .sched_set = NULL } +}; + +/* Full CCCH in a combined CCCH on C0T0 */ +static const struct mframe_sched_item mf_ccch_comb[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 12 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 16 }, + { .sched_set = NULL } +}; + +/* SDCCH/4 in a combined CCCH on C0T0, cannot be FH */ +static const struct mframe_sched_item mf_sdcch4_0[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 22 }, + { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 22+15 }, + { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 42, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 42+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch4_1[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 26 }, + { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 26+15 }, + { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 46, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 46+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch4_2[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 32 }, + { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 32+15 }, + { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 51+42, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 51+42+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch4_3[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 36 }, + { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 36+15 }, + { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 51+46, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 51+46+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; + +/* SDCCH/8, can be frequency hopping (FH) */ +static const struct mframe_sched_item mf_sdcch8_0[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 0 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 0+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 32, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 32+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_1[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 4 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 4+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 36, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 36+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_2[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 8 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 8+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 40, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 40+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_3[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 12 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 12+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 44, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 44+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_4[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 16 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 16+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+32, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+32+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_5[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 20 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 20+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+36, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+36+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_6[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 24 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 24+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+40, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+40+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_7[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 28 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 28+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+44, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+44+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; + +/* TCH */ +#define TCH tch_sched_set +#define TCH_A tch_a_sched_set +#define TCH_D tch_d_sched_set + +static const struct mframe_sched_item mf_tch_f_even[] = { + { .sched_set = TCH, .modulo = 13, .frame_nr = 0 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 1 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 2 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 3 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 4 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 5 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 6 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 7 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 8 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 9 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 10 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 11 }, + { .sched_set = TCH_A, .modulo = 26, .frame_nr = 12, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; + +static const struct mframe_sched_item mf_tch_f_odd[] = { + { .sched_set = TCH, .modulo = 13, .frame_nr = 0 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 1 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 2 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 3 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 4 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 5 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 6 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 7 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 8 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 9 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 10 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 11 }, + { .sched_set = TCH_A, .modulo = 26, .frame_nr = 25, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; + +static const struct mframe_sched_item mf_tch_h_0[] = { + { .sched_set = TCH, .modulo = 13, .frame_nr = 0 }, + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 1 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 2 }, + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 3 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 4 }, + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 5 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 6 }, + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 7 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 8 }, + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 9 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 10 }, + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 11 }, + { .sched_set = TCH_A, .modulo = 26, .frame_nr = 12, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; + +static const struct mframe_sched_item mf_tch_h_1[] = { + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 0 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 1 }, + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 2 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 3 }, + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 4 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 5 }, + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 6 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 7 }, + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 8 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 9 }, + { .sched_set = TCH_D, .modulo = 13, .frame_nr = 10 }, + { .sched_set = TCH, .modulo = 13, .frame_nr = 11 }, + { .sched_set = TCH_A, .modulo = 26, .frame_nr = 25, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; + +/* Test TX */ +static const struct mframe_sched_item mf_tx_all_nb[] = { + { .sched_set = NB_QUAD_FH_UL, .modulo = 4, .frame_nr = 0 }, + { .sched_set = NULL } +}; + +static const struct mframe_sched_item *sched_set_for_task[32] = { + [MF_TASK_BCCH_NORM] = mf_bcch_norm, + [MF_TASK_BCCH_EXT] = mf_bcch_ext, + [MF_TASK_CCCH] = mf_ccch, + [MF_TASK_CCCH_COMB] = mf_ccch_comb, + + [MF_TASK_SDCCH4_0] = mf_sdcch4_0, + [MF_TASK_SDCCH4_1] = mf_sdcch4_1, + [MF_TASK_SDCCH4_2] = mf_sdcch4_2, + [MF_TASK_SDCCH4_3] = mf_sdcch4_3, + + [MF_TASK_SDCCH8_0] = mf_sdcch8_0, + [MF_TASK_SDCCH8_1] = mf_sdcch8_1, + [MF_TASK_SDCCH8_2] = mf_sdcch8_2, + [MF_TASK_SDCCH8_3] = mf_sdcch8_3, + [MF_TASK_SDCCH8_4] = mf_sdcch8_4, + [MF_TASK_SDCCH8_5] = mf_sdcch8_5, + [MF_TASK_SDCCH8_6] = mf_sdcch8_6, + [MF_TASK_SDCCH8_7] = mf_sdcch8_7, + + [MF_TASK_TCH_F_EVEN] = mf_tch_f_even, + [MF_TASK_TCH_F_ODD] = mf_tch_f_odd, + [MF_TASK_TCH_H_0] = mf_tch_h_0, + [MF_TASK_TCH_H_1] = mf_tch_h_1, + + [MF_TASK_UL_ALL_NB] = mf_tx_all_nb, +}; + +/* encodes a channel number according to 08.58 Chapter 9.3.1 */ +uint8_t mframe_task2chan_nr(enum mframe_task mft, uint8_t ts) +{ + uint8_t cbits; + + switch (mft) { + case MF_TASK_BCCH_NORM: + case MF_TASK_BCCH_EXT: + cbits = 0x10; + break; + case MF_TASK_CCCH: + case MF_TASK_CCCH_COMB: + cbits = 0x12; + break; + case MF_TASK_SDCCH4_0: + cbits = 0x04 + 0; + break; + case MF_TASK_SDCCH4_1: + cbits = 0x04 + 1; + break; + case MF_TASK_SDCCH4_2: + cbits = 0x04 + 2; + break; + case MF_TASK_SDCCH4_3: + cbits = 0x04 + 3; + break; + case MF_TASK_SDCCH8_0: + cbits = 0x08 + 0; + break; + case MF_TASK_SDCCH8_1: + cbits = 0x08 + 1; + break; + case MF_TASK_SDCCH8_2: + cbits = 0x08 + 2; + break; + case MF_TASK_SDCCH8_3: + cbits = 0x08 + 3; + break; + case MF_TASK_SDCCH8_4: + cbits = 0x08 + 4; + break; + case MF_TASK_SDCCH8_5: + cbits = 0x08 + 5; + break; + case MF_TASK_SDCCH8_6: + cbits = 0x08 + 6; + break; + case MF_TASK_SDCCH8_7: + cbits = 0x08 + 7; + break; + case MF_TASK_TCH_F_EVEN: + case MF_TASK_TCH_F_ODD: + cbits = 0x01; + break; + case MF_TASK_TCH_H_0: + cbits = 0x02 + 0; + break; + case MF_TASK_TCH_H_1: + cbits = 0x02 + 1; + break; + case MF_TASK_UL_ALL_NB: + /* ERROR: cannot express as channel number */ + cbits = 0; + break; + } + + return (cbits << 3) | (ts & 0x7); +} + +/* how many TDMA frame ticks should we schedule events ahead? */ +#define SCHEDULE_AHEAD 2 + +/* how long do we need to tell the DSP in advance what we want to do? */ +#define SCHEDULE_LATENCY 1 + +/* (test and) schedule one particular sched_item_set by means of the TDMA scheduler */ +static void mframe_schedule_set(enum mframe_task task_id) +{ + const struct mframe_sched_item *set = sched_set_for_task[task_id]; + const struct mframe_sched_item *si; + + for (si = set; si->sched_set != NULL; si++) { + unsigned int trigger = si->frame_nr % si->modulo; + unsigned int current = (l1s.current_time.fn + SCHEDULE_AHEAD) % si->modulo; + if (current == trigger) { + uint32_t fn; + int rv; + + /* Schedule the set */ + /* FIXME: what to do with SACCH Flag etc? */ + rv = tdma_schedule_set(SCHEDULE_AHEAD-SCHEDULE_LATENCY, + si->sched_set, task_id | (si->flags<<8)); + + /* Compute the next safe time to queue a DSP command */ + fn = l1s.current_time.fn; + ADD_MODULO(fn, rv - 2, GSM_MAX_FN); /* -2 = worst case last dsp command */ + if ((fn > l1s.mframe_sched.safe_fn) || + (l1s.mframe_sched.safe_fn >= GSM_MAX_FN)) + l1s.mframe_sched.safe_fn = fn; + } + } +} + +/* Enable a specific task */ +void mframe_enable(enum mframe_task task_id) +{ + l1s.mframe_sched.tasks_tgt |= (1 << task_id); +} + +/* Disable a specific task */ +void mframe_disable(enum mframe_task task_id) +{ + l1s.mframe_sched.tasks_tgt &= ~(1 << task_id); +} + +/* Replace the current active set by the new one */ +void mframe_set(uint32_t tasks) +{ + l1s.mframe_sched.tasks_tgt = tasks; +} + +/* Schedule mframe_sched_items according to current MF TASK list */ +void mframe_schedule(void) +{ + unsigned int i; + int fn_diff; + + /* Try to enable/disable task to meet target bitmap */ + fn_diff = l1s.mframe_sched.safe_fn - l1s.current_time.fn; + if ((fn_diff <= 0) || (fn_diff >= (GSM_MAX_FN>>1)) || + (l1s.mframe_sched.safe_fn >= GSM_MAX_FN)) + /* If nothing is in the way, enable new tasks */ + l1s.mframe_sched.tasks = l1s.mframe_sched.tasks_tgt; + else + /* Else, Disable only */ + l1s.mframe_sched.tasks &= l1s.mframe_sched.tasks_tgt; + + /* Schedule any active pending set */ + for (i = 0; i < 32; i++) { + if (l1s.mframe_sched.tasks & (1 << i)) + mframe_schedule_set(i); + } +} + +/* reset the scheduler, disabling all tasks */ +void mframe_reset(void) +{ + l1s.mframe_sched.tasks = 0; + l1s.mframe_sched.tasks_tgt = 0; + l1s.mframe_sched.safe_fn = -1UL; /* Force safe */ +} + diff --git a/src/target/firmware/layer1/prim_fbsb.c b/src/target/firmware/layer1/prim_fbsb.c new file mode 100644 index 00000000..5177651e --- /dev/null +++ b/src/target/firmware/layer1/prim_fbsb.c @@ -0,0 +1,569 @@ +/* Layer 1 - FCCH and SCH burst handling */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> + +#include <layer1/sync.h> +#include <layer1/afc.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> + +#include <l1ctl_proto.h> + +#define FB0_RETRY_COUNT 3 +#define AFC_RETRY_COUNT 30 + +extern uint16_t rf_arfcn; // TODO + +struct mon_state { + uint32_t fnr_report; /* frame number when DSP reported it */ + int attempt; /* which attempt was this ? */ + + int16_t toa; + uint16_t pm; + uint16_t angle; + uint16_t snr; + + /* computed values */ + int16_t freq_diff; + + /* Sync Burst (SB) */ + uint8_t bsic; + struct gsm_time time; +}; + +struct l1a_fb_state { + struct mon_state mon; + struct l1ctl_fbsb_req req; + int16_t initial_freq_err; + uint8_t fb_retries; + uint8_t afc_retries; +}; + +static struct l1a_fb_state fbs; +static struct mon_state *last_fb = &fbs.mon; + +static void dump_mon_state(struct mon_state *fb) +{ +#if 0 + printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, " + "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle), + fb->snr, l1s_snr_int(fb->snr), l1s_snr_fract(fb->snr), + tpu_get_offset(), tpu_get_synchro()); +#else + printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle)); +#endif +} + +static int l1ctl_fbsb_resp(uint8_t res) +{ + struct msgb *msg; + struct l1ctl_fbsb_conf *resp; + + msg = l1_create_l2_msg(L1CTL_FBSB_CONF, fbs.mon.time.fn, + l1s_snr_int(fbs.mon.snr), + fbs.req.band_arfcn); + if (!msg) + return -ENOMEM; + + resp = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*resp)); + resp->initial_freq_err = htons(fbs.initial_freq_err); + resp->result = res; + resp->bsic = fbs.mon.bsic; + + /* no need to set BSIC, as it is never used here */ + l1_queue_for_l2(msg); + + return 0; +} + +/* SCH Burst Detection ********************************************************/ + +/* determine the GSM time and BSIC from a Sync Burst */ +static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb) +{ + uint8_t bsic = (sb >> 2) & 0x3f; + uint8_t t3p; + + memset(time, 0, sizeof(*time)); + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + time->t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + time->t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); + + time->tc = (time->fn / 51) % 8; + + return bsic; +} + +static void read_sb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + st->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + st->freq_diff = ANGLE_TO_FREQ(st->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + if (st->snr > AFC_SNR_THRESHOLD) + afc_input(st->freq_diff, rf_arfcn, 1); + else + afc_input(st->freq_diff, rf_arfcn, 0); + + dsp_api.r_page_used = 1; +} + +/* Note: When we get the SB response, it is 2 TDMA frames after the SB + * actually happened, as it is a "C W W R" task */ +#define SB2_LATENCY 2 + +static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt, + __unused uint16_t p3) +{ + uint32_t sb; + int qbits, fn_offset; + struct l1_cell_info *cinfo = &l1s.serving_cell; + int fnr_delta, bits_delta; + struct l1ctl_sync_new_ccch_resp *l1; + struct msgb *msg; + + putchart('s'); + + if (dsp_api.db_r->a_sch[0] & (1<<B_SCH_CRC)) { + /* mark READ page as being used */ + dsp_api.r_page_used = 1; + + /* after 2nd attempt, we failed */ + if (attempt == 2) { + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + /* after 1st attempt, we simply wait for 2nd */ + return 0; + } + + printf("SB%d ", attempt); + read_sb_result(last_fb, attempt); + + sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb); + printf("=> SB 0x%08x: BSIC=%u ", sb, fbs.mon.bsic); + l1s_time_dump(&fbs.mon.time); + + l1s.serving_cell.bsic = fbs.mon.bsic; + + /* calculate synchronisation value (TODO: only complete for qbits) */ + last_fb->toa -= 23; + qbits = last_fb->toa * 4; + fn_offset = l1s.current_time.fn; // TODO + + if (qbits > QBITS_PER_TDMA) { + qbits -= QBITS_PER_TDMA; + fn_offset -= 1; + } else if (qbits < 0) { + qbits += QBITS_PER_TDMA; + fn_offset += 1; + } + + fnr_delta = last_fb->fnr_report - attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports SB in bit that is %d bits in the " + "future?!?\n", last_fb->toa - bits_delta); + else + printf(" qbits=%u\n", qbits); + + synchronize_tdma(&l1s.serving_cell); + + /* if we have recived a SYNC burst, update our local GSM time */ + gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* If we call tdma_sched_reset(), which is only needed if there + * are further l1s_sbdet_resp() scheduled, we will bring + * dsp_api.db_r and dsp_api.db_w out of sync because we changed + * dsp_api.db_w for l1s_sbdet_cmd() and canceled + * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP + * however expects dsp_api.db_w and dsp_api.db_r to be in sync + * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w + * and dsp_api.db_r into sync again, otherwise NB reading will + * complain. We probably don't need the Abort command and could + * just bring dsp_api.db_w and dsp_api.db_r into sync. */ + if (attempt != 2) { + tdma_sched_reset(); + l1s_dsp_abort(); + } + + l1s_reset_hw(); + /* enable the MF Task for BCCH reading */ + mframe_enable(MF_TASK_BCCH_NORM); + if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED) + mframe_enable(MF_TASK_CCCH_COMB); + else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED) + mframe_enable(MF_TASK_CCCH); + + l1s_compl_sched(L1_COMPL_FB); + + return 0; +} + +static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + putchart('S'); + + fbs.mon.bsic = 0; + fbs.mon.time.fn = 0; + + dsp_api.db_w->d_task_md = SB_DSP_TASK; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + + /* Program TPU */ + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB, 0); + + return 0; +} + +/* This is how it is done by the TSM30 */ +static const struct tdma_sched_item sb_sched_set[] = { + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_sbdet_cmd, 0, 0, 2), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +void l1s_sb_test(uint8_t base_fn) +{ + tdma_schedule_set(base_fn, sb_sched_set, 0); +} +/* FCCH Burst *****************************************************************/ + +static int read_fb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.ndb->a_sync_demod[D_TOA]; + st->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3; + st->angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + st->snr = dsp_api.ndb->a_sync_demod[D_SNR]; + + //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE); + st->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + + /* Update AFC with current frequency offset */ + afc_correct(st->freq_diff, rf_arfcn); + + //tpu_dsp_frameirq_enable(); + return 1; +} + +static void fbinfo2cellinfo(struct l1_cell_info *cinfo, + const struct mon_state *mon) +{ + int ntdma, qbits, fn_offset, fnr_delta, bits_delta; + + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + fnr_delta = last_fb->fnr_report - last_fb->attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports FB in bit that is %d bits in " + "the future?!?\n", last_fb->toa - bits_delta); + else { + int fb_fnr = (last_fb->fnr_report - last_fb->attempt) + + last_fb->toa/BITS_PER_TDMA; + printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n", + fb_fnr, fn_offset, qbits); + } +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + if (fb_mode == 0) { + putchart('F'); + } else { + putchart('V'); + } + + l1s.fb.mode = fb_mode; + + /* Tell the RF frontend to set the gain appropriately */ + rffe_set_gain(-85, CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.ndb->d_fb_mode = fb_mode; + + /* Program TPU */ + l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB, 0); + + return 0; +} + +#if 0 +#define FB0_SNR_THRESH 2000 +#define FB1_SNR_THRESH 3000 +#else +#define FB0_SNR_THRESH 0 +#define FB1_SNR_THRESH 0 +#endif + +static const struct tdma_sched_item fb_sched_set[]; + +/* scheduler callback to check for a FB detection response */ +static int l1s_fbdet_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + putchart('f'); + + if (!dsp_api.ndb->d_fb_det) { + /* we did not detect a FB */ + + /* attempt < 12, do nothing */ + if (attempt < 12) + return 0; + + /* attempt >= 12, we simply don't find one */ + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + if (fbs.fb_retries < FB0_RETRY_COUNT) { + /* retry once more */ + tdma_schedule_set(1, fb_sched_set, 0); + fbs.fb_retries++; + } else { + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; + } + + /* We found a frequency burst, reset everything */ + l1s_reset_hw(); + + printf("FB%u ", dsp_api.ndb->d_fb_mode); + read_fb_result(last_fb, attempt); + + /* if this is the first success, save freq err */ + if (!fbs.initial_freq_err) + fbs.initial_freq_err = last_fb->freq_diff; + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + /* Immediately schedule further TDMA tasklets, if requested. Doing + * this directly from L1S means we can do this quickly without any + * additional delays */ + if (fb_mode == 0) { + if (fbs.req.flags & L1CTL_FBSB_F_FB1) { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + /* FIXME: don't only use the last but an average */ + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 && + last_fb->snr > FB0_SNR_THRESH) { + /* continue with FB1 task in DSP */ + tdma_schedule_set(1, fb_sched_set, 1); + } else { + if (fbs.afc_retries < AFC_RETRY_COUNT) { + tdma_schedule_set(1, fb_sched_set, 0); + fbs.afc_retries++; + } else { + /* Abort */ + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + } + } else + l1s_compl_sched(L1_COMPL_FB); + } else if (fb_mode == 1) { + if (fbs.req.flags & L1CTL_FBSB_F_SB) { + + int ntdma, qbits; + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + + int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + int delay = fn_offset + 11 - l1s.current_time.fn - 1; + printf(" fn_offset=%d (fn=%u + attempt=%u + ntdma = %d)\m", + fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma); + printf(" delay=%d (fn_offset=%d + 11 - fn=%u - 1\n", delay, + fn_offset, l1s.current_time.fn); + printf(" scheduling next FB/SB detection task with delay %u\n", delay); + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 && + last_fb->snr > FB1_SNR_THRESH) { + /* synchronize before reading SB */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + synchronize_tdma(&l1s.serving_cell); + tdma_schedule_set(delay, sb_sched_set, 0); + } else + tdma_schedule_set(delay, fb_sched_set, 1); + } else + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; +} + +/* FB detection */ +static const struct tdma_sched_item fb_sched_set[] = { + SCHED_ITEM_DT(l1s_fbdet_cmd, 0, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 4), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 5), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 6), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 7), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 8), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 9), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 10), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 11), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, -4, 0, 12), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Asynchronous completion handler for FB detection */ +static void l1a_fb_compl(__unused enum l1_compl c) +{ + struct l1_cell_info *cinfo = &l1s.serving_cell; + + if (last_fb->attempt >= 13) { + /* FB detection failed, signal this via L1CTL */ + return l1ctl_fbsb_resp(255); + } + + /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + + /* send FBSB_CONF success message via L1CTL */ + l1ctl_fbsb_resp(0); +} + +void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req) +{ + /* copy + endian convert request data */ + fbs.req.band_arfcn = ntohs(req->band_arfcn); + fbs.req.timeout = ntohs(req->timeout); + fbs.req.freq_err_thresh1 = ntohs(req->freq_err_thresh1); + fbs.req.freq_err_thresh2 = ntohs(req->freq_err_thresh2); + fbs.req.num_freqerr_avg = req->num_freqerr_avg; + fbs.req.flags = req->flags; + fbs.req.sync_info_idx = req->sync_info_idx; + + /* clear initial frequency error */ + fbs.initial_freq_err = 0; + fbs.fb_retries = 0; + fbs.afc_retries = 0; + + /* Make sure we start at a 'center' AFCDAC output value */ + afc_reset(); + + if (fbs.req.flags & L1CTL_FBSB_F_FB0) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_FB1) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_SB) + tdma_schedule_set(base_fn, sb_sched_set, 0); + +} + +static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void) +{ + l1s.completion[L1_COMPL_FB] = &l1a_fb_compl; +} diff --git a/src/target/firmware/layer1/prim_freq.c b/src/target/firmware/layer1/prim_freq.c new file mode 100644 index 00000000..7878be68 --- /dev/null +++ b/src/target/firmware/layer1/prim_freq.c @@ -0,0 +1,112 @@ +/* Layer 1 Frequency redefinition at "starting time" */ + +/* (C) 2010 by Andreas Eversverg <jolly@eversberg.eu> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> +#include <asm/system.h> + +#include <layer1/sync.h> +#include <layer1/async.h> +#include <layer1/tdma_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> + +#include <l1ctl_proto.h> + +struct { + uint32_t fn; + uint16_t band_arfcn; +} last_rach; + +/* if the "starting time" is reached, use frequencies "after time" */ +static int l1s_freq_cmd(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3) +{ + putchart('F'); + + printf("Reached starting time, altering frequency set\n"); + + l1s.dedicated.tsc = l1s.dedicated.st_tsc; + l1s.dedicated.h = l1s.dedicated.st_h; + if (l1s.dedicated.h) + memcpy(&l1s.dedicated.h1, &l1s.dedicated.st_h1, + sizeof(l1s.dedicated.h1)); + else + memcpy(&l1s.dedicated.h0, &l1s.dedicated.st_h0, + sizeof(l1s.dedicated.h0)); + + return 0; +} + +/* sched set for frequency change */ +const struct tdma_sched_item freq_sched_set[] = { + SCHED_ITEM(l1s_freq_cmd, -3, 1, 0), + SCHED_END_SET() +}; + +/* request a frequency change at the given frame number + * Note: The fn_sched parameter must be in range 0..42431. */ +void l1a_freq_req(uint32_t fn_sched) +{ + int32_t diff; + unsigned long flags; + + /* We must check here, if the time already elapsed. + * This is required, because we may have an undefined delay between + * layer 1 and layer 3. + */ + diff = fn_sched - (l1s.current_time.fn % 42432); + if (diff < 0) + diff += 42432; + /* note: 5 is used to give scheduler some time */ + if (diff == 5 || diff >= 32024) { + l1s_freq_cmd(0, 0, 0); + return; + } + + /* calculate (full range) frame number */ + fn_sched = l1s.current_time.fn + diff; + if (fn_sched >= GSM_MAX_FN) + fn_sched -= GSM_MAX_FN; + printf("Scheduling frequency change at fn=%u, currently fn=%u\n", + fn_sched, l1s.current_time.fn); + + local_firq_save(flags); + sched_gsmtime(freq_sched_set, fn_sched, 0); + local_irq_restore(flags); +} + diff --git a/src/target/firmware/layer1/prim_pm.c b/src/target/firmware/layer1/prim_pm.c new file mode 100644 index 00000000..134bffde --- /dev/null +++ b/src/target/firmware/layer1/prim_pm.c @@ -0,0 +1,153 @@ +/* Layer 1 Power Measurement */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> +#include <asm/system.h> + +#include <layer1/sync.h> +#include <layer1/agc.h> +#include <layer1/tdma_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> +#include <layer1/prim.h> + +#include <l1ctl_proto.h> + +static void l1ddsp_meas_read(uint8_t nbmeas, uint16_t *pm) +{ + uint8_t i; + + for (i = 0; i < nbmeas; i++) + pm[i] = (uint16_t) ((dsp_api.db_r->a_pm[i] & 0xffff) >> 3); + dsp_api.r_page_used = 1; +} + +/* scheduler callback to issue a power measurement task to the DSP */ +static int l1s_pm_cmd(uint8_t num_meas, + __unused uint8_t p2, uint16_t arfcn) +{ + putchart('P'); + + dsp_api.db_w->d_task_md = num_meas; /* number of measurements */ + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + + /* Tell the RF frontend to set the gain appropriately */ + rffe_set_gain(-85, CAL_DSP_TGT_BB_LVL); + + /* Program TPU */ + /* FIXME: RXWIN_PW needs to set up multiple times in case + * num_meas > 1 */ + l1s_rx_win_ctrl(arfcn, L1_RXWIN_PW, 0); + //l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB); + + return 0; +} + +/* scheduler callback to read power measurement resposnse from the DSP */ +static int l1s_pm_resp(uint8_t num_meas, __unused uint8_t p2, + uint16_t arfcn) +{ + struct l1ctl_pm_conf *pmr; + uint16_t pm_level[2]; + + putchart('p'); + + l1ddsp_meas_read(num_meas, pm_level); + + printf("PM MEAS: ARFCN=%u, %-4d dBm at baseband, %-4d dBm at RF\n", + arfcn, pm_level[0]/8, agc_inp_dbm8_by_pm(pm_level[0])/8); + + printd("PM MEAS: %-4d dBm, %-4d dBm ARFCN=%u\n", + agc_inp_dbm8_by_pm(pm_level[0])/8, + agc_inp_dbm8_by_pm(pm_level[1])/8, arfcn); + + if (!l1s.pm.msg) + l1s.pm.msg = l1ctl_msgb_alloc(L1CTL_PM_CONF); + + if (msgb_tailroom(l1s.pm.msg) < sizeof(*pmr)) { + /* flush current msgb */ + l1_queue_for_l2(l1s.pm.msg); + /* allocate a new msgb and initialize header */ + l1s.pm.msg = l1ctl_msgb_alloc(L1CTL_PM_CONF); + } + + pmr = msgb_put(l1s.pm.msg, sizeof(*pmr)); + pmr->band_arfcn = htons(arfcn); + /* FIXME: do this as RxLev rather than DBM8 ? */ + pmr->pm[0] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level[0])/8); + if (num_meas > 1) + pmr->pm[1] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level[1])/8); + else + pmr->pm[1] = 0; + + if (l1s.pm.mode == 1) { + if (l1s.pm.range.arfcn_next <= l1s.pm.range.arfcn_end) { + /* schedule PM for next ARFCN in range */ + l1s_pm_test(1, l1s.pm.range.arfcn_next); + l1s.pm.range.arfcn_next++; + } else { + /* we have finished, flush the msgb to L2 */ + struct l1ctl_hdr *l1h = l1s.pm.msg->l1h; + l1h->flags |= L1CTL_F_DONE; + l1_queue_for_l2(l1s.pm.msg); + l1s.pm.msg = NULL; + } + } + + return 0; +} + +static const struct tdma_sched_item pm_sched_set[] = { + SCHED_ITEM_DT(l1s_pm_cmd, 0, 1, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_pm_resp, -4, 1, 0), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Schedule a power measurement test */ +void l1s_pm_test(uint8_t base_fn, uint16_t arfcn) +{ + unsigned long flags; + + printd("l1s_pm_test(%u, %u)\n", base_fn, arfcn); + + local_firq_save(flags); + tdma_schedule_set(base_fn, pm_sched_set, arfcn); + local_irq_restore(flags); +} diff --git a/src/target/firmware/layer1/prim_rach.c b/src/target/firmware/layer1/prim_rach.c new file mode 100644 index 00000000..b0e7288d --- /dev/null +++ b/src/target/firmware/layer1/prim_rach.c @@ -0,0 +1,159 @@ +/* Layer 1 Random Access Channel Burst */ + +/* (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de> + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> +#include <asm/system.h> + +#include <layer1/sync.h> +#include <layer1/async.h> +#include <layer1/tdma_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> + +#include <l1ctl_proto.h> + +struct { + uint32_t fn; + uint16_t band_arfcn; +} last_rach; + +/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */ +static int l1s_tx_rach_cmd(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3) +{ + int i; + uint16_t *info_ptr; + uint8_t data[2]; + + putchart('T'); + + l1s_tx_apc_helper(l1s.serving_cell.arfcn); + + data[0] = l1s.serving_cell.bsic << 2; + data[1] = l1s.rach.ra; + + info_ptr = &dsp_api.ndb->d_rach; + info_ptr[0] = ((uint16_t)(data[0])) | ((uint16_t)(data[1])<<8); + + dsp_api.db_w->d_task_ra = RACH_DSP_TASK; + + l1s_tx_win_ctrl(l1s.serving_cell.arfcn, L1_TXWIN_AB, 0, 3); + + return 0; +} + +/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */ +static int l1s_tx_rach_resp(__unused uint8_t p1, __unused uint8_t burst_id, + __unused uint16_t p3) +{ + putchart('t'); + + dsp_api.r_page_used = 1; + + /* schedule a confirmation back indicating the GSM time at which + * the RACH burst was transmitted to the BTS */ + last_rach.fn = l1s.current_time.fn - 1; + last_rach.band_arfcn = l1s.serving_cell.arfcn; + l1s_compl_sched(L1_COMPL_RACH); + + return 0; +} + +/* sched sets for uplink */ +const struct tdma_sched_item rach_sched_set_ul[] = { + SCHED_ITEM_DT(l1s_tx_rach_cmd, 3, 1, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_rach_resp, -4, 1, 0), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Asynchronous completion handler for FB detection */ +static void l1a_rach_compl(__unused enum l1_compl c) +{ + struct msgb *msg; + + msg = l1_create_l2_msg(L1CTL_RACH_CONF, last_rach.fn, 0, + last_rach.band_arfcn); + l1_queue_for_l2(msg); +} + +static uint8_t t3_to_rach_comb[51] = { + 0, 0, 0, 0, + 0, 1, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 25, 25, 25, 25, 25, 25, 25, + 25, 26, + 27, 27, 27, 27}; +static uint8_t rach_to_t3_comb[27] = { + 4, 5, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 45, 46}; + +/* request a RACH request at the next multiframe T3 = fn51 */ +void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra) +{ + uint32_t fn_sched; + unsigned long flags; + + offset += 3; + + local_firq_save(flags); + if (combined) { + /* add elapsed RACH slots to offset */ + offset += t3_to_rach_comb[l1s.current_time.t3]; + /* offset is the number of RACH slots in the future */ + fn_sched = l1s.current_time.fn - l1s.current_time.t3; + fn_sched += offset / 27 * 51; + fn_sched += rach_to_t3_comb[offset % 27]; + } else + fn_sched = l1s.current_time.fn + offset; + l1s.rach.ra = ra; + sched_gsmtime(rach_sched_set_ul, fn_sched, 0); + local_irq_restore(flags); + + memset(&last_rach, 0, sizeof(last_rach)); +} + +static __attribute__ ((constructor)) void prim_rach_init(void) +{ + l1s.completion[L1_COMPL_RACH] = &l1a_rach_compl; +} diff --git a/src/target/firmware/layer1/prim_rx_nb.c b/src/target/firmware/layer1/prim_rx_nb.c new file mode 100644 index 00000000..e3e808da --- /dev/null +++ b/src/target/firmware/layer1/prim_rx_nb.c @@ -0,0 +1,211 @@ +/* Layer 1 - Receiving Normal Bursts */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> + +#include <layer1/sync.h> +#include <layer1/afc.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> +#include <layer1/rfch.h> +#include <layer1/prim.h> + +#include <l1ctl_proto.h> + +struct l1s_rxnb_state { + struct l1s_meas_hdr meas[4]; + + struct msgb *msg; + struct l1ctl_info_dl *dl; + struct l1ctl_data_ind *di; +}; + +static struct l1s_rxnb_state rxnb; + +static int l1s_nb_resp(__unused uint8_t p1, uint8_t burst_id, uint16_t p3) +{ + struct gsm_time rx_time; + uint8_t mf_task_id = p3 & 0xff; + uint8_t mf_task_flags = p3 >> 8; + uint16_t rf_arfcn; + uint8_t tsc, tn; + + putchart('n'); + + /* just for debugging, d_task_d should not be 0 */ + if (dsp_api.db_r->d_task_d == 0) { + puts("EMPTY\n"); + return 0; + } + + /* DSP burst ID needs to corespond with what we expect */ + if (dsp_api.db_r->d_burst_d != burst_id) { + printf("BURST ID %u!=%u\n", dsp_api.db_r->d_burst_d, burst_id); + return 0; + } + + /* get radio parameters for _this_ burst */ + gsm_fn2gsmtime(&rx_time, l1s.current_time.fn - 1); + rfch_get_params(&rx_time, &rf_arfcn, &tsc, &tn); + + /* collect measurements */ + rxnb.meas[burst_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA]; + rxnb.meas[burst_id].pm_dbm8 = + agc_inp_dbm8_by_pm(dsp_api.db_r->a_serv_demod[D_PM] >> 3); + rxnb.meas[burst_id].freq_err = + ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]); + rxnb.meas[burst_id].snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + /* feed computed frequency error into AFC loop */ + if (rxnb.meas[burst_id].snr > AFC_SNR_THRESHOLD) + afc_input(rxnb.meas[burst_id].freq_err, rf_arfcn, 1); + else + afc_input(rxnb.meas[burst_id].freq_err, rf_arfcn, 0); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_set_gain(rxnb.meas[burst_id].pm_dbm8/8, CAL_DSP_TGT_BB_LVL); + + /* 4th burst, get frame data */ + if (dsp_api.db_r->d_burst_d == 3) { + uint8_t i; + uint16_t num_biterr; + uint32_t avg_snr = 0; + int32_t avg_dbm8 = 0; + + /* Get radio parameters for the first burst */ + gsm_fn2gsmtime(&rx_time, l1s.current_time.fn - 4); + rfch_get_params(&rx_time, &rf_arfcn, &tsc, &tn); + + /* Set Channel Number depending on MFrame Task ID */ + rxnb.dl->chan_nr = mframe_task2chan_nr(mf_task_id, tn); + + /* Set SACCH indication in Link IDentifier */ + if (mf_task_flags & MF_F_SACCH) + rxnb.dl->link_id = 0x40; + else + rxnb.dl->link_id = 0x00; + + rxnb.dl->band_arfcn = htons(rf_arfcn); + + rxnb.dl->frame_nr = htonl(rx_time.fn); + + /* compute average snr and rx level */ + for (i = 0; i < 4; ++i) { + avg_snr += rxnb.meas[i].snr; + avg_dbm8 += rxnb.meas[i].pm_dbm8; + } + rxnb.dl->snr = avg_snr / 4; + rxnb.dl->rx_level = dbm2rxlev(avg_dbm8 / (8*4)); + + num_biterr = dsp_api.ndb->a_cd[2] & 0xffff; + if (num_biterr > 0xff) + rxnb.dl->num_biterr = 0xff; + else + rxnb.dl->num_biterr = num_biterr; + + rxnb.dl->fire_crc = ((dsp_api.ndb->a_cd[0] & 0xffff) & ((1 << B_FIRE1) | (1 << B_FIRE0))) >> B_FIRE0; + + /* update rx level for pm report */ + pu_update_rx_level(rxnb.dl->rx_level); + + /* copy actual data, skipping the information block [0,1,2] */ + dsp_memcpy_from_api(rxnb.di->data, &dsp_api.ndb->a_cd[3], 23, 0); + + l1_queue_for_l2(rxnb.msg); + rxnb.msg = NULL; rxnb.dl = NULL; rxnb.di = NULL; + + /* clear downlink task */ + dsp_api.db_w->d_task_d = 0; + } + + /* mark READ page as being used */ + dsp_api.r_page_used = 1; + + return 0; +} + +static int l1s_nb_cmd(__unused uint8_t p1, uint8_t burst_id, + __unused uint16_t p3) +{ + uint16_t arfcn; + uint8_t tsc, tn; + + putchart('N'); + + if (burst_id == 1) { + /* allocate message only at 2nd burst in case of + * consecutive/overlapping normal burst RX tasks */ + /* FIXME: we actually want all allocation out of L1S! */ + if (rxnb.msg) { + /* Can happen when resetting ... */ + printf("nb_cmd(0) and rxnb.msg != NULL\n"); + msgb_free(rxnb.msg); + } + /* allocate msgb as needed. FIXME: from L1A ?? */ + rxnb.msg = l1ctl_msgb_alloc(L1CTL_DATA_IND); + if (!rxnb.msg) + printf("nb_cmd(0): unable to allocate msgb\n"); + rxnb.dl = (struct l1ctl_info_dl *) msgb_put(rxnb.msg, sizeof(*rxnb.dl)); + rxnb.di = (struct l1ctl_data_ind *) msgb_put(rxnb.msg, sizeof(*rxnb.di)); + } + + rfch_get_params(&l1s.next_time, &arfcn, &tsc, &tn); + + /* DDL_DSP_TASK, four normal bursts */ + dsp_load_tch_param(&l1s.next_time, + SIG_ONLY_MODE, SDCCH_4, 0, 0, 0, tn); + + dsp_load_rx_task(ALLC_DSP_TASK, burst_id, tsc); + + l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0); + + return 0; +} + +const struct tdma_sched_item nb_sched_set[] = { + SCHED_ITEM_DT(l1s_nb_cmd, 0, 0, 0), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_nb_cmd, 0, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, -4, 0, 0), SCHED_ITEM_DT(l1s_nb_cmd, 0, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, -4, 0, 1), SCHED_ITEM_DT(l1s_nb_cmd, 0, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, -4, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, -4, 0, 3), SCHED_END_FRAME(), + SCHED_END_SET() +}; diff --git a/src/target/firmware/layer1/prim_tch.c b/src/target/firmware/layer1/prim_tch.c new file mode 100644 index 00000000..4c08e3e8 --- /dev/null +++ b/src/target/firmware/layer1/prim_tch.c @@ -0,0 +1,647 @@ +/* Layer 1 - TCH */ + +/* (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de> + * (C) 2010 by Sylvain Munaut <tnt@246tnt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/protocol/gsm_04_08.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> + +#include <rffe.h> +#include <layer1/sync.h> +#include <layer1/afc.h> +#include <layer1/agc.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> +#include <layer1/rfch.h> +#include <layer1/prim.h> + +#include <l1ctl_proto.h> + + +/* This computes various parameters both for the DSP and for + * our logic. Not all are used all the time, but it's easier + * to build all in one place */ +static void tch_get_params(struct gsm_time *time, uint8_t chan_nr, + uint32_t *fn_report, uint8_t *tch_f_hn, + uint8_t *tch_sub, uint8_t *tch_mode) +{ + uint8_t tn = chan_nr & 0x07; + uint8_t cbits = chan_nr >> 3; + + *tch_f_hn = (cbits & 2) ? 0 : 1; + + if (*tch_f_hn) { + *fn_report = (time->fn - (tn * 13) + 104) % 104; + *tch_sub = 0; + } else { + uint8_t chan_sub = cbits & 1; + uint8_t tn_report = (tn & ~1) | chan_sub; + *fn_report = (time->fn - (tn_report * 13) + 104) % 104; + *tch_sub = chan_sub; + } + + if (tch_mode) { + switch (l1s.tch_mode) { + case GSM48_CMODE_SPEECH_V1: + *tch_mode = *tch_f_hn ? TCH_FS_MODE : TCH_HS_MODE; + break; + case GSM48_CMODE_SPEECH_EFR: + *tch_mode = *tch_f_hn ? TCH_EFR_MODE : SIG_ONLY_MODE; + break; + default: + *tch_mode = SIG_ONLY_MODE; + } + } +} + + +/* ------------------------------------------------------------------------- + * Shared completion handler + * ------------------------------------------------------------------------- */ + +/* + * FIXME We really need a better way to handle completion, where we can + * pass arguments and such ... + * + * Right now, we just 'hope' it gets processed before the next one ... + */ + +static uint16_t last_tx_tch_fn; + +static void l1a_tx_tch_compl(__unused enum l1_compl c) +{ + struct msgb *msg; + + msg = l1_create_l2_msg(L1CTL_DATA_CONF, last_tx_tch_fn, 0, 0); + l1_queue_for_l2(msg); +} + +static __attribute__ ((constructor)) void prim_tch_init(void) +{ + l1s.completion[L1_COMPL_TX_TCH] = &l1a_tx_tch_compl; +} + + +/* ------------------------------------------------------------------------- + * TCH: Voice & FACCH + * ------------------------------------------------------------------------- */ + +/* + * Voice and FACCH data are spread in various ways depending on a lot of + * factors. Trying to handle that with the mframe scheduler is just a mess, + * so we schedule it burst by burst and handle the complex logic inside the + * primitive task code itself. + */ + + +#define FACCH_MEAS_HIST 8 /* Up to 8 bursts history */ +struct l1s_rx_tch_state { + struct l1s_meas_hdr meas[FACCH_MEAS_HIST]; +}; + +static struct l1s_rx_tch_state rx_tch; + + +static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) +{ + static uint8_t meas_id = 0; + uint8_t mf_task_id = p3 & 0xff; + struct gsm_time rx_time; + uint8_t chan_nr; + uint16_t arfcn; + uint8_t tsc, tn; + uint8_t tch_f_hn, tch_sub; + uint32_t fn_report; + int facch_rx_now, traffic_rx_now; + + /* Get/compute various parameters */ + gsm_fn2gsmtime(&rx_time, (l1s.current_time.fn - 1 + GSM_MAX_FN) % GSM_MAX_FN); + rfch_get_params(&rx_time, &arfcn, &tsc, &tn); + chan_nr = mframe_task2chan_nr(mf_task_id, tn); + tch_get_params(&rx_time, chan_nr, &fn_report, &tch_f_hn, &tch_sub, NULL); + + meas_id = (meas_id + 1) % FACCH_MEAS_HIST; /* absolute value doesn't matter */ + + /* Collect measurements */ + rx_tch.meas[meas_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA]; + rx_tch.meas[meas_id].pm_dbm8 = + agc_inp_dbm8_by_pm(dsp_api.db_r->a_serv_demod[D_PM] >> 3); + rx_tch.meas[meas_id].freq_err = + ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]); + rx_tch.meas[meas_id].snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + /* feed computed frequency error into AFC loop */ + if (rx_tch.meas[meas_id].snr > AFC_SNR_THRESHOLD) + afc_input(rx_tch.meas[meas_id].freq_err, arfcn, 1); + else + afc_input(rx_tch.meas[meas_id].freq_err, arfcn, 0); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_set_gain(rx_tch.meas[meas_id].pm_dbm8 / 8, CAL_DSP_TGT_BB_LVL); + + /* FACCH Block end ? */ + if (tch_f_hn) { + /* FACCH/F: B0(0...7),B1(4...11),B2(8...11,0...3) (mod 13) */ + facch_rx_now = ((rx_time.fn % 13) % 4) == 3; + } else { + /* FAACH/H: See GSM 05.02 Clause 7 Table 1of9 */ + uint8_t t2_norm = rx_time.t2 - tch_sub; + facch_rx_now = (t2_norm == 15) || + (t2_norm == 23) || + (t2_norm == 6); + } + + if (facch_rx_now && (dsp_api.ndb->a_fd[0] & (1<<B_BLUD))) { + struct msgb *msg; + struct l1ctl_info_dl *dl; + struct l1ctl_data_ind *di; + uint16_t num_biterr; + uint32_t avg_snr = 0; + int32_t avg_dbm8 = 0; + int i, n; + + /* Allocate msgb */ + /* FIXME: we actually want all allocation out of L1S! */ + msg = l1ctl_msgb_alloc(L1CTL_DATA_IND); + if(!msg) { + printf("TCH FACCH: unable to allocate msgb\n"); + goto skip; + } + + dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl)); + di = (struct l1ctl_data_ind *) msgb_put(msg, sizeof(*di)); + + /* Fill DL header (should be about the first burst ... here is the last) */ + dl->chan_nr = chan_nr; + dl->link_id = 0x00; /* FACCH */ + dl->band_arfcn = htons(arfcn); + dl->frame_nr = htonl(rx_time.fn); + + /* Average SNR & RX level */ + n = tch_f_hn ? 8 : 6; + for (i=0; i<n; i++) { + int j = (meas_id + FACCH_MEAS_HIST - i) % FACCH_MEAS_HIST; + avg_snr += rx_tch.meas[j].snr; + avg_dbm8 += rx_tch.meas[j].pm_dbm8; + } + + dl->snr = avg_snr / n; + dl->rx_level = dbm2rxlev(avg_dbm8 / (8*n)); + + /* Errors & CRC status */ + num_biterr = dsp_api.ndb->a_fd[2] & 0xffff; + if (num_biterr > 0xff) + dl->num_biterr = 0xff; + else + dl->num_biterr = num_biterr; + + dl->fire_crc = ((dsp_api.ndb->a_fd[0] & 0xffff) & ((1 << B_FIRE1) | (1 << B_FIRE0))) >> B_FIRE0; + + /* Update rx level for pm report */ + pu_update_rx_level(dl->rx_level); + + /* Copy actual data, skipping the information block [0,1,2] */ + dsp_memcpy_from_api(di->data, &dsp_api.ndb->a_fd[3], 23, 0); + + /* Give message to up layer */ + l1_queue_for_l2(msg); + + skip: + /* Reset A_FD header (needed by DSP) */ + /* B_FIRE1 =1, B_FIRE0 =0 , BLUD =0 */ + dsp_api.ndb->a_fd[0] = (1<<B_FIRE1); + dsp_api.ndb->a_fd[2] = 0xffff; + + /* Reset A_DD_0 header in NDB (needed by DSP) */ + dsp_api.ndb->a_dd_0[0] = 0; + dsp_api.ndb->a_dd_0[2] = 0xffff; + + /* Reset A_DD_1 header in NDB (needed by DSP) */ + dsp_api.ndb->a_dd_1[0] = 0; + dsp_api.ndb->a_dd_1[2] = 0xffff; + } + + /* Traffic now ? */ + if (tch_f_hn) { + /* TCH/F: B0(0...7),B1(4...11),B2(8...11,0...3) (mod 13)*/ + traffic_rx_now = ((rx_time.fn % 13) % 4) == 3; + } else { + /* TCH/H0: B0(0,2,4,6),B1(4,6,8,10),B2(8,10,0,2) (mod 13) */ + /* H1: B0(1,3,5,7),B1(5,7,9,11),B2(9,11,1,3) (mod 13) */ + traffic_rx_now = (((rx_time.fn - tch_sub + 13) % 13) % 4) == 2; + } + + if (traffic_rx_now) { + volatile uint16_t *traffic_buf; + + traffic_buf = tch_sub ? dsp_api.ndb->a_dd_1 : dsp_api.ndb->a_dd_0; + + if (traffic_buf[0] & (1<<B_BLUD)) { + /* Reset traffic buffer header in NDB (needed by DSP) */ + traffic_buf[0] = 0; + traffic_buf[2] = 0xffff; + } + } + + /* mark READ page as being used */ + dsp_api.r_page_used = 1; + + return 0; +} + +static int l1s_tch_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) +{ + uint8_t mf_task_id = p3 & 0xff; + uint8_t chan_nr; + uint16_t arfcn; + uint8_t tsc, tn; + uint8_t tch_f_hn, tch_sub, tch_mode; + uint32_t fn_report; + uint8_t sync = 0; + static int icnt; + int facch_tx_now; + + /* Get/compute various parameters */ + rfch_get_params(&l1s.next_time, &arfcn, &tsc, &tn); + chan_nr = mframe_task2chan_nr(mf_task_id, tn); + tch_get_params(&l1s.next_time, chan_nr, &fn_report, &tch_f_hn, &tch_sub, &tch_mode); + + /* Sync & FACCH delay */ + if (l1s.tch_sync) { + l1s.tch_sync = 0; + sync = 1; + icnt = 0; + } else if (icnt <= 26) + icnt++; + + /* Load FACCH data if we start a new burst */ + /* (the DSP wants the data on the CMD of the burst _preceding_ the + * first burst) */ + if (tch_f_hn) { + /* FACCH/F: B0(0...7),B1(4...11),B2(8...11,0...3) */ + facch_tx_now = ((l1s.next_time.fn % 13) % 4) == 3; + } else { + /* FAACH/H: See GSM 05.02 Clause 7 Table 1of9 */ + uint8_t t2_norm = l1s.next_time.t2 - tch_sub; + facch_tx_now = (t2_norm == 23) || + (t2_norm == 6) || + (t2_norm == 15); + } + + if (facch_tx_now) { + uint16_t *info_ptr = dsp_api.ndb->a_fu; + struct msgb *msg; + const uint8_t *data; + + /* Pull FACCH data (if ready) */ + if (icnt > 26) + msg = msgb_dequeue(&l1s.tx_queue[L1S_CHAN_MAIN]); + else + msg = NULL; + + /* If TX is empty and we're signalling only, use dummy frame */ + if (msg) + data = msg->l3h; + else if (tch_mode == SIG_ONLY_MODE) + data = pu_get_idle_frame(); + else + data = NULL; + + /* Do we really send something ? */ + if (data) { + /* Fill data block header */ + info_ptr[0] = (1 << B_BLUD); /* 1st word: Set B_BLU bit. */ + info_ptr[1] = 0; /* 2nd word: cleared. */ + info_ptr[2] = 0; /* 3nd word: cleared. */ + + /* Copy the actual data after the header */ + dsp_memcpy_to_api(&info_ptr[3], data, 23, 0); + } + + /* Indicate completion (FIXME: early but easier this way for now) */ + if (msg) { + last_tx_tch_fn = l1s.next_time.fn; + l1s_compl_sched(L1_COMPL_TX_TCH); + } + + /* Free msg now that we're done with it */ + if (msg) + msgb_free(msg); + } + + /* Configure DSP for TX/RX */ + l1s_tx_apc_helper(arfcn); + + dsp_load_tch_param( + &l1s.next_time, + tch_mode, tch_f_hn ? TCH_F : TCH_H, tch_sub, + 0, sync, tn + ); + + dsp_load_rx_task(TCHT_DSP_TASK, 0, tsc); /* burst_id unused for TCH */ + l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0); + + dsp_load_tx_task(TCHT_DSP_TASK, 0, tsc); /* burst_id unused for TCH */ + l1s_tx_win_ctrl(arfcn, L1_TXWIN_NB, 0, 3); + + return 0; +} + + +const struct tdma_sched_item tch_sched_set[] = { + SCHED_ITEM_DT(l1s_tch_cmd, 0, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_tch_resp, 0, 0, -4), SCHED_END_FRAME(), + SCHED_END_SET() +}; + + +/* ------------------------------------------------------------------------- + * TCH/H: Dummy + * ------------------------------------------------------------------------- */ + +/* This task is needed to perform some operation in the DSP when there is + * no data to be exchanged */ + +static int l1s_tch_d_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) +{ + /* mark READ page as being used */ + dsp_api.r_page_used = 1; + + return 0; +} + +static int l1s_tch_d_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) +{ + uint8_t mf_task_id = p3 & 0xff; + uint8_t chan_nr; + uint8_t tsc, tn; + uint8_t tch_f_hn, tch_sub, tch_mode; + uint32_t fn_report; + + /* Get/compute various parameters */ + rfch_get_params(&l1s.next_time, NULL, &tsc, &tn); + chan_nr = mframe_task2chan_nr(mf_task_id, tn); + tch_get_params(&l1s.next_time, chan_nr, &fn_report, &tch_f_hn, &tch_sub, &tch_mode); + + /* Configure DSP */ + dsp_load_tch_param( + &l1s.next_time, + tch_mode, tch_f_hn ? TCH_F : TCH_H, tch_sub, + 0, 0, tn + ); + + dsp_load_rx_task(TCHD_DSP_TASK, 0, tsc); /* burst_id unused for TCH */ + dsp_load_tx_task(TCHD_DSP_TASK, 0, tsc); /* burst_id unused for TCH */ + + return 0; +} + +const struct tdma_sched_item tch_d_sched_set[] = { + SCHED_ITEM_DT(l1s_tch_d_cmd, 0, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_tch_d_resp, 0, 0, -4), SCHED_END_FRAME(), + SCHED_END_SET() +}; + + +/* ------------------------------------------------------------------------- + * TCH: SACCH + * ------------------------------------------------------------------------- */ + +/* + * SACCH data are spread over 4 bursts, however they are so far appart that + * we can't use the normal scheduler to schedule all them at once in a single + * set. + * Therefore, the task code itself decides in which burst it is, if it's the + * start/end, and act appropriately. + */ + + +struct l1s_rx_tch_a_state { + struct l1s_meas_hdr meas[4]; + + struct msgb *msg; + struct l1ctl_info_dl *dl; + struct l1ctl_data_ind *di; +}; + +static struct l1s_rx_tch_a_state rx_tch_a; + + +static int l1s_tch_a_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) +{ + uint8_t mf_task_id = p3 & 0xff; + struct gsm_time rx_time; + uint8_t chan_nr; + uint16_t arfcn; + uint8_t tsc, tn; + uint8_t tch_f_hn, tch_sub; + uint32_t fn_report; + uint8_t burst_id; + + /* It may happen we've never gone through cmd(0) yet, skip until then */ + if (!rx_tch_a.msg) + goto skip; + + /* Get/compute various parameters */ + gsm_fn2gsmtime(&rx_time, (l1s.current_time.fn - 1 + GSM_MAX_FN) % GSM_MAX_FN); + rfch_get_params(&rx_time, &arfcn, &tsc, &tn); + chan_nr = mframe_task2chan_nr(mf_task_id, tn); + tch_get_params(&rx_time, chan_nr, &fn_report, &tch_f_hn, &tch_sub, NULL); + burst_id = (fn_report - 12) / 26; + + /* Collect measurements */ + rx_tch_a.meas[burst_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA]; + rx_tch_a.meas[burst_id].pm_dbm8 = + agc_inp_dbm8_by_pm(dsp_api.db_r->a_serv_demod[D_PM] >> 3); + rx_tch_a.meas[burst_id].freq_err = + ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]); + rx_tch_a.meas[burst_id].snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + /* feed computed frequency error into AFC loop */ + if (rx_tch_a.meas[burst_id].snr > AFC_SNR_THRESHOLD) + afc_input(rx_tch_a.meas[burst_id].freq_err, arfcn, 1); + else + afc_input(rx_tch_a.meas[burst_id].freq_err, arfcn, 0); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_set_gain(rx_tch_a.meas[burst_id].pm_dbm8 / 8, CAL_DSP_TGT_BB_LVL); + + /* Last burst, read data & send to the up layer */ + if ((burst_id == 3) && (dsp_api.ndb->a_cd[0] & (1<<B_BLUD))) { + unsigned int i; + uint16_t num_biterr; + uint32_t avg_snr = 0; + int32_t avg_dbm8 = 0; + + /* Average SNR & RX level + error & crc status */ + for (i=0; i<4; i++) { + avg_snr += rx_tch_a.meas[i].snr; + avg_dbm8 += rx_tch_a.meas[i].pm_dbm8; + } + rx_tch_a.dl->snr = avg_snr / 4; + rx_tch_a.dl->rx_level = dbm2rxlev(avg_dbm8 / (8*4)); + + num_biterr = dsp_api.ndb->a_cd[2]; + if (num_biterr > 0xff) + rx_tch_a.dl->num_biterr = 0xff; + else + rx_tch_a.dl->num_biterr = num_biterr; + + rx_tch_a.dl->fire_crc = ((dsp_api.ndb->a_cd[0] & 0xffff) & ((1 << B_FIRE1) | (1 << B_FIRE0))) >> B_FIRE0; + + /* Update rx level for pm report */ + pu_update_rx_level(rx_tch_a.dl->rx_level); + + /* Copy actual data, skipping the information block [0,1,2] */ + dsp_memcpy_from_api(rx_tch_a.di->data, &dsp_api.ndb->a_cd[3], 23, 0); + + /* Give message to up layer */ + l1_queue_for_l2(rx_tch_a.msg); + rx_tch_a.msg = NULL; rx_tch_a.dl = NULL; rx_tch_a.di = NULL; + + /* Reset header */ + dsp_api.ndb->a_cd[0] = (1<<B_FIRE1); + dsp_api.ndb->a_cd[2] = 0xffff; + } + +skip: + /* mark READ page as being used */ + dsp_api.r_page_used = 1; + + return 0; +} + +static int l1s_tch_a_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) +{ + uint8_t mf_task_id = p3 & 0xff; + uint8_t chan_nr; + uint16_t arfcn; + uint8_t tsc, tn; + uint8_t tch_f_hn, tch_sub, tch_mode; + uint32_t fn_report; + uint8_t burst_id; + + /* Get/compute various parameters */ + rfch_get_params(&l1s.next_time, &arfcn, &tsc, &tn); + chan_nr = mframe_task2chan_nr(mf_task_id, tn); + tch_get_params(&l1s.next_time, chan_nr, &fn_report, &tch_f_hn, &tch_sub, &tch_mode); + burst_id = (fn_report - 12) / 26; + + /* Load SACCH data if we start a new burst */ + if (burst_id == 0) { + uint16_t *info_ptr = dsp_api.ndb->a_cu; + struct msgb *msg; + const uint8_t *data; + + /* If the TX queue is empty, send dummy measurement */ + msg = msgb_dequeue(&l1s.tx_queue[L1S_CHAN_SACCH]); + data = msg ? msg->l3h : pu_get_meas_frame(); + + /* Fill data block header */ + info_ptr[0] = (1 << B_BLUD); /* 1st word: Set B_BLU bit. */ + info_ptr[1] = 0; /* 2nd word: cleared. */ + info_ptr[2] = 0; /* 3nd word: cleared. */ + + /* Copy the actual data after the header */ + dsp_memcpy_to_api(&info_ptr[3], data, 23, 0); + + /* Indicate completion (FIXME: early but easier this way for now) */ + if (msg) { + last_tx_tch_fn = l1s.next_time.fn; + l1s_compl_sched(L1_COMPL_TX_TCH); + } + + /* Free msg now that we're done with it */ + if (msg) + msgb_free(msg); + } + + /* Allocate RX burst */ + if (burst_id == 0) { + /* Clear 'dangling' msgb */ + if (rx_tch_a.msg) { + /* Can happen if the task was shutdown in the middle of + * 4 bursts ... */ + msgb_free(rx_tch_a.msg); + } + + /* Allocate burst */ + /* FIXME: we actually want all allocation out of L1S! */ + rx_tch_a.msg = l1ctl_msgb_alloc(L1CTL_DATA_IND); + if (!rx_tch_a.msg) + printf("tch_a_cmd(0): unable to allocate msgb\n"); + + rx_tch_a.dl = (struct l1ctl_info_dl *) msgb_put(rx_tch_a.msg, sizeof(*rx_tch_a.dl)); + rx_tch_a.di = (struct l1ctl_data_ind *) msgb_put(rx_tch_a.msg, sizeof(*rx_tch_a.di)); + + /* Pre-fill DL header with some info about burst(0) */ + rx_tch_a.dl->chan_nr = chan_nr; + rx_tch_a.dl->link_id = 0x40; /* SACCH */ + rx_tch_a.dl->band_arfcn = htons(arfcn); + rx_tch_a.dl->frame_nr = htonl(l1s.next_time.fn); + } + + /* Configure DSP for TX/RX */ + l1s_tx_apc_helper(arfcn); + + dsp_load_tch_param( + &l1s.next_time, + tch_mode, tch_f_hn ? TCH_F : TCH_H, tch_sub, + 0, 0, tn + ); + + dsp_load_rx_task(TCHA_DSP_TASK, 0, tsc); /* burst_id unused for TCHA */ + l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0); + + dsp_load_tx_task(TCHA_DSP_TASK, 0, tsc); /* burst_id unused for TCHA */ + l1s_tx_win_ctrl(arfcn, L1_TXWIN_NB, 0, 3); + + return 0; +} + + +const struct tdma_sched_item tch_a_sched_set[] = { + SCHED_ITEM_DT(l1s_tch_a_cmd, 0, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_tch_a_resp, 0, 0, -4), SCHED_END_FRAME(), + SCHED_END_SET() +}; diff --git a/src/target/firmware/layer1/prim_tx_nb.c b/src/target/firmware/layer1/prim_tx_nb.c new file mode 100644 index 00000000..ba4a59c7 --- /dev/null +++ b/src/target/firmware/layer1/prim_tx_nb.c @@ -0,0 +1,173 @@ +/* Layer 1 - Transmit Normal Burst */ + +/* (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de> + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> + +#include <layer1/sync.h> +#include <layer1/agc.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> +#include <layer1/rfch.h> +#include <layer1/prim.h> + +#include <l1ctl_proto.h> + + +static uint32_t last_txnb_fn; + +/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */ +static int l1s_tx_resp(__unused uint8_t p1, __unused uint8_t burst_id, + __unused uint16_t p3) +{ + putchart('t'); + + dsp_api.r_page_used = 1; + + if (burst_id == 3) { + last_txnb_fn = l1s.current_time.fn - 4; + l1s_compl_sched(L1_COMPL_TX_NB); + } + + return 0; +} + +/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */ +static int l1s_tx_cmd(uint8_t p1, uint8_t burst_id, uint16_t p3) +{ + uint16_t arfcn; + uint8_t tsc, tn; + uint8_t mf_task_id = p3 & 0xff; + uint8_t mf_task_flags = p3 >> 8; + + putchart('T'); + + /* before sending first of the four bursts, copy data to API ram */ + if (burst_id == 0) { + uint16_t *info_ptr = dsp_api.ndb->a_cu; + struct msgb *msg; + const uint8_t *data; + + /* distinguish between DCCH and ACCH */ + if (mf_task_flags & MF_F_SACCH) { + msg = msgb_dequeue(&l1s.tx_queue[L1S_CHAN_SACCH]); + data = msg ? msg->l3h : pu_get_meas_frame(); + } else { + msg = msgb_dequeue(&l1s.tx_queue[L1S_CHAN_MAIN]); + data = msg ? msg->l3h : pu_get_idle_frame(); + } + + /* Fill data block Header */ + info_ptr[0] = (1 << B_BLUD); // 1st word: Set B_BLU bit. + info_ptr[1] = 0; // 2nd word: cleared. + info_ptr[2] = 0; // 3rd word: cleared. + + /* Copy the actual data after the header */ + dsp_memcpy_to_api(&info_ptr[3], data, 23, 0); + + if (msg) + msgb_free(msg); + } + + rfch_get_params(&l1s.next_time, &arfcn, &tsc, &tn); + + l1s_tx_apc_helper(arfcn); + + if (p1 == 0) + /* DUL_DSP_TASK, one normal burst */ + dsp_load_tch_param(&l1s.next_time, + SIG_ONLY_MODE, INVALID_CHANNEL, 0, 0, 0, tn); + + else if (p1 == 2) + /* DUL_DSP_TASK, four normal bursts */ + dsp_load_tch_param(&l1s.next_time, + SIG_ONLY_MODE, SDCCH_4, 0, 0, 0, tn); + + dsp_load_tx_task(DUL_DSP_TASK, burst_id, tsc); + + l1s_tx_win_ctrl(arfcn, L1_TXWIN_NB, 0, 3); + + return 0; +} + +/* Asynchronous completion handler for NB transmit */ +static void l1a_tx_nb_compl(__unused enum l1_compl c) +{ + struct msgb *msg; + + msg = l1_create_l2_msg(L1CTL_DATA_CONF, last_txnb_fn, 0, 0); + l1_queue_for_l2(msg); +} + +void l1s_tx_test(uint8_t base_fn, uint8_t type) +{ + printf("Starting TX %d\n", type); + + if (type == 0) {// one normal burst + tdma_schedule(base_fn, &l1s_tx_cmd, 0, 0, 0, 3); + tdma_schedule(base_fn + 2, &l1s_tx_resp, 0, 0, 0, 3); + } else if (type == 2) { // four normal burst + tdma_schedule(base_fn, &l1s_tx_cmd, 2, 0, 0, 3); + tdma_schedule(base_fn + 1, &l1s_tx_cmd, 2, 1, 0, 3); + tdma_schedule(base_fn + 2, &l1s_tx_resp, 2, 0, 0, 3); + tdma_schedule(base_fn + 2, &l1s_tx_cmd, 2, 2, 0, 3); + tdma_schedule(base_fn + 3, &l1s_tx_resp, 2, 1, 0, 3); + tdma_schedule(base_fn + 3, &l1s_tx_cmd, 2, 3, 0, 3); + tdma_schedule(base_fn + 4, &l1s_tx_resp, 2, 2, 0, 3); + tdma_schedule(base_fn + 5, &l1s_tx_resp, 2, 3, 0, 3); + } +} + +/* sched sets for uplink */ +const struct tdma_sched_item nb_sched_set_ul[] = { + SCHED_ITEM_DT(l1s_tx_cmd, 3, 2, 0), SCHED_END_FRAME(), + SCHED_ITEM_DT(l1s_tx_cmd, 3, 2, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_resp, -4, 2, 0), SCHED_ITEM_DT(l1s_tx_cmd, 3, 2, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_resp, -4, 2, 1), SCHED_ITEM_DT(l1s_tx_cmd, 3, 2, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_resp, -4, 2, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_resp, -4, 2, 3), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +static __attribute__ ((constructor)) void prim_tx_nb_init(void) +{ + l1s.completion[L1_COMPL_TX_NB] = &l1a_tx_nb_compl; +} diff --git a/src/target/firmware/layer1/prim_utils.c b/src/target/firmware/layer1/prim_utils.c new file mode 100644 index 00000000..5d6c71c7 --- /dev/null +++ b/src/target/firmware/layer1/prim_utils.c @@ -0,0 +1,74 @@ +/* Layer 1 Various primitive utilities */ + +/* (C) 2010 by Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +#include <osmocore/msgb.h> +#include <layer1/sync.h> + + +static const uint8_t ubUui[23] = { + /* dummy lapdm header */ + 0x01, 0x03, 0x01, + + /* fill bytes */ + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b +}; + +static uint8_t ubMeas[23] = { + /* L1 SAACH pseudo-header */ + 0x0f, 0x00, + + /* lapdm header */ + 0x01, 0x03, 0x49, + + /* Measurement report */ + 0x06, 0x15, 0x36, 0x36, 0x01, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; + + +const uint8_t *pu_get_idle_frame(void) +{ + return ubUui; +} + +void pu_update_rx_level(uint8_t rx_level) +{ + ubMeas[7] = ubMeas[8] = rx_level; +} + +const uint8_t *pu_get_meas_frame(void) +{ + if (l1s.tx_meas) { + return l1s.tx_meas->l3h; + } else { + /* Update L1 SAACH pseudo-header */ + ubMeas[0] = l1s.tx_power; + ubMeas[1] = l1s.ta; + + return ubMeas; + } +} diff --git a/src/target/firmware/layer1/rfch.c b/src/target/firmware/layer1/rfch.c new file mode 100644 index 00000000..5627b7e4 --- /dev/null +++ b/src/target/firmware/layer1/rfch.c @@ -0,0 +1,152 @@ +/* RF Channel utilities */ + +/* (C) 2010 by Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +#include <osmocore/gsm_utils.h> + +#include <layer1/sync.h> + + +/* + * Hopping sequence generation + * + * The algorithm is explained in GSM 05.02 Section 6.2.3 + * + * if HSN = 0 (cyclic hopping) then: + * MAI, integer (0 .. N-1) : + * MAI = (FN + MAIO) modulo N + * + * else: + * M, integer (0 .. 152) : + * M = T2 + RNTABLE((HSN xor T1R) + T3) + * + * S, integer (0 .. N-1) : + * M' = M modulo (2 ^ NBIN) + * T' = T3 modulo (2 ^ NBIN) + * + * if M' < N then: + * S = M' + * else: + * S = (M'+T') modulo N + * + * MAI, integer (0 .. N-1) : + * MAI = (S + MAIO) modulo N + */ + +static uint8_t rn_table[114] = { + 48, 98, 63, 1, 36, 95, 78, 102, 94, 73, + 0, 64, 25, 81, 76, 59, 124, 23, 104, 100, + 101, 47, 118, 85, 18, 56, 96, 86, 54, 2, + 80, 34, 127, 13, 6, 89, 57, 103, 12, 74, + 55, 111, 75, 38, 109, 71, 112, 29, 11, 88, + 87, 19, 3, 68, 110, 26, 33, 31, 8, 45, + 82, 58, 40, 107, 32, 5, 106, 92, 62, 67, + 77, 108, 122, 37, 60, 66, 121, 42, 51, 126, + 117, 114, 4, 90, 43, 52, 53, 113, 120, 72, + 16, 49, 7, 79, 119, 61, 22, 84, 9, 97, + 91, 15, 21, 24, 46, 39, 93, 105, 65, 70, + 125, 99, 17, 123, +}; + + +static int pow_nbin_mask(int n) +{ + int x; + x = (n ) | + (n >> 1) | + (n >> 2) | + (n >> 3) | + (n >> 4) | + (n >> 5) | + (n >> 6); + return x; +} + +static int16_t rfch_hop_seq_gen(struct gsm_time *t, + uint8_t hsn, uint8_t maio, + uint8_t n, uint16_t *arfcn_tbl) +{ + int mai; + + if (!hsn) { + /* cyclic hopping */ + mai = (t->fn + maio) % n; + } else { + /* pseudo random hopping */ + int m, mp, tp, s, pnm; + + pnm = pow_nbin_mask(n); + + m = t->t2 + rn_table[(hsn ^ (t->t1 & 63)) + t->t3]; + mp = m & pnm; + + if (mp < n) + s = mp; + else { + tp = t->t3 & pnm; + s = (mp + tp) % n; + } + + mai = (s + maio) % n; + } + + return arfcn_tbl ? arfcn_tbl[mai] : mai; +} + + +/* RF Channel parameters */ +void rfch_get_params(struct gsm_time *t, + uint16_t *arfcn_p, uint8_t *tsc_p, uint8_t *tn_p) +{ + if (l1s.dedicated.type == GSM_DCHAN_NONE) { + /* Serving cell only */ + if (arfcn_p) + *arfcn_p = l1s.serving_cell.arfcn; + + if (tsc_p) + *tsc_p = l1s.serving_cell.bsic & 0x7; + + if (tn_p) + *tn_p = 0; + } else { + /* Dedicated channel */ + if (arfcn_p) { + if (l1s.dedicated.h) { + *arfcn_p = rfch_hop_seq_gen(t, + l1s.dedicated.h1.hsn, + l1s.dedicated.h1.maio, + l1s.dedicated.h1.n, + l1s.dedicated.h1.ma); + } else { + *arfcn_p = l1s.dedicated.h0.arfcn; + } + } + + if (tsc_p) + *tsc_p = l1s.dedicated.tsc; + + if (tn_p) + *tn_p = l1s.dedicated.tn; + } +} + diff --git a/src/target/firmware/layer1/sched_gsmtime.c b/src/target/firmware/layer1/sched_gsmtime.c new file mode 100644 index 00000000..6a549e2d --- /dev/null +++ b/src/target/firmware/layer1/sched_gsmtime.c @@ -0,0 +1,119 @@ +/* GSM-Time One-shot Event Scheduler Implementation (on top of TDMA sched) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> + +#include <debug.h> +#include <osmocore/linuxlist.h> + +#include <layer1/tdma_sched.h> +#include <layer1/sched_gsmtime.h> + +static struct sched_gsmtime_event sched_gsmtime_events[16]; +static LLIST_HEAD(active_evts); +static LLIST_HEAD(inactive_evts); + +/* Scheduling of a tdma_sched_item list one-shot at a givnen GSM time */ +int sched_gsmtime(const struct tdma_sched_item *si, uint32_t fn, uint16_t p3) +{ + struct llist_head *lh; + struct sched_gsmtime_event *evt, *cur; + + printd("sched_gsmtime(si=%p, fn=%u)\n", si, fn); + + /* obtain a free/inactive event structure */ + if (llist_empty(&inactive_evts)) + return -EBUSY; + lh = inactive_evts.next; + llist_del(lh); + evt = llist_entry(lh, struct sched_gsmtime_event, list); + + evt->fn = fn; + evt->si = si; + evt->p3 = p3; + + /* do a sorted insert into the list, i.e. insert the new + * event _before_ the first entry that has a higher fn */ + llist_for_each_entry(cur, &active_evts, list) { + if (cur->fn > evt->fn) { + llist_add_tail(lh, &cur->list); + return 0; + } + } + + /* if we reach here, active_evts is empty _OR_ new event + * is after all the other events: append at end of list */ + llist_add_tail(lh, &active_evts); + + return 0; +} + +/* how many TDMA frame ticks should we schedule events ahead? */ +#define SCHEDULE_AHEAD 2 + +/* how long do we need to tell the DSP in advance what we want to do? */ +#define SCHEDULE_LATENCY 1 + +/* execute all GSMTIME one-shot events pending for 'fn' */ +int sched_gsmtime_execute(uint32_t fn) +{ + struct sched_gsmtime_event *evt, *evt2; + int num = 0; + + llist_for_each_entry_safe(evt, evt2, &active_evts, list) { + if (evt->fn == fn + SCHEDULE_AHEAD) { + printd("sched_gsmtime_execute(time=%u): fn=%u si=%p\n", fn, evt->fn, evt->si); + tdma_schedule_set(SCHEDULE_AHEAD-SCHEDULE_LATENCY, + evt->si, evt->p3); + llist_del(&evt->list); + /* put event back in list of inactive (free) events */ + llist_add(&evt->list, &inactive_evts); + num++; + } if (evt->fn > fn + SCHEDULE_AHEAD) { + /* break the loop as our list is ordered */ + break; + } + } + return num; +} + +void sched_gsmtime_init(void) +{ + unsigned int i; + + printd("sched_gsmtime_init()\n"); + + for (i = 0; i < ARRAY_SIZE(sched_gsmtime_events); i++) + llist_add(&sched_gsmtime_events[i].list, &inactive_evts); +} + +void sched_gsmtime_reset(void) +{ + struct sched_gsmtime_event *evt, *evt2; + + llist_for_each_entry_safe(evt, evt2, &active_evts, list) { + llist_del(&evt->list); + /* put event back in list of inactive (free) events */ + llist_add(&evt->list, &inactive_evts); + } +} diff --git a/src/target/firmware/layer1/sync.c b/src/target/firmware/layer1/sync.c new file mode 100644 index 00000000..3397dc03 --- /dev/null +++ b/src/target/firmware/layer1/sync.c @@ -0,0 +1,402 @@ +/* Synchronous part of GSM Layer 1 */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de> + * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <asm/system.h> + +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> + +#include <abb/twl3025.h> + +//#define DEBUG_EVERY_TDMA + +#include <layer1/sync.h> +#include <layer1/afc.h> +#include <layer1/agc.h> +#include <layer1/apc.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> +#include <layer1/sched_gsmtime.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> + +#include <l1ctl_proto.h> + +struct l1s_state l1s; + +void l1s_time_inc(struct gsm_time *time, uint32_t delta_fn) +{ + ADD_MODULO(time->fn, delta_fn, GSM_MAX_FN); + + if (delta_fn == 1) { + ADD_MODULO(time->t2, 1, 26); + ADD_MODULO(time->t3, 1, 51); + + /* if the new frame number is a multiple of 51 */ + if (time->t3 == 0) { + ADD_MODULO(time->tc, 1, 8); + + /* if new FN is multiple of 51 and 26 */ + if (time->t2 == 0) + ADD_MODULO(time->t1, 1, 2048); + } + } else + gsm_fn2gsmtime(time, time->fn); +} + +void l1s_time_dump(const struct gsm_time *time) +{ + printf("fn=%lu(%u/%2u/%2u)", time->fn, time->t1, time->t2, time->t3); +} + +/* clip a signed 16bit value at a certain limit */ +int16_t clip_int16(int16_t angle, int16_t clip_at) +{ + if (angle > clip_at) + angle = clip_at; + else if (angle < -clip_at) + angle = -clip_at; + + return angle; +} + +int16_t l1s_snr_int(uint16_t snr) +{ + return snr >> 10; +} + +uint16_t l1s_snr_fract(uint16_t snr) +{ + uint32_t fract = snr & 0x3ff; + fract = fract * 1000 / (2 << 10); + + return fract & 0xffff; +} + +#define AFC_MAX_ANGLE 328 /* 0.01 radian in fx1.15 */ + +/* synchronize the L1S to a new timebase (typically a new cell */ +void synchronize_tdma(struct l1_cell_info *cinfo) +{ + int32_t fn_offset; + uint32_t tpu_shift = cinfo->time_alignment; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + if (tpu_shift < SWITCH_TIME) + fn_offset++; + +#if 0 /* probably wrong as we already added "offset" and "shift" above */ + /* increment the TPU quarter-bit offset */ + l1s.tpu_offset = (l1s.tpu_offset + tpu_shift) % TPU_RANGE; +#else + l1s.tpu_offset = tpu_shift; +#endif + + puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +#if 0 + /* FIXME: properly end the TPU window at the emd of l1_sync() */ + tpu_end_scenario(); +#endif + + /* Change the current time to reflect the new value */ + l1s_time_inc(&l1s.current_time, fn_offset); + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* The serving cell now no longer has a frame or bit offset */ + cinfo->fn_offset = 0; + cinfo->time_alignment = 0; +} + +void l1s_reset_hw(void) +{ + dsp_api.w_page = 0; + dsp_api.r_page = 0; + dsp_api.r_page_used = 0; + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0; + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0; + dsp_api.ndb->d_dsp_page = 0; + + /* we have to really reset the TPU, otherwise FB detection + * somtimes returns wrong TOA values. */ + tpu_reset(1); + tpu_reset(0); + tpu_rewind(); + tpu_enq_wait(5); /* really needed ? */ + tpu_enq_sync(l1s.tpu_offset); + tpu_end_scenario(); +} + +/* Lost TDMA interrupt detection. This works by starting a hardware timer + * that is clocked by the same master clock source (VCTCXO). We expect + * 1875 timer ticks in the duration of a TDMA frame (5000 qbits / 1250 bits) */ + +/* Timer for detecting lost IRQ */ +#define TIMER_TICKS_PER_TDMA 1875 +#define TIMER_TICK_JITTER 1 + +static int last_timestamp; + +static inline void check_lost_frame(void) +{ + int diff, timestamp = hwtimer_read(1); + + if (last_timestamp < timestamp) + last_timestamp += (4*TIMER_TICKS_PER_TDMA); + + diff = last_timestamp - timestamp; + + /* allow for a bit of jitter */ + if (diff < TIMER_TICKS_PER_TDMA - TIMER_TICK_JITTER || + diff > TIMER_TICKS_PER_TDMA + TIMER_TICK_JITTER) + printf("LOST %d!\n", diff); + + last_timestamp = timestamp; +} + +/* schedule a completion */ +void l1s_compl_sched(enum l1_compl c) +{ + unsigned long flags; + + local_firq_save(flags); + l1s.scheduled_compl |= (1 << c); + local_irq_restore(flags); +} + +/* main routine for synchronous part of layer 1, called by frame interrupt + * generated by TPU once every TDMA frame */ +static void l1_sync(void) +{ + uint16_t sched_flags; + + putchart('+'); + + check_lost_frame(); + + /* Increment Time */ + l1s.current_time = l1s.next_time; + l1s_time_inc(&l1s.next_time, 1); + //l1s_time_dump(&l1s.current_time); putchar(' '); + + dsp_api.frame_ctr++; + dsp_api.r_page_used = 0; + + /* Update pointers */ + if (dsp_api.w_page == 0) + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0; + else + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_1; + + if (dsp_api.r_page == 0) + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0; + else + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_1; + + /* Reset MCU->DSP page */ + dsp_api_memset((uint16_t *) dsp_api.db_w, sizeof(*dsp_api.db_w)); + + /* Update AFC */ + afc_load_dsp(); + + if (dsp_api.ndb->d_error_status) { + printf("DSP Error Status: %u\n", dsp_api.ndb->d_error_status); + dsp_api.ndb->d_error_status = 0; + } + + /* execute the sched_items that have been scheduled for this + * TDMA frame (including setup/cleanup steps) */ + sched_flags = tdma_sched_flag_scan(); + + if (sched_flags & TDMA_IFLG_TPU) + l1s_win_init(); + + tdma_sched_execute(); + + if (dsp_api.r_page_used) { + /* clear and switch the read page */ + dsp_api_memset((uint16_t *) dsp_api.db_r, + sizeof(*dsp_api.db_r)); + + /* TSM30 does it (really needed ?): + * Set crc result as "SB not found". */ + dsp_api.db_r->a_sch[0] = (1<<B_SCH_CRC); /* B_SCH_CRC =1, BLUD =0 */ + + dsp_api.r_page ^= 1; + } + + if (sched_flags & TDMA_IFLG_DSP) + dsp_end_scenario(); + + if (sched_flags & TDMA_IFLG_TPU) + tpu_end_scenario(); + + /* schedule new / upcoming TDMA items */ + mframe_schedule(); + /* schedule new / upcoming one-shot events */ + sched_gsmtime_execute(l1s.current_time.fn); + + tdma_sched_advance(); +} + +/* ABORT command ********************************************************/ + +static int l1s_abort_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + putchart('A'); + + /* similar to l1s_reset_hw() without touching the TPU */ + + dsp_api.w_page = 0; + dsp_api.r_page = 0; + dsp_api.r_page_used = 0; + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0; + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0; + + /* Reset task commands. */ + dsp_api.db_w->d_task_d = NO_DSP_TASK; /* Init. RX task to NO TASK */ + dsp_api.db_w->d_task_u = NO_DSP_TASK; /* Init. TX task to NO TASK */ + dsp_api.db_w->d_task_ra = NO_DSP_TASK; /* Init. RA task to NO TASK */ + dsp_api.db_w->d_task_md = NO_DSP_TASK; /* Init. MONITORING task to NO TASK */ + dsp_api.ndb->d_dsp_page = 0; + + /* Set "b_abort" to TRUE, dsp will reset current and pending tasks */ + dsp_api.db_w->d_ctrl_system |= (1 << B_TASK_ABORT); + return 0; +} + +void l1s_dsp_abort(void) +{ + /* abort right now */ + tdma_schedule(0, &l1s_abort_cmd, 0, 0, 0, 10); +} + +void l1s_tx_apc_helper(uint16_t arfcn) +{ + int16_t auxapc; + enum gsm_band band; + int i; + + /* Get DAC setting */ + band = gsm_arfcn2band(arfcn); + auxapc = apc_tx_pwrlvl2auxapc(band, l1s.tx_power); + + /* Load the ApcOffset into the DSP */ + #define MY_OFFSET 4 + dsp_api.ndb->d_apcoff = ABB_VAL(APCOFF, (1 << 6) | MY_OFFSET) | 1; /* 2x slope for the GTA-02 ramp */ + + /* Load the TX Power into the DSP */ + /* + If the power is too low (below 0 dBm) the ramp is not OK, + especially for GSM-1800. However an MS does not send below + 0dBm anyway. + */ + dsp_api.db_w->d_power_ctl = ABB_VAL(AUXAPC, auxapc); + + /* Update the ramp according to the PCL */ + for (i = 0; i < 16; i++) + dsp_api.ndb->a_ramp[i] = ABB_VAL(APCRAM, twl3025_default_ramp[i]); + + /* The Ramp Table is sent to ABB only once after RF init routine called */ + dsp_api.db_w->d_ctrl_abb |= (1 << B_RAMP) | (1 << B_BULRAMPDEL); +} + +/* Interrupt handler */ +static void frame_irq(__unused enum irq_nr nr) +{ + l1_sync(); +} + +/* reset the layer1 as part of synchronizing to a new cell */ +void l1s_reset(void) +{ + /* Reset state */ + l1s.fb.mode = 0; + l1s.tx_power = 7; /* initial power reset */ + + /* Leave dedicated mode */ + l1s.dedicated.type = GSM_DCHAN_NONE; + + /* reset scheduler and hardware */ + sched_gsmtime_reset(); + mframe_reset(); + tdma_sched_reset(); + l1s_dsp_abort(); + + /* Cipher off */ + dsp_load_ciph_param(0, NULL); +} + +void l1s_init(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(l1s.tx_queue); i++) + INIT_LLIST_HEAD(&l1s.tx_queue[i]); + l1s.tx_meas = NULL; + + sched_gsmtime_init(); + + /* register FRAME interrupt as FIQ so it can interrupt normal IRQs */ + irq_register_handler(IRQ_TPU_FRAME, &frame_irq); + irq_config(IRQ_TPU_FRAME, 1, 1, 0); + irq_enable(IRQ_TPU_FRAME); + + /* configure timer 1 to be auto-reload and have a prescale of 12 (13MHz/12 == qbit clock) */ + hwtimer_enable(1, 1); + hwtimer_load(1, (1875*4)-1); + hwtimer_config(1, 0, 1); + hwtimer_enable(1, 1); +} + diff --git a/src/target/firmware/layer1/tdma_sched.c b/src/target/firmware/layer1/tdma_sched.c new file mode 100644 index 00000000..013d305a --- /dev/null +++ b/src/target/firmware/layer1/tdma_sched.c @@ -0,0 +1,244 @@ +/* TDMA Scheduler Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <defines.h> +#include <debug.h> +#include <osmocore/gsm_utils.h> + +#include <layer1/tdma_sched.h> +#include <layer1/sync.h> + +#include <calypso/dsp.h> + +/* dummy function to mark end of set */ +int tdma_end_set(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + return 0; +} + +static uint8_t wrap_bucket(uint8_t offset) +{ + uint16_t bucket; + + bucket = (l1s.tdma_sched.cur_bucket + offset) + % ARRAY_SIZE(l1s.tdma_sched.bucket); + + return bucket; +} + +/* Schedule an item at 'frame_offset' TDMA frames in the future */ +int tdma_schedule(uint8_t frame_offset, tdma_sched_cb *cb, + uint8_t p1, uint8_t p2, uint16_t p3, int16_t prio) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + uint8_t bucket_nr = wrap_bucket(frame_offset); + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + struct tdma_sched_item *sched_item; + + if (bucket->num_items >= ARRAY_SIZE(bucket->item)) { + puts("tdma_schedule bucket overflow\n"); + return -1; + } + + sched_item = &bucket->item[bucket->num_items++]; + + sched_item->cb = cb; + sched_item->p1 = p1; + sched_item->p2 = p2; + sched_item->p3 = p3; + sched_item->prio = prio; + + return 0; +} + +/* Schedule a set of items starting from 'frame_offset' TDMA frames in the future */ +int tdma_schedule_set(uint8_t frame_offset, const struct tdma_sched_item *item_set, uint16_t p3) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + uint8_t bucket_nr = wrap_bucket(frame_offset); + int i, j; + + for (i = 0, j = 0; 1; i++) { + const struct tdma_sched_item *sched_item = &item_set[i]; + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + + if (sched_item->cb == &tdma_end_set) { + /* end of scheduler set, return */ + break; + } + + if (sched_item->cb == NULL) { + /* advance to next bucket (== TDMA frame) */ + bucket_nr = wrap_bucket(++frame_offset); + j++; + continue; + } + /* check for bucket overflow */ + if (bucket->num_items >= ARRAY_SIZE(bucket->item)) { + puts("tdma_schedule bucket overflow\n"); + return -1; + } + /* copy the item from the set into the current bucket item position */ + memcpy(&bucket->item[bucket->num_items], sched_item, sizeof(*sched_item)); + bucket->item[bucket->num_items].p3 = p3; + bucket->num_items++; + } + + return j; +} + +/* Advance TDMA scheduler to the next bucket */ +void tdma_sched_advance(void) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + uint8_t next_bucket; + + /* advance to the next bucket */ + next_bucket = wrap_bucket(1); + sched->cur_bucket = next_bucket; +} + +/* Scan current frame scheduled items for flags */ +uint16_t tdma_sched_flag_scan(void) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + struct tdma_sched_bucket *bucket; + int i; + uint16_t flags = 0; + + /* determine current bucket */ + bucket = &sched->bucket[sched->cur_bucket]; + + /* iterate over items in this bucket and call callback function */ + for (i=0; i<bucket->num_items; i++) { + struct tdma_sched_item *item = &bucket->item[i]; + flags |= item->flags; + } + + return flags; +} + +/* Sort a bucket entries by priority */ +static void _tdma_sched_bucket_sort(struct tdma_sched_bucket *bucket, int *seq) +{ + int i, j, k; + struct tdma_sched_item *item_i, *item_j; + + /* initial sequence */ + /* we need all the items because some call back may schedule + * new call backs 'on the fly' */ + for (i=0; i<TDMASCHED_NUM_CB; i++) + seq[i] = i; + + /* iterate over items in this bucket and sort them */ + for (i=0; i<bucket->num_items; i++) + { + item_i = &bucket->item[seq[i]]; + + for (j=i+1; j<bucket->num_items; j++) + { + item_j = &bucket->item[seq[j]]; + + if (item_i->prio > item_j->prio) + { + item_i = item_j; + k = seq[i]; + seq[i] = seq[j]; + seq[j] = k; + } + } + } +} + +/* Execute pre-scheduled events for current frame */ +int tdma_sched_execute(void) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + struct tdma_sched_bucket *bucket; + int i, num_events = 0; + int seq[TDMASCHED_NUM_CB]; + + /* determine current bucket */ + bucket = &sched->bucket[sched->cur_bucket]; + + /* get sequence in priority order */ + _tdma_sched_bucket_sort(bucket, seq); + + /* iterate over items in this bucket and call callback function */ + for (i = 0; i < bucket->num_items; i++) { + struct tdma_sched_item *item = &bucket->item[seq[i]]; + int rc; + + num_events++; + + rc = item->cb(item->p1, item->p2, item->p3); + if (rc < 0) { + printf("Error %d during processing of item %u of bucket %u\n", + rc, i, sched->cur_bucket); + return rc; + } + /* if the cb() we just called has scheduled more items for the + * current TDMA, bucket->num_items will have increased and we + * will simply continue to execute them as intended. Priorities + * won't work though ! */ + } + + /* clear/reset the bucket */ + bucket->num_items = 0; + + /* return number of items that we called */ + return num_events; +} + +void tdma_sched_reset(void) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + unsigned int bucket_nr; + + for (bucket_nr = 0; bucket_nr < ARRAY_SIZE(sched->bucket); bucket_nr++) { + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + /* current bucket will be reset by iteration code above! */ + if (bucket_nr != sched->cur_bucket) + bucket->num_items = 0; + } + + /* Don't reset cur_bucket, as it would upset the bucket iteration code + * in tdma_sched_execute() */ +} + +void tdma_sched_dump(void) +{ + unsigned int i; + + printf("\n(%2u)", l1s.tdma_sched.cur_bucket); + for (i = 0; i < ARRAY_SIZE(l1s.tdma_sched.bucket); i++) { + int bucket_nr = wrap_bucket(i); + struct tdma_sched_bucket *bucket = &l1s.tdma_sched.bucket[bucket_nr]; + printf("%u:", bucket->num_items); + } + putchar('\n'); +} diff --git a/src/target/firmware/layer1/tpu_window.c b/src/target/firmware/layer1/tpu_window.c new file mode 100644 index 00000000..09437da5 --- /dev/null +++ b/src/target/firmware/layer1/tpu_window.c @@ -0,0 +1,163 @@ +/* TPU window control routines for Layer 1 */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <debug.h> +#include <defines.h> +#include <stdio.h> + +#include <rffe.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <abb/twl3025.h> +#include <rf/trf6151.h> + +#include <layer1/sync.h> +#include <layer1/tpu_window.h> + +/* all units in GSM quarter-bits (923.1ns) */ +#define L1_TDMA_LENGTH_Q 5000 +#define L1_BURST_LENGTH_Q 625 /* L1_TDMA_LENGTH_Q/8 */ + +#define L1_NB_MARGIN_Q (3 * 4) +#define L1_SB_MARGIN_Q (23 * 4) +#define L1_TAIL_DURATION_Q (3 * 4) + +/* Sample length as required by the Calypso DSP */ +#define L1_NB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_NB_MARGIN_Q - L1_TAIL_DURATION_Q) +#define L1_SB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_SB_MARGIN_Q - L1_TAIL_DURATION_Q) +#define L1_FB_DURATION_Q (11 * L1_TDMA_LENGTH_Q + 2057) /* more than 11 full slots */ +#define L1_FB26_DURATION_Q (L1_TDMA_LENGTH_Q + 798) +#define L1_PW_DURATION_Q 289 + +#define DSP_SETUP_TIME 66 + +static const uint16_t rx_burst_duration[_NUM_L1_RXWIN] = { + [L1_RXWIN_PW] = L1_PW_DURATION_Q, + [L1_RXWIN_FB] = L1_FB_DURATION_Q, + [L1_RXWIN_SB] = L1_SB_DURATION_Q, + [L1_RXWIN_NB] = L1_NB_DURATION_Q, +}; + +#define L1_TX_NB_DURATION_Q 626 +#define L1_TX_AB_DURATION_Q 386 + +static const uint16_t tx_burst_duration[_NUM_L1_TXWIN] = { + [L1_TXWIN_NB] = L1_TX_NB_DURATION_Q, + [L1_TXWIN_AB] = L1_TX_AB_DURATION_Q, +}; + + +static int _win_setup(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3) +{ + uint8_t tn; + + rfch_get_params(&l1s.next_time, NULL, NULL, &tn); + + tpu_enq_at(4740); + tpu_enq_sync((5000 + l1s.tpu_offset + (L1_BURST_LENGTH_Q * tn)) % 5000); + + return 0; +} + +static int _win_cleanup(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3) +{ + uint8_t tn; + + rfch_get_params(&l1s.next_time, NULL, NULL, &tn); + + /* restore offset */ + tpu_enq_offset((5000 + l1s.tpu_offset + (L1_BURST_LENGTH_Q * tn)) % 5000); + + return 0; +} + +void l1s_win_init(void) +{ + tdma_schedule(0, _win_setup, 0, 0, 0, -2); + tdma_schedule(0, _win_cleanup, 0, 0, 0, 9); +} + +void l1s_rx_win_ctrl(uint16_t arfcn, enum l1_rxwin_type wtype, uint8_t tn_ofs) +{ + int16_t start, stop; + + /* TN offset & TA adjust */ + start = DSP_SETUP_TIME; + start += L1_BURST_LENGTH_Q * tn_ofs; + + stop = start + rx_burst_duration[wtype] - 1; + + /* window open for TRF6151 */ + /* FIXME: why do we need the magic value 100 ? */ + rffe_mode(gsm_arfcn2band(arfcn), 0); + trf6151_rx_window(start - 100, arfcn); + + /* Window open for ABB */ + twl3025_downlink(1, start); + + /* Delay 11 full TDMA frames */ + if (wtype == L1_RXWIN_FB) { + uint8_t i; + for (i = 0; i < 11; i++) + tpu_enq_at(0); + + stop -= 11 * L1_TDMA_LENGTH_Q; + } + + /* Window close for ABB */ + twl3025_downlink(0, stop); + + /* window close for TRF6151 */ + trf6151_set_mode(TRF6151_IDLE); +} + +void l1s_tx_win_ctrl(uint16_t arfcn, enum l1_txwin_type wtype, uint8_t pwr, uint8_t tn_ofs) +{ + uint16_t offset; + + /* TN offset & TA adjust */ + offset = 28; /* ("+ 32" gives a TA of 1) */ + offset += L1_BURST_LENGTH_Q * tn_ofs; + offset -= l1s.ta << 2; + +#ifdef CONFIG_TX_ENABLE + /* window open for TRF6151 and RFFE */ + rffe_mode(gsm_arfcn2band(arfcn), 1); + trf6151_tx_window(offset, arfcn); +#endif + + /* Window open for ABB */ + twl3025_uplink(1, offset); + + /* Window close for ABB */ + twl3025_uplink(0, tx_burst_duration[wtype] + offset + 2); // TODO: "+ 2" + + /* window close for TRF6151 and RFFE */ + trf6151_set_mode(TRF6151_IDLE); +} + +void tpu_end_scenario(void) +{ + tpu_enq_sleep(); + tpu_enable(1); +} diff --git a/src/target/firmware/lib/Makefile b/src/target/firmware/lib/Makefile new file mode 100644 index 00000000..987857c9 --- /dev/null +++ b/src/target/firmware/lib/Makefile @@ -0,0 +1,7 @@ + +LIBRARIES+=mini +mini_DIR=lib +mini_SRCS=vsprintf.c string.c ctype.c printf.c console.c ctors.c \ + changebit.S clearbit.S div64.S lib1funcs.S memcpy.S memset.S setbit.S testchangebit.S testclearbit.S testsetbit.S + + diff --git a/src/target/firmware/lib/bitops.h b/src/target/firmware/lib/bitops.h new file mode 100644 index 00000000..428c9a63 --- /dev/null +++ b/src/target/firmware/lib/bitops.h @@ -0,0 +1,33 @@ + .macro bitop, instr + and r2, r0, #7 + mov r3, #1 + mov r3, r3, lsl r2 + save_and_disable_irqs ip + ldrb r2, [r1, r0, lsr #3] + \instr r2, r2, r3 + strb r2, [r1, r0, lsr #3] + restore_irqs ip + mov pc, lr + .endm + +/** + * testop - implement a test_and_xxx_bit operation. + * @instr: operational instruction + * @store: store instruction + * + * Note: we can trivially conditionalise the store instruction + * to avoid dirting the data cache. + */ + .macro testop, instr, store + add r1, r1, r0, lsr #3 + and r3, r0, #7 + mov r0, #1 + save_and_disable_irqs ip + ldrb r2, [r1] + tst r2, r0, lsl r3 + \instr r2, r2, r0, lsl r3 + \store r2, [r1] + restore_irqs ip + moveq r0, #0 + mov pc, lr + .endm diff --git a/src/target/firmware/lib/changebit.S b/src/target/firmware/lib/changebit.S new file mode 100644 index 00000000..7c709fb3 --- /dev/null +++ b/src/target/firmware/lib/changebit.S @@ -0,0 +1,21 @@ +/* + * linux/arch/arm/lib/changebit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +/* Purpose : Function to change a bit + * Prototype: int change_bit(int bit, void *addr) + */ +ENTRY(_change_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_change_bit_le) + bitop eor diff --git a/src/target/firmware/lib/clearbit.S b/src/target/firmware/lib/clearbit.S new file mode 100644 index 00000000..cb48f7ac --- /dev/null +++ b/src/target/firmware/lib/clearbit.S @@ -0,0 +1,22 @@ +/* + * linux/arch/arm/lib/clearbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +/* + * Purpose : Function to clear a bit + * Prototype: int clear_bit(int bit, void *addr) + */ +ENTRY(_clear_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_clear_bit_le) + bitop bic diff --git a/src/target/firmware/lib/console.c b/src/target/firmware/lib/console.c new file mode 100644 index 00000000..2ec028a0 --- /dev/null +++ b/src/target/firmware/lib/console.c @@ -0,0 +1,190 @@ +/* Ringbuffer based serial console layer, imported from OpenPCD */ + +/* (C) 2006-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <string.h> +#include <console.h> +#include <calypso/uart.h> + +#include <asm/system.h> + +struct cons { + char buf[CONS_RB_SIZE]; + char *next_inbyte; + char *next_outbyte; + int initialized; +}; +static struct cons cons; + +void cons_init(void) +{ + memset(cons.buf, 0, sizeof(cons.buf)); + cons.next_inbyte = &cons.buf[0]; + cons.next_outbyte = &cons.buf[0]; + cons.initialized = 1; +} + +/* determine how many bytes are left in the ringbuffer without overwriting + bytes that haven't been written to the console yet */ +static int __cons_rb_space(void) +{ + if (cons.next_inbyte == cons.next_outbyte) + return sizeof(cons.buf)-1; + else if (cons.next_outbyte > cons.next_inbyte) + return (cons.next_outbyte - cons.next_inbyte) -1; + else + return sizeof(cons.buf) - 1 - (cons.next_inbyte - cons.next_outbyte); +} + +/* pull one char out of debug ring buffer */ +static int cons_rb_pull(char *ret) +{ + unsigned long flags; + + local_irq_save(flags); + + if (cons.next_outbyte == cons.next_inbyte) { + local_irq_restore(flags); + return -1; + } + + *ret = *cons.next_outbyte; + + cons.next_outbyte++; + if (cons.next_outbyte >= &cons.buf[0]+sizeof(cons.buf)) { + cons.next_outbyte = &cons.buf[0]; + } +#if 0 + else if (cons.next_outbyte > &cons.buf[0]+sizeof(cons.buf)) { + cons.next_outbyte -= sizeof(cons.buf); + } +#endif + + local_irq_restore(flags); + + return 0; +} + +/* returns if everything was flushed (1) or if there's more to flush (0) */ +static void __rb_flush_wait(void) +{ + char ch; + while (cons_rb_pull(&ch) >= 0) + uart_putchar_wait(CONS_UART_NR, ch); +} + +/* returns if everything was flushed (1) or if there's more to flush (0) */ +static int __rb_flush(void) +{ + while (!uart_tx_busy(CONS_UART_NR)) { + char ch; + if (cons_rb_pull(&ch) < 0) { + /* no more data to write, disable interest in Tx FIFO interrupts */ + return 1; + } + uart_putchar_nb(CONS_UART_NR, ch); + } + + /* if we reach here, UART Tx FIFO is busy again */ + return 0; +} + +/* flush pending data from debug ring buffer to serial port */ +int cons_rb_flush(void) +{ + return __rb_flush(); +} + +/* Append bytes to ring buffer, not more than we have left! */ +static void __cons_rb_append(const char *data, int len) +{ + if (cons.next_inbyte + len >= &cons.buf[0]+sizeof(cons.buf)) { + int before_tail = (&cons.buf[0]+sizeof(cons.buf)) - cons.next_inbyte; + /* copy the first part before we wrap */ + memcpy(cons.next_inbyte, data, before_tail); + data += before_tail; + len -= before_tail; + /* reset the buffer */ + cons.next_inbyte = &cons.buf[0]; + } + memcpy(cons.next_inbyte, data, len); + cons.next_inbyte += len; +} + +/* append bytes to the ringbuffer, do one wrap */ +int cons_rb_append(const char *data, int len) +{ + unsigned long flags; + int bytes_left; + const char *data_cur; + + /* we will never be able to write more than the console buffer */ + if (len > (int) sizeof(cons.buf)) + len = sizeof(cons.buf); + + local_irq_save(flags); + + bytes_left = __cons_rb_space(); + data_cur = data; + + if (len > bytes_left) { + /* append what we can */ + __cons_rb_append(data_cur, bytes_left); + /* busy-wait for all characters to be transmitted */ + __rb_flush_wait(); + /* fill it with the remaining bytes */ + len -= bytes_left; + data_cur += bytes_left; + } + __cons_rb_append(data_cur, len); + + /* we want to get Tx FIFO interrupts */ + uart_irq_enable(CONS_UART_NR, UART_IRQ_TX_EMPTY, 1); + + local_irq_restore(flags); + + return len; +} + +int cons_puts(const char *s) +{ + if (cons.initialized) { + return cons_rb_append(s, strlen(s)); + } else { + /* if the console is not active yet, we need to fall back */ + int i = strlen(s); + while (i--) + uart_putchar_wait(CONS_UART_NR, *s++); + return i; + } +} + +int cons_putchar(char c) +{ + if (cons.initialized) + return cons_rb_append(&c, 1); + else { + /* if the console is not active yet, we need to fall back */ + uart_putchar_wait(CONS_UART_NR, c); + return 0; + } +} diff --git a/src/target/firmware/lib/copy_template.S b/src/target/firmware/lib/copy_template.S new file mode 100644 index 00000000..cab355c0 --- /dev/null +++ b/src/target/firmware/lib/copy_template.S @@ -0,0 +1,255 @@ +/* + * linux/arch/arm/lib/copy_template.s + * + * Code template for optimized memory copy functions + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This can be used to enable code to cacheline align the source pointer. + * Experiments on tested architectures (StrongARM and XScale) didn't show + * this a worthwhile thing to do. That might be different in the future. + */ +//#define CALGN(code...) code +#define CALGN(code...) + +/* + * Theory of operation + * ------------------- + * + * This file provides the core code for a forward memory copy used in + * the implementation of memcopy(), copy_to_user() and copy_from_user(). + * + * The including file must define the following accessor macros + * according to the need of the given function: + * + * ldr1w ptr reg abort + * + * This loads one word from 'ptr', stores it in 'reg' and increments + * 'ptr' to the next word. The 'abort' argument is used for fixup tables. + * + * ldr4w ptr reg1 reg2 reg3 reg4 abort + * ldr8w ptr, reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * + * This loads four or eight words starting from 'ptr', stores them + * in provided registers and increments 'ptr' past those words. + * The'abort' argument is used for fixup tables. + * + * ldr1b ptr reg cond abort + * + * Similar to ldr1w, but it loads a byte and increments 'ptr' one byte. + * It also must apply the condition code if provided, otherwise the + * "al" condition is assumed by default. + * + * str1w ptr reg abort + * str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * str1b ptr reg cond abort + * + * Same as their ldr* counterparts, but data is stored to 'ptr' location + * rather than being loaded. + * + * enter reg1 reg2 + * + * Preserve the provided registers on the stack plus any additional + * data as needed by the implementation including this code. Called + * upon code entry. + * + * exit reg1 reg2 + * + * Restore registers with the values previously saved with the + * 'preserv' macro. Called upon code termination. + */ + + + enter r4, lr + + subs r2, r2, #4 + blt 8f + ands ip, r0, #3 + PLD( pld [r1, #0] ) + bne 9f + ands ip, r1, #3 + bne 10f + +1: subs r2, r2, #(28) + stmfd sp!, {r5 - r8} + blt 5f + + CALGN( ands ip, r1, #31 ) + CALGN( rsb r3, ip, #32 ) + CALGN( sbcnes r4, r3, r2 ) @ C is always set here + CALGN( bcs 2f ) + CALGN( adr r4, 6f ) + CALGN( subs r2, r2, r3 ) @ C gets set + CALGN( add pc, r4, ip ) + + PLD( pld [r1, #0] ) +2: PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 4f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +3: PLD( pld [r1, #124] ) +4: ldr8w r1, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f + subs r2, r2, #32 + str8w r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f + bge 3b + PLD( cmn r2, #96 ) + PLD( bge 4b ) + +5: ands ip, r2, #28 + rsb ip, ip, #32 + addne pc, pc, ip @ C is always clear here + b 7f +6: nop + ldr1w r1, r3, abort=20f + ldr1w r1, r4, abort=20f + ldr1w r1, r5, abort=20f + ldr1w r1, r6, abort=20f + ldr1w r1, r7, abort=20f + ldr1w r1, r8, abort=20f + ldr1w r1, lr, abort=20f + + add pc, pc, ip + nop + nop + str1w r0, r3, abort=20f + str1w r0, r4, abort=20f + str1w r0, r5, abort=20f + str1w r0, r6, abort=20f + str1w r0, r7, abort=20f + str1w r0, r8, abort=20f + str1w r0, lr, abort=20f + + CALGN( bcs 2b ) + +7: ldmfd sp!, {r5 - r8} + +8: movs r2, r2, lsl #31 + ldr1b r1, r3, ne, abort=21f + ldr1b r1, r4, cs, abort=21f + ldr1b r1, ip, cs, abort=21f + str1b r0, r3, ne, abort=21f + str1b r0, r4, cs, abort=21f + str1b r0, ip, cs, abort=21f + + exit r4, pc + +9: rsb ip, ip, #4 + cmp ip, #2 + ldr1b r1, r3, gt, abort=21f + ldr1b r1, r4, ge, abort=21f + ldr1b r1, lr, abort=21f + str1b r0, r3, gt, abort=21f + str1b r0, r4, ge, abort=21f + subs r2, r2, ip + str1b r0, lr, abort=21f + blt 8b + ands ip, r1, #3 + beq 1b + +10: bic r1, r1, #3 + cmp ip, #2 + ldr1w r1, lr, abort=21f + beq 17f + bgt 18f + + + .macro forward_copy_shift pull push + + subs r2, r2, #28 + blt 14f + + CALGN( ands ip, r1, #31 ) + CALGN( rsb ip, ip, #32 ) + CALGN( sbcnes r4, ip, r2 ) @ C is always set here + CALGN( subcc r2, r2, ip ) + CALGN( bcc 15f ) + +11: stmfd sp!, {r5 - r9} + + PLD( pld [r1, #0] ) + PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 13f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +12: PLD( pld [r1, #124] ) +13: ldr4w r1, r4, r5, r6, r7, abort=19f + mov r3, lr, pull #\pull + subs r2, r2, #32 + ldr4w r1, r8, r9, ip, lr, abort=19f + orr r3, r3, r4, push #\push + mov r4, r4, pull #\pull + orr r4, r4, r5, push #\push + mov r5, r5, pull #\pull + orr r5, r5, r6, push #\push + mov r6, r6, pull #\pull + orr r6, r6, r7, push #\push + mov r7, r7, pull #\pull + orr r7, r7, r8, push #\push + mov r8, r8, pull #\pull + orr r8, r8, r9, push #\push + mov r9, r9, pull #\pull + orr r9, r9, ip, push #\push + mov ip, ip, pull #\pull + orr ip, ip, lr, push #\push + str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f + bge 12b + PLD( cmn r2, #96 ) + PLD( bge 13b ) + + ldmfd sp!, {r5 - r9} + +14: ands ip, r2, #28 + beq 16f + +15: mov r3, lr, pull #\pull + ldr1w r1, lr, abort=21f + subs ip, ip, #4 + orr r3, r3, lr, push #\push + str1w r0, r3, abort=21f + bgt 15b + CALGN( cmp r2, #0 ) + CALGN( bge 11b ) + +16: sub r1, r1, #(\push / 8) + b 8b + + .endm + + + forward_copy_shift pull=8 push=24 + +17: forward_copy_shift pull=16 push=16 + +18: forward_copy_shift pull=24 push=8 + + +/* + * Abort preamble and completion macros. + * If a fixup handler is required then those macros must surround it. + * It is assumed that the fixup code will handle the private part of + * the exit macro. + */ + + .macro copy_abort_preamble +19: ldmfd sp!, {r5 - r9} + b 21f +20: ldmfd sp!, {r5 - r8} +21: + .endm + + .macro copy_abort_end + ldmfd sp!, {r4, pc} + .endm + diff --git a/src/target/firmware/lib/ctors.c b/src/target/firmware/lib/ctors.c new file mode 100644 index 00000000..982169df --- /dev/null +++ b/src/target/firmware/lib/ctors.c @@ -0,0 +1,15 @@ + +/* iterate over list of constructor functions and call each element */ +void do_global_ctors(const char *_ctors_start, const char *ctors_end) +{ + typedef void (*func_ptr)(void); + func_ptr *func, *ctors_start = (func_ptr *) _ctors_start; + + /* skip the first entry, as it contains the number of + * constructors which we don't use */ + ctors_start++; + + for (func = ctors_start; + *func && (func != (func_ptr *) ctors_end); func++) + (*func)(); +} diff --git a/src/target/firmware/lib/ctype.c b/src/target/firmware/lib/ctype.c new file mode 100644 index 00000000..f3732140 --- /dev/null +++ b/src/target/firmware/lib/ctype.c @@ -0,0 +1,34 @@ +/* + * linux/lib/ctype.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include <ctype.h> + +unsigned char _ctype[] = { +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ +_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ +_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ +_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ +_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ +_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ + diff --git a/src/target/firmware/lib/div64.S b/src/target/firmware/lib/div64.S new file mode 100644 index 00000000..7eeef50c --- /dev/null +++ b/src/target/firmware/lib/div64.S @@ -0,0 +1,200 @@ +/* + * linux/arch/arm/lib/div64.S + * + * Optimized computation of 64-bit dividend / 32-bit divisor + * + * Author: Nicolas Pitre + * Created: Oct 5, 2003 + * Copyright: Monta Vista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <asm/linkage.h> + +#ifdef __ARMEB__ +#define xh r0 +#define xl r1 +#define yh r2 +#define yl r3 +#else +#define xl r0 +#define xh r1 +#define yl r2 +#define yh r3 +#endif + +/* + * __do_div64: perform a division with 64-bit dividend and 32-bit divisor. + * + * Note: Calling convention is totally non standard for optimal code. + * This is meant to be used by do_div() from include/asm/div64.h only. + * + * Input parameters: + * xh-xl = dividend (clobbered) + * r4 = divisor (preserved) + * + * Output values: + * yh-yl = result + * xh = remainder + * + * Clobbered regs: xl, ip + */ + +ENTRY(__do_div64) + + @ Test for easy paths first. + subs ip, r4, #1 + bls 9f @ divisor is 0 or 1 + tst ip, r4 + beq 8f @ divisor is power of 2 + + @ See if we need to handle upper 32-bit result. + cmp xh, r4 + mov yh, #0 + blo 3f + + @ Align divisor with upper part of dividend. + @ The aligned divisor is stored in yl preserving the original. + @ The bit position is stored in ip. + +#if __LINUX_ARM_ARCH__ >= 5 + + clz yl, r4 + clz ip, xh + sub yl, yl, ip + mov ip, #1 + mov ip, ip, lsl yl + mov yl, r4, lsl yl + +#else + + mov yl, r4 + mov ip, #1 +1: cmp yl, #0x80000000 + cmpcc yl, xh + movcc yl, yl, lsl #1 + movcc ip, ip, lsl #1 + bcc 1b + +#endif + + @ The division loop for needed upper bit positions. + @ Break out early if dividend reaches 0. +2: cmp xh, yl + orrcs yh, yh, ip + subcss xh, xh, yl + movnes ip, ip, lsr #1 + mov yl, yl, lsr #1 + bne 2b + + @ See if we need to handle lower 32-bit result. +3: cmp xh, #0 + mov yl, #0 + cmpeq xl, r4 + movlo xh, xl + movlo pc, lr + + @ The division loop for lower bit positions. + @ Here we shift remainer bits leftwards rather than moving the + @ divisor for comparisons, considering the carry-out bit as well. + mov ip, #0x80000000 +4: movs xl, xl, lsl #1 + adcs xh, xh, xh + beq 6f + cmpcc xh, r4 +5: orrcs yl, yl, ip + subcs xh, xh, r4 + movs ip, ip, lsr #1 + bne 4b + mov pc, lr + + @ The top part of remainder became zero. If carry is set + @ (the 33th bit) this is a false positive so resume the loop. + @ Otherwise, if lower part is also null then we are done. +6: bcs 5b + cmp xl, #0 + moveq pc, lr + + @ We still have remainer bits in the low part. Bring them up. + +#if __LINUX_ARM_ARCH__ >= 5 + + clz xh, xl @ we know xh is zero here so... + add xh, xh, #1 + mov xl, xl, lsl xh + mov ip, ip, lsr xh + +#else + +7: movs xl, xl, lsl #1 + mov ip, ip, lsr #1 + bcc 7b + +#endif + + @ Current remainder is now 1. It is worthless to compare with + @ divisor at this point since divisor can not be smaller than 3 here. + @ If possible, branch for another shift in the division loop. + @ If no bit position left then we are done. + movs ip, ip, lsr #1 + mov xh, #1 + bne 4b + mov pc, lr + +8: @ Division by a power of 2: determine what that divisor order is + @ then simply shift values around + +#if __LINUX_ARM_ARCH__ >= 5 + + clz ip, r4 + rsb ip, ip, #31 + +#else + + mov yl, r4 + cmp r4, #(1 << 16) + mov ip, #0 + movhs yl, yl, lsr #16 + movhs ip, #16 + + cmp yl, #(1 << 8) + movhs yl, yl, lsr #8 + addhs ip, ip, #8 + + cmp yl, #(1 << 4) + movhs yl, yl, lsr #4 + addhs ip, ip, #4 + + cmp yl, #(1 << 2) + addhi ip, ip, #3 + addls ip, ip, yl, lsr #1 + +#endif + + mov yh, xh, lsr ip + mov yl, xl, lsr ip + rsb ip, ip, #32 + orr yl, yl, xh, lsl ip + mov xh, xl, lsl ip + mov xh, xh, lsr ip + mov pc, lr + + @ eq -> division by 1: obvious enough... +9: moveq yl, xl + moveq yh, xh + moveq xh, #0 + moveq pc, lr + + @ Division by 0: + str lr, [sp, #-8]! + bl __div0 + + @ as wrong as it could be... + mov yl, #0 + mov yh, #0 + mov xh, #0 + ldr pc, [sp], #8 + diff --git a/src/target/firmware/lib/lib1funcs.S b/src/target/firmware/lib/lib1funcs.S new file mode 100644 index 00000000..b02a85eb --- /dev/null +++ b/src/target/firmware/lib/lib1funcs.S @@ -0,0 +1,334 @@ +/* + * linux/arch/arm/lib/lib1funcs.S: Optimized ARM division routines + * + * Author: Nicolas Pitre <nico@cam.org> + * - contributed to gcc-3.4 on Sep 30, 2003 + * - adapted for the Linux kernel on Oct 2, 2003 + */ + +/* Copyright 1995, 1996, 1998, 1999, 2000, 2003 Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +#include <asm/linkage.h> +#include <asm/assembler.h> + + +.macro ARM_DIV_BODY dividend, divisor, result, curbit + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \curbit, \divisor + clz \result, \dividend + sub \result, \curbit, \result + mov \curbit, #1 + mov \divisor, \divisor, lsl \result + mov \curbit, \curbit, lsl \result + mov \result, #0 + +#else + + @ Initially shift the divisor left 3 bits if possible, + @ set curbit accordingly. This allows for curbit to be located + @ at the left end of each 4 bit nibbles in the division loop + @ to save one loop in most cases. + tst \divisor, #0xe0000000 + moveq \divisor, \divisor, lsl #3 + moveq \curbit, #8 + movne \curbit, #1 + + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. +1: cmp \divisor, #0x10000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #4 + movlo \curbit, \curbit, lsl #4 + blo 1b + + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. +1: cmp \divisor, #0x80000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #1 + movlo \curbit, \curbit, lsl #1 + blo 1b + + mov \result, #0 + +#endif + + @ Division loop +1: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + orrhs \result, \result, \curbit + cmp \dividend, \divisor, lsr #1 + subhs \dividend, \dividend, \divisor, lsr #1 + orrhs \result, \result, \curbit, lsr #1 + cmp \dividend, \divisor, lsr #2 + subhs \dividend, \dividend, \divisor, lsr #2 + orrhs \result, \result, \curbit, lsr #2 + cmp \dividend, \divisor, lsr #3 + subhs \dividend, \dividend, \divisor, lsr #3 + orrhs \result, \result, \curbit, lsr #3 + cmp \dividend, #0 @ Early termination? + movnes \curbit, \curbit, lsr #4 @ No, any more bits to do? + movne \divisor, \divisor, lsr #4 + bne 1b + +.endm + + +.macro ARM_DIV2_ORDER divisor, order + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \order, \divisor + rsb \order, \order, #31 + +#else + + cmp \divisor, #(1 << 16) + movhs \divisor, \divisor, lsr #16 + movhs \order, #16 + movlo \order, #0 + + cmp \divisor, #(1 << 8) + movhs \divisor, \divisor, lsr #8 + addhs \order, \order, #8 + + cmp \divisor, #(1 << 4) + movhs \divisor, \divisor, lsr #4 + addhs \order, \order, #4 + + cmp \divisor, #(1 << 2) + addhi \order, \order, #3 + addls \order, \order, \divisor, lsr #1 + +#endif + +.endm + + +.macro ARM_MOD_BODY dividend, divisor, order, spare + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \order, \divisor + clz \spare, \dividend + sub \order, \order, \spare + mov \divisor, \divisor, lsl \order + +#else + + mov \order, #0 + + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. +1: cmp \divisor, #0x10000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #4 + addlo \order, \order, #4 + blo 1b + + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. +1: cmp \divisor, #0x80000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #1 + addlo \order, \order, #1 + blo 1b + +#endif + + @ Perform all needed substractions to keep only the reminder. + @ Do comparisons in batch of 4 first. + subs \order, \order, #3 @ yes, 3 is intended here + blt 2f + +1: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + cmp \dividend, \divisor, lsr #1 + subhs \dividend, \dividend, \divisor, lsr #1 + cmp \dividend, \divisor, lsr #2 + subhs \dividend, \dividend, \divisor, lsr #2 + cmp \dividend, \divisor, lsr #3 + subhs \dividend, \dividend, \divisor, lsr #3 + cmp \dividend, #1 + mov \divisor, \divisor, lsr #4 + subges \order, \order, #4 + bge 1b + + tst \order, #3 + teqne \dividend, #0 + beq 5f + + @ Either 1, 2 or 3 comparison/substractions are left. +2: cmn \order, #2 + blt 4f + beq 3f + cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + mov \divisor, \divisor, lsr #1 +3: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + mov \divisor, \divisor, lsr #1 +4: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor +5: +.endm + + +ENTRY(__udivsi3) +ENTRY(__aeabi_uidiv) + + subs r2, r1, #1 + moveq pc, lr + bcc Ldiv0 + cmp r0, r1 + bls 11f + tst r1, r2 + beq 12f + + ARM_DIV_BODY r0, r1, r2, r3 + + mov r0, r2 + mov pc, lr + +11: moveq r0, #1 + movne r0, #0 + mov pc, lr + +12: ARM_DIV2_ORDER r1, r2 + + mov r0, r0, lsr r2 + mov pc, lr + + +ENTRY(__umodsi3) + + subs r2, r1, #1 @ compare divisor with 1 + bcc Ldiv0 + cmpne r0, r1 @ compare dividend with divisor + moveq r0, #0 + tsthi r1, r2 @ see if divisor is power of 2 + andeq r0, r0, r2 + movls pc, lr + + ARM_MOD_BODY r0, r1, r2, r3 + + mov pc, lr + + +ENTRY(__divsi3) +ENTRY(__aeabi_idiv) + + cmp r1, #0 + eor ip, r0, r1 @ save the sign of the result. + beq Ldiv0 + rsbmi r1, r1, #0 @ loops below use unsigned. + subs r2, r1, #1 @ division by 1 or -1 ? + beq 10f + movs r3, r0 + rsbmi r3, r0, #0 @ positive dividend value + cmp r3, r1 + bls 11f + tst r1, r2 @ divisor is power of 2 ? + beq 12f + + ARM_DIV_BODY r3, r1, r0, r2 + + cmp ip, #0 + rsbmi r0, r0, #0 + mov pc, lr + +10: teq ip, r0 @ same sign ? + rsbmi r0, r0, #0 + mov pc, lr + +11: movlo r0, #0 + moveq r0, ip, asr #31 + orreq r0, r0, #1 + mov pc, lr + +12: ARM_DIV2_ORDER r1, r2 + + cmp ip, #0 + mov r0, r3, lsr r2 + rsbmi r0, r0, #0 + mov pc, lr + + +ENTRY(__modsi3) + + cmp r1, #0 + beq Ldiv0 + rsbmi r1, r1, #0 @ loops below use unsigned. + movs ip, r0 @ preserve sign of dividend + rsbmi r0, r0, #0 @ if negative make positive + subs r2, r1, #1 @ compare divisor with 1 + cmpne r0, r1 @ compare dividend with divisor + moveq r0, #0 + tsthi r1, r2 @ see if divisor is power of 2 + andeq r0, r0, r2 + bls 10f + + ARM_MOD_BODY r0, r1, r2, r3 + +10: cmp ip, #0 + rsbmi r0, r0, #0 + mov pc, lr + +ENTRY(__aeabi_uidivmod) + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_uidiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +ENTRY(__aeabi_idivmod) + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_idiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +Ldiv0: + + str lr, [sp, #-8]! + bl __div0 + mov r0, #0 @ About as wrong as it could be. + ldr pc, [sp], #8 + +ENTRY(__div0) + mov pc, lr diff --git a/src/target/firmware/lib/memcpy.S b/src/target/firmware/lib/memcpy.S new file mode 100644 index 00000000..2bbd5692 --- /dev/null +++ b/src/target/firmware/lib/memcpy.S @@ -0,0 +1,59 @@ +/* + * linux/arch/arm/lib/memcpy.S + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <asm/linkage.h> +#include <asm/assembler.h> + + .macro ldr1w ptr reg abort + ldr \reg, [\ptr], #4 + .endm + + .macro ldr4w ptr reg1 reg2 reg3 reg4 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4} + .endm + + .macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro ldr1b ptr reg cond=al abort + ldr\cond\()b \reg, [\ptr], #1 + .endm + + .macro str1w ptr reg abort + str \reg, [\ptr], #4 + .endm + + .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + stmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro str1b ptr reg cond=al abort + str\cond\()b \reg, [\ptr], #1 + .endm + + .macro enter reg1 reg2 + stmdb sp!, {r0, \reg1, \reg2} + .endm + + .macro exit reg1 reg2 + ldmfd sp!, {r0, \reg1, \reg2} + .endm + + .text + +/* Prototype: void *memcpy(void *dest, const void *src, size_t n); */ + +ENTRY(memcpy) + +#include "copy_template.S" + diff --git a/src/target/firmware/lib/memset.S b/src/target/firmware/lib/memset.S new file mode 100644 index 00000000..04e254a8 --- /dev/null +++ b/src/target/firmware/lib/memset.S @@ -0,0 +1,80 @@ +/* + * linux/arch/arm/lib/memset.S + * + * Copyright (C) 1995-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ASM optimised string functions + */ +#include <asm/linkage.h> +#include <asm/assembler.h> + + .text + .align 5 + .word 0 + +1: subs r2, r2, #4 @ 1 do we have enough + blt 5f @ 1 bytes to align with? + cmp r3, #2 @ 1 + strltb r1, [r0], #1 @ 1 + strleb r1, [r0], #1 @ 1 + strb r1, [r0], #1 @ 1 + add r2, r2, r3 @ 1 (r2 = r2 - (4 - r3)) +/* + * The pointer is now aligned and the length is adjusted. Try doing the + * memzero again. + */ + +ENTRY(memset) + ands r3, r0, #3 @ 1 unaligned? + bne 1b @ 1 +/* + * we know that the pointer in r0 is aligned to a word boundary. + */ + orr r1, r1, r1, lsl #8 + orr r1, r1, r1, lsl #16 + mov r3, r1 + cmp r2, #16 + blt 4f +/* + * We need an extra register for this loop - save the return address and + * use the LR + */ + str lr, [sp, #-4]! + mov ip, r1 + mov lr, r1 + +2: subs r2, r2, #64 + stmgeia r0!, {r1, r3, ip, lr} @ 64 bytes at a time. + stmgeia r0!, {r1, r3, ip, lr} + stmgeia r0!, {r1, r3, ip, lr} + stmgeia r0!, {r1, r3, ip, lr} + bgt 2b + LOADREGS(eqfd, sp!, {pc}) @ Now <64 bytes to go. +/* + * No need to correct the count; we're only testing bits from now on + */ + tst r2, #32 + stmneia r0!, {r1, r3, ip, lr} + stmneia r0!, {r1, r3, ip, lr} + tst r2, #16 + stmneia r0!, {r1, r3, ip, lr} + ldr lr, [sp], #4 + +4: tst r2, #8 + stmneia r0!, {r1, r3} + tst r2, #4 + strne r1, [r0], #4 +/* + * When we get here, we've got less than 4 bytes to zero. We + * may have an unaligned pointer as well. + */ +5: tst r2, #2 + strneb r1, [r0], #1 + strneb r1, [r0], #1 + tst r2, #1 + strneb r1, [r0], #1 + RETINSTR(mov,pc,lr) diff --git a/src/target/firmware/lib/printf.c b/src/target/firmware/lib/printf.c new file mode 100644 index 00000000..a4fc6876 --- /dev/null +++ b/src/target/firmware/lib/printf.c @@ -0,0 +1,19 @@ + +#include <stdio.h> +#include <stdarg.h> + +static char printf_buffer[1024]; + +int printf(const char *fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + r = vsnprintf(printf_buffer, sizeof(printf_buffer), fmt, args); + va_end(args); + + puts(printf_buffer); + + return r; +} diff --git a/src/target/firmware/lib/setbit.S b/src/target/firmware/lib/setbit.S new file mode 100644 index 00000000..9009bc1e --- /dev/null +++ b/src/target/firmware/lib/setbit.S @@ -0,0 +1,22 @@ +/* + * linux/arch/arm/lib/setbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +/* + * Purpose : Function to set a bit + * Prototype: int set_bit(int bit, void *addr) + */ +ENTRY(_set_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_set_bit_le) + bitop orr diff --git a/src/target/firmware/lib/string.c b/src/target/firmware/lib/string.c new file mode 100644 index 00000000..97036528 --- /dev/null +++ b/src/target/firmware/lib/string.c @@ -0,0 +1,50 @@ +/* + * linux/lib/string.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * stupid library routines.. The optimized versions should generally be found + * as inline code in <asm-xx/string.h> + * + * These are buggy as well.. + * + * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de> + * - Added strsep() which will replace strtok() soon (because strsep() is + * reentrant and should be faster). Use only strsep() in new code, please. + * + * * Sat Feb 09 2002, Jason Thomas <jason@topic.com.au>, + * Matthew Hawkins <matt@mh.dropbear.id.au> + * - Kissed strtok() goodbye + */ + +#include <sys/types.h> +#include <string.h> +#include <ctype.h> + + +#ifndef __HAVE_ARCH_STRNLEN +/** + * strnlen - Find the length of a length-limited string + * @s: The string to be sized + * @count: The maximum number of bytes to search + */ +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} +#endif + +size_t strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} diff --git a/src/target/firmware/lib/testchangebit.S b/src/target/firmware/lib/testchangebit.S new file mode 100644 index 00000000..37c303e3 --- /dev/null +++ b/src/target/firmware/lib/testchangebit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testchangebit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +ENTRY(_test_and_change_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_change_bit_le) + testop eor, strb diff --git a/src/target/firmware/lib/testclearbit.S b/src/target/firmware/lib/testclearbit.S new file mode 100644 index 00000000..985c3996 --- /dev/null +++ b/src/target/firmware/lib/testclearbit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testclearbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +ENTRY(_test_and_clear_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_clear_bit_le) + testop bicne, strneb diff --git a/src/target/firmware/lib/testsetbit.S b/src/target/firmware/lib/testsetbit.S new file mode 100644 index 00000000..4a8a164b --- /dev/null +++ b/src/target/firmware/lib/testsetbit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testsetbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +ENTRY(_test_and_set_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_set_bit_le) + testop orreq, streqb diff --git a/src/target/firmware/lib/vsprintf.c b/src/target/firmware/lib/vsprintf.c new file mode 100644 index 00000000..80e8c1ad --- /dev/null +++ b/src/target/firmware/lib/vsprintf.c @@ -0,0 +1,847 @@ +/* + * linux/lib/vsprintf.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +/* + * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com> + * - changed to provide snprintf and vsnprintf functions + * So Feb 1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de> + * - scnprintf and vscnprintf + */ + +#include <stdio.h> +#include <limits.h> +#include <stdarg.h> +#include <sys/types.h> +#include <string.h> +#include <ctype.h> + +#include <asm/div64.h> + +/** + * strtoul - convert a string to an unsigned long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long strtoul(const char *cp,char **endp,unsigned int base) +{ + unsigned long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((toupper(*cp) == 'X') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } else if (base == 16) { + if (cp[0] == '0' && toupper(cp[1]) == 'X') + cp += 2; + } + while (isxdigit(*cp) && + (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + + +/** + * strtol - convert a string to a signed long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +long strtol(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -strtoul(cp+1,endp,base); + return strtoul(cp,endp,base); +} + + +/** + * strtoull - convert a string to an unsigned long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long long strtoull(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((toupper(*cp) == 'X') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } else if (base == 16) { + if (cp[0] == '0' && toupper(cp[1]) == 'X') + cp += 2; + } + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + + +/** + * strtoll - convert a string to a signed long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +long long strtoll(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -strtoull(cp+1,endp,base); + return strtoull(cp,endp,base); +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type) +{ + char c,sign,tmp[66]; + const char *digits; + static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int i; + + digits = (type & LARGE) ? large_digits : small_digits; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return NULL; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if ((signed long long) num < 0) { + sign = '-'; + num = - (signed long long) num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) { + while(size-->0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + } + if (sign) { + if (buf <= end) + *buf = sign; + ++buf; + } + if (type & SPECIAL) { + if (base==8) { + if (buf <= end) + *buf = '0'; + ++buf; + } else if (base==16) { + if (buf <= end) + *buf = '0'; + ++buf; + if (buf <= end) + *buf = digits[33]; + ++buf; + } + } + if (!(type & LEFT)) { + while (size-- > 0) { + if (buf <= end) + *buf = c; + ++buf; + } + } + while (i < precision--) { + if (buf <= end) + *buf = '0'; + ++buf; + } + while (i-- > 0) { + if (buf <= end) + *buf = tmp[i]; + ++buf; + } + while (size-- > 0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + return buf; +} + +/** + * vsnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The return value is the number of characters which would + * be generated for the given input, excluding the trailing + * '\0', as per ISO C99. If you want to have the exact + * number of characters written into @buf as return value + * (not including the trailing '\0'), use vscnprintf. If the + * return is greater than or equal to @size, the resulting + * string is truncated. + * + * Call this function if you are already dealing with a va_list. + * You probably want snprintf instead. + */ +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + int len; + unsigned long long num; + int i, base; + char *str, *end, c; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + /* 't' added for ptrdiff_t */ + + /* Reject out-of-range values early */ + if ((int) size < 0) { + return 0; + } + + str = buf; + end = buf + size - 1; + + if (end < buf - 1) { + end = ((void *) -1); + size = end - buf + 1; + } + + for (; *fmt ; ++fmt) { + if (*fmt != '%') { + if (str <= end) + *str = *fmt; + ++str; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt =='Z' || *fmt == 'z' || *fmt == 't') { + qualifier = *fmt; + ++fmt; + if (qualifier == 'l' && *fmt == 'l') { + qualifier = 'L'; + ++fmt; + } + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) { + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + } + c = (unsigned char) va_arg(args, int); + if (str <= end) + *str = c; + ++str; + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 's': + s = va_arg(args, char *); + + len = strnlen(s, precision); + + if (!(flags & LEFT)) { + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + } + for (i = 0; i < len; ++i) { + if (str <= end) + *str = *s; + ++str; ++s; + } + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, end, + (unsigned long) va_arg(args, void *), + 16, field_width, precision, flags); + continue; + + + case 'n': + /* FIXME: + * What does C99 say about the overflow case here? */ + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else if (qualifier == 'Z' || qualifier == 'z') { + size_t * ip = va_arg(args, size_t *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + if (str <= end) + *str = '%'; + ++str; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + if (str <= end) + *str = '%'; + ++str; + if (*fmt) { + if (str <= end) + *str = *fmt; + ++str; + } else { + --fmt; + } + continue; + } + if (qualifier == 'L') + num = va_arg(args, long long); + else if (qualifier == 'l') { + num = va_arg(args, unsigned long); + if (flags & SIGN) + num = (signed long) num; + } else if (qualifier == 'Z' || qualifier == 'z') { + num = va_arg(args, size_t); + } else if (qualifier == 't') { + num = va_arg(args, long); + } else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (signed short) num; + } else { + num = va_arg(args, unsigned int); + if (flags & SIGN) + num = (signed int) num; + } + str = number(str, end, num, base, + field_width, precision, flags); + } + if (str <= end) + *str = '\0'; + else if (size > 0) + /* don't write out a null byte if the buf size is zero */ + *end = '\0'; + /* the trailing null byte doesn't count towards the total + * ++str; + */ + return str-buf; +} + + +/** + * vscnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The return value is the number of characters which have been written into + * the @buf not including the trailing '\0'. If @size is <= 0 the function + * returns 0. + * + * Call this function if you are already dealing with a va_list. + * You probably want scnprintf instead. + */ +int vscnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + unsigned int i; + + i=vsnprintf(buf,size,fmt,args); + return (i >= size) ? (size - 1) : i; +} + + +/** + * snprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The return value is the number of characters which would be + * generated for the given input, excluding the trailing null, + * as per ISO C99. If the return is greater than or equal to + * @size, the resulting string is truncated. + */ +int snprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsnprintf(buf,size,fmt,args); + va_end(args); + return i; +} + + +/** + * scnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The return value is the number of characters written into @buf not including + * the trailing '\0'. If @size is <= 0 the function returns 0. If the return is + * greater than or equal to @size, the resulting string is truncated. + */ + +int scnprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + unsigned int i; + + va_start(args, fmt); + i = vsnprintf(buf, size, fmt, args); + va_end(args); + return (i >= size) ? (size - 1) : i; +} + +/** + * vsprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The function returns the number of characters written + * into @buf. Use vsnprintf or vscnprintf in order to avoid + * buffer overflows. + * + * Call this function if you are already dealing with a va_list. + * You probably want sprintf instead. + */ +int vsprintf(char *buf, const char *fmt, va_list args) +{ + return vsnprintf(buf, INT_MAX, fmt, args); +} + + +/** + * sprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The function returns the number of characters written + * into @buf. Use snprintf or scnprintf in order to avoid + * buffer overflows. + */ +int sprintf(char * buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsnprintf(buf, INT_MAX, fmt, args); + va_end(args); + return i; +} + + +/** + * vsscanf - Unformat a buffer into a list of arguments + * @buf: input buffer + * @fmt: format of buffer + * @args: arguments + */ +int vsscanf(const char * buf, const char * fmt, va_list args) +{ + const char *str = buf; + char *next; + char digit; + int num = 0; + int qualifier; + int base; + int field_width; + int is_sign = 0; + + while(*fmt && *str) { + /* skip any white space in format */ + /* white space in format matchs any amount of + * white space, including none, in the input. + */ + if (isspace(*fmt)) { + while (isspace(*fmt)) + ++fmt; + while (isspace(*str)) + ++str; + } + + /* anything that is not a conversion must match exactly */ + if (*fmt != '%' && *fmt) { + if (*fmt++ != *str++) + break; + continue; + } + + if (!*fmt) + break; + ++fmt; + + /* skip this conversion. + * advance both strings to next white space + */ + if (*fmt == '*') { + while (!isspace(*fmt) && *fmt) + fmt++; + while (!isspace(*str) && *str) + str++; + continue; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + + /* get conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt == 'Z' || *fmt == 'z') { + qualifier = *fmt++; + if (qualifier == *fmt) { + if (qualifier == 'h') { + qualifier = 'H'; + fmt++; + } else if (qualifier == 'l') { + qualifier = 'L'; + fmt++; + } + } + } + base = 10; + is_sign = 0; + + if (!*fmt || !*str) + break; + + switch(*fmt++) { + case 'c': + { + char *s = (char *) va_arg(args,char*); + if (field_width == -1) + field_width = 1; + do { + *s++ = *str++; + } while (--field_width > 0 && *str); + num++; + } + continue; + case 's': + { + char *s = (char *) va_arg(args, char *); + if(field_width == -1) + field_width = INT_MAX; + /* first, skip leading white space in buffer */ + while (isspace(*str)) + str++; + + /* now copy until next white space */ + while (*str && !isspace(*str) && field_width--) { + *s++ = *str++; + } + *s = '\0'; + num++; + } + continue; + case 'n': + /* return number of characters read so far */ + { + int *i = (int *)va_arg(args,int*); + *i = str - buf; + } + continue; + case 'o': + base = 8; + break; + case 'x': + case 'X': + base = 16; + break; + case 'i': + base = 0; + case 'd': + is_sign = 1; + case 'u': + break; + case '%': + /* looking for '%' in str */ + if (*str++ != '%') + return num; + continue; + default: + /* invalid format; stop here */ + return num; + } + + /* have some sort of integer conversion. + * first, skip white space in buffer. + */ + while (isspace(*str)) + str++; + + digit = *str; + if (is_sign && digit == '-') + digit = *(str + 1); + + if (!digit + || (base == 16 && !isxdigit(digit)) + || (base == 10 && !isdigit(digit)) + || (base == 8 && (!isdigit(digit) || digit > '7')) + || (base == 0 && !isdigit(digit))) + break; + + switch(qualifier) { + case 'H': /* that's 'hh' in format */ + if (is_sign) { + signed char *s = (signed char *) va_arg(args,signed char *); + *s = (signed char) strtol(str,&next,base); + } else { + unsigned char *s = (unsigned char *) va_arg(args, unsigned char *); + *s = (unsigned char) strtoul(str, &next, base); + } + break; + case 'h': + if (is_sign) { + short *s = (short *) va_arg(args,short *); + *s = (short) strtol(str,&next,base); + } else { + unsigned short *s = (unsigned short *) va_arg(args, unsigned short *); + *s = (unsigned short) strtoul(str, &next, base); + } + break; + case 'l': + if (is_sign) { + long *l = (long *) va_arg(args,long *); + *l = strtol(str,&next,base); + } else { + unsigned long *l = (unsigned long*) va_arg(args,unsigned long*); + *l = strtoul(str,&next,base); + } + break; + case 'L': + if (is_sign) { + long long *l = (long long*) va_arg(args,long long *); + *l = strtoll(str,&next,base); + } else { + unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*); + *l = strtoull(str,&next,base); + } + break; + case 'Z': + case 'z': + { + size_t *s = (size_t*) va_arg(args,size_t*); + *s = (size_t) strtoul(str,&next,base); + } + break; + default: + if (is_sign) { + int *i = (int *) va_arg(args, int*); + *i = (int) strtol(str,&next,base); + } else { + unsigned int *i = (unsigned int*) va_arg(args, unsigned int*); + *i = (unsigned int) strtoul(str,&next,base); + } + break; + } + num++; + + if (!next) + break; + str = next; + } + return num; +} + + +/** + * sscanf - Unformat a buffer into a list of arguments + * @buf: input buffer + * @fmt: formatting of buffer + * @...: resulting arguments + */ +int sscanf(const char * buf, const char * fmt, ...) +{ + va_list args; + int i; + + va_start(args,fmt); + i = vsscanf(buf,fmt,args); + va_end(args); + return i; +} + +/* generic puts() implementation independent of who provides putchar() */ +int puts(const char *s) +{ +#ifdef ARCH_HAS_CONSOLE + return _puts(s); +#else + while (1) { + char c = *s++; + if (c == 0) + return; + putchar(c); + } + return 0; +#endif +} diff --git a/src/target/firmware/rf/mt6139.c b/src/target/firmware/rf/mt6139.c new file mode 100644 index 00000000..6103646a --- /dev/null +++ b/src/target/firmware/rf/mt6139.c @@ -0,0 +1,205 @@ +/* Driver for RF Transceiver Circuit (MT6139) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <keypad.h> +#include <osmocore/gsm_utils.h> + +#include <layer1/agc.h> +#include <rffe.h> + +#include <mtk/mt6139.h> + +static void mt6139_compute_pll(uint32_t f_vco_100khz, + uint16_t *nint, uint16_t *nfrac) +{ + /* To compute Nint, we assume Nfrac is zero */ + *nint = (fvco_100khz / (10 * 2 * 26)) - (0 / 130); + + if (nint > 127) + printf("VCO Frequency %u kHz is out of spec\n", f_vco_100khz); + + /* Compute Nfract using the pre-computed Nint */ + /* Nfrac = ( (Fvco/2*26) - Nint) * 130 */ + /* Nfrac = ( (Fvco*130)/(2*26) - (Nint * 130) */ + *nfrac = (f_vco_100khz*130)/(52*10) - (nint * 130); +} + +/* Set ARFCN. Takes 2 reg_write, i.e. 8 TPU instructions */ +void mt6139_set_arfcn(uint16_t arfcn, int uplink) +{ + uint32_t regval = 0; + uint32_t vco_mult; + uint32_t freq_khz, f_vco_100khz; + uint16_t nint, nfrac; + + freq_khz = gsm_arfcn2freq10(arfcn, uplink) * 100; + printd("ARFCN %u -> %u kHz\n", arfcn, freq_khz); + + switch (gsm_arfcn2band(arfcn)) { + case GSM_BAND_850: + if (uplink) + regval |= MT6139_CW1_TRX_850; + regval |= (0 << MT6139_CW1_BAND_SHIFT); + vco_mult = 4; + break; + case GSM_BAND_900: + regval |= (1 << MT6139_CW1_BAND_SHIFT); + vco_mult = 4; + break; + case GSM_BAND_1800: + regval |= (2 << MT6139_CW1_BAND_SHIFT); + vco_mult = 2; + break; + case GSM_BAND_1900: + regval |= (3 << MT6139_CW1_BAND_SHIFT); + vco_mult = 2; + break; + default: + printf("Unsupported rf_band.\n"); + break; + } + + /* Compute VCO frequency for channel frequency */ + f_vco_100khz = (freq_khz / 100) * vco_mult; + + /* Compute Nint and Nfract */ + mt6139_compute_pll(f_vco_100khz, &nint, &nfrac); + + /* mask-in the Nint / Nfrac bits in CW1 */ + regval |= (nfrac & 0xff) << MT6139_CW1_NFRACT_SHIFT; + regval |= (nint & 0x7f) << MT6139_CW1_NINT_SHIFT; + +} + +void mt6139_init() +{ + uint32_t val; + + /* reset and get it out of reset again */ + val = MT6139_CW0_DIEN | (0x20 << MT6139_CW0_AFC_SHIFT); + mt6139_reg_write(0, val | MT6139_CW0_POR); + mt6139_reg_write(0, val); + + /* Turn off AM and A loop calibration function (CM9) */ + val = (0x40 << MT6139_CW9_DCD_CQ_SHIFT) | + (0x40 << MT6139_CW9_DCD_BQ_SHIFT) | + MT6139_CW9_PWR_DAC_C | MT6139_CW9_PWR_DAC_B; + mt6139_reg_write(9, val); + + /* Move to SLEEP mode */ + val = (0x3e << MT6139_CW2_GAINTBL_SHIFT) | + (MODE_SLEEP << MT6139_CW2_MODE_SHIFT) | + MT6139_CW2_AUTO_CAL | + (0x20 << MT6139_CW2_DCD_AQ_SHIFT) | + (0x20 << MT6139_CW2_DCD_AI_SHIFT); + mt6139_reg_write(2, val); +} + +void mt6139_rx_burst() +{ + uint8_t pga_gain; + + /* Turn on the synthesizer and move into Warm-up mode */ + val = (0x3e << MT6139_CW2_GAINTBL_SHIFT) | + (MODE_WARM_UP << MT6139_CW2_MODE_SHIFT) | + MT6139_CW2_AUTO_CAL | + (0x20 << MT6139_CW2_DCD_AQ_SHIFT) | + (0x20 << MT6139_CW2_DCD_AI_SHIFT); + mt6139_reg_write(2, val); + + /* Program the frequency synthesizer N counter and band selection */ + /* FIXME: see above for mt6139_set_arfcn() */ + + /* Set receive mode, PGA gain */ + val = (pga_gain << MT6139_CW2_GAINTBL_SHIFT) | + (MODE_RECEIVE << MT6139_CW2_MODE_SHIFT) | + MT6139_CW2_AUTO_CAL | + (0x20 << MT6139_CW2_DCD_AQ_SHIFT) | + (0x20 << MT6139_CW2_DCD_AI_SHIFT); + mt6139_reg_write(2, val); + + /* FIXME: Do the actual burst Rx */ + + /* Set Sleep mode */ + val = (0x3e << MT6139_CW2_GAINTBL_SHIFT) | + (MODE_SLEEP << MT6139_CW2_MODE_SHIFT) | + MT6139_CW2_AUTO_CAL | + (0x20 << MT6139_CW2_DCD_AQ_SHIFT) | + (0x20 << MT6139_CW2_DCD_AI_SHIFT); + mt6139_reg_write(2, val); +} + +void mt6139_tx_burst() +{ + /* Turn on the synthesizer and move into Warm-up mode */ + val = (0x3e << MT6139_CW2_GAINTBL_SHIFT) | + (MODE_WARM_UP << MT6139_CW2_MODE_SHIFT) | + MT6139_CW2_AUTO_CAL | + (0x20 << MT6139_CW2_DCD_AQ_SHIFT) | + (0x20 << MT6139_CW2_DCD_AI_SHIFT); + mt6139_reg_write(2, val); + + /* Program the frequency synthesizer N counter and band selection */ + /* FIXME: see above for mt6139_set_arfcn() */ + + /* Send Tx setting */ + val = MT6139_CW11_TX_CTL | + MT6139_CW11_TXG_IQM | + MT6139_CW11_TXD_IQM | + MT6139_CW11_TX_DIV2 | + MT6139_CW11_TX_DIV4 | + MT6139_CW11_TXG_BUF | + MT6139_CW11_TXD_BUF | + (3 << MT6139_CW11_TX_FLT_SHIFT) | + (1 << MT6139_CW11_TXAPC_SHIFT) | + (3 << MT6139_CW11_TXPW_SHIFT) | + (2 << MT6139_CW11_TXBIAST_SHIFT) | + MT6139_CW11_TXDIV_GC0; + if (1) // low band + mt6139_reg_write(11, val | (0 << MT6139_CW11_TXMODGAIN_SHIFT)); + else + mt6139_reg_write(11, val | (4 << MT6139_CW11_TXMODGAIN_SHIFT)); + + /* Set Transmit mode */ + val = (0x3e << MT6139_CW2_GAINTBL_SHIFT) | + (MODE_TRANSMIT << MT6139_CW2_MODE_SHIFT) | + MT6139_CW2_AUTO_CAL | + (0x20 << MT6139_CW2_DCD_AQ_SHIFT) | + (0x20 << MT6139_CW2_DCD_AI_SHIFT); + mt6139_reg_write(2, val); + + /* FIXME: Do the actual burst Tx */ + + /* Set Sleep mode */ + val = (0x3e << MT6139_CW2_GAINTBL_SHIFT) | + (MODE_SLEEP << MT6139_CW2_MODE_SHIFT) | + MT6139_CW2_AUTO_CAL | + (0x20 << MT6139_CW2_DCD_AQ_SHIFT) | + (0x20 << MT6139_CW2_DCD_AI_SHIFT); + mt6139_reg_write(2, val); +} + diff --git a/src/target/firmware/rf/trf6151.c b/src/target/firmware/rf/trf6151.c new file mode 100644 index 00000000..b465611d --- /dev/null +++ b/src/target/firmware/rf/trf6151.c @@ -0,0 +1,503 @@ +/* Driver for RF Transceiver Circuit (TRF6151) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <keypad.h> +#include <osmocore/gsm_utils.h> + +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <layer1/agc.h> +#include <rffe.h> + +#include <rf/trf6151.h> + +enum trf6151_reg { + REG_RX = 0, /* RF general settings */ + REG_PLL = 1, /* PLL settings */ + REG_PWR = 2, /* Power on/off funcitonal blocks */ + REG_CFG = 3, /* Transceiver and PA controller settings */ + REG_TEST1 = 4, + REG_TEST2 = 5, + REG_TEST3 = 6, + REG_TEST4 = 7, + _MAX_REG +}; + +/* REG_RX */ +#define RX_READ_EN (1 << 7) +#define RX_CAL_MODE (1 << 8) +#define RX_RF_GAIN_HIGH (3 << 9) +#define RX_VGA_GAIN_SHIFT 11 + +/* REG_PWR */ +#define PWR_BANDGAP_SHIFT 3 +#define PWR_BANDGAP_OFF (0 << PWR_BANDGAP_SHIFT) +#define PWR_BANDGAP_ON_SPEEDUP (2 << PWR_BANDGAP_SHIFT) +#define PWR_BANDGAP_ON (3 << PWR_BANDGAP_SHIFT) +#define PWR_REGUL_ON (1 << 5) +#define PWR_SYNTHE_OFF (0) +#define PWR_SYNTHE_RX_ON (1 << 9) +#define PWR_SYNTHE_TX_ON (1 << 10) +#define PWR_RX_MODE (1 << 11) +#define PWR_TX_MODE (1 << 13) +#define PWR_PACTRL_APC (1 << 14) +#define PWR_PACTRL_APCEN (1 << 15) + +/* REG_CFG */ +#define CFG_TX_LOOP_MANU (1 << 3) +#define CFG_PACTLR_IDIOD_30uA (0 << 4) +#define CFG_PACTLR_IDIOD_300uA (1 << 4) +#define CFG_PACTLR_RES_OPEN (0 << 10) +#define CFG_PACTLR_RES_150k (1 << 10) +#define CFG_PACTLR_RES_300k (2 << 10) +#define CFG_PACTLR_CAP_0pF (0 << 12) +#define CFG_PACTLR_CAP_12p5F (1 << 12) +#define CFG_PACTLR_CAP_25pF (3 << 12) +#define CFG_PACTLR_CAP_50pF (2 << 12) +#define CFG_TEMP_SENSOR (1 << 14) +#define CFG_ILOGIC_INIT_DIS (1 << 15) + +/* FIXME: This must be defined in the RFFE configuration */ +#define TRF6151_TSP_UID 2 +#define TRF6151_PACTRL_CFG (CFG_PACTLR_RES_OPEN|CFG_PACTLR_CAP_0pF|CFG_PACTLR_IDIOD_30uA) + +#define PLL_VAL(a, b) ((a << 3) | (((b)-64) << 9)) + +/* All values in qbits unless otherwise speciifed */ +#define TRF6151_LDO_DELAY_TS 6 /* six TDMA frames (at least 25ms) */ +#define TRF6151_RX_PLL_DELAY 184 /* 170 us */ +#define TRF6151_TX_PLL_DELAY 260 /* 240 us */ + +uint16_t rf_arfcn = 871; /* TODO: this needs to be private */ +static uint16_t rf_band; + +static uint8_t trf6151_vga_dbm = 40; +static int trf6151_gain_high = 1; + +static uint16_t trf6151_reg_cache[_MAX_REG] = { + [REG_RX] = 0x9E00, + [REG_PLL] = 0x0000, + [REG_PWR] = 0x0000, + [REG_CFG] = 0x2980, +}; + +/* Write to a TRF6151 register (4 TPU instructions) */ +static void trf6151_reg_write(uint16_t reg, uint16_t val) +{ + printd("trf6151_reg_write(reg=%u, val=0x%04x)\n", reg, val); + /* each TSP write takes 4 TPU instructions */ + tsp_write(TRF6151_TSP_UID, 16, (reg | val)); + trf6151_reg_cache[reg] = val; +} + +int trf6151_set_gain(uint8_t dbm, int high) +{ + uint16_t reg = trf6151_reg_cache[REG_RX] & 0x07ff; + printd("trf6151_set_gain(%u, %d)\n", dbm, high); + + if (dbm < 14 || dbm > 40) + return -1; + + /* clear the gain bits first */ + reg &= ~((0x1F) << RX_VGA_GAIN_SHIFT); + /* OR-in the new gain value */ + reg |= (6 + (dbm-14)/2) << RX_VGA_GAIN_SHIFT; + + if (high) + reg |= RX_RF_GAIN_HIGH; + else + reg &= ~RX_RF_GAIN_HIGH; + + trf6151_reg_write(REG_RX, reg); + + return 0; +} + +#define SCALE_100KHZ 100 + +/* Compute TRF6151 PLL valuese for all 4 RX bands */ +static uint16_t trf6151_pll_rx(uint32_t freq_khz) +{ + uint32_t freq_100khz = freq_khz / SCALE_100KHZ; /* Scale from *1000 (k) to *100000 (0.1M) */ + uint32_t fb_100khz; /* frequency of B alone, without A (units of 100kHz) */ + uint32_t l; + uint32_t a, b; /* The PLL multipliers we want to compute */ + + /* L = 4 for low band, 2 for high band */ + if (freq_khz < 1000000) + l = 4; + else + l = 2; + + /* To compute B, we assume A is zero */ + b = (freq_100khz * 65 * l) / (64 * 26 * 10); + + if ((l == 4 && (b < 135 || b > 150)) || + (l == 2 && (b < 141 || b > 155))) + printf("Frequency %u kHz is out of spec\n", freq_khz); + + /* Compute PLL frequency assuming A == 0 */ + fb_100khz = (b * 64 * 26 * 10) / (65 * l); + + /* Compute how many 100kHz units A needs to add */ + a = freq_100khz - fb_100khz; + + if (l == 2) + a = a / 2; + + /* since all frequencies are expanded a factor of 10, we don't need to multiply A */ + printd("Freq %u kHz => A = %u, B = %u\n", freq_khz, a, b); + + /* return value in trf6151 register layout form */ + return PLL_VAL(a, b); +} + +/* Compute TRF6151 PLL TX values for GSM900 and GSM1800 only! */ +static uint16_t trf6151_pll_tx(uint32_t freq_khz) +{ + uint32_t freq_100khz = freq_khz / SCALE_100KHZ; /* Scale from *1000 (k) to *100000 (0.1M) */ + uint32_t fb_100khz; /* frequency of B alone, without A (units of 100kHz) */ + uint32_t l, r, m; + uint32_t a, b; /* The PLL multipliers we want to compute */ + + /* L = 4 for low band, 2 for high band */ + if (freq_khz < 1000000) { + r = 35; + l = 4; + m = 52; + } else { + r = 70; + l = 2; + m = 26; + } + + /* To compute B, we assume A is zero */ + b = (freq_100khz * r * l * m) / (64 * 26 * 10 * (m + l)); + + if ((l == 4 && (b < 68 || b > 71)) || + (l == 2 && (b < 133 || b > 149))) + printf("Frequency %u kHz is out of spec\n", freq_khz); + + /* Compute PLL frequency assuming A == 0 */ + fb_100khz = (b * 64 * 26 * 10 * (m + l)) / (r * l * m); + + /* Compute how many 100kHz units A needs to add */ + a = freq_100khz - fb_100khz; + + a = a / 2; + + /* since all frequencies are expanded a factor of 10, we don't need to multiply A */ + printd("Freq %u kHz => A = %u, B = %u\n", freq_khz, a, b); + + /* return value in trf6151 register layout form */ + return PLL_VAL(a, b); +} + +enum trf6151_pwr_unit { + TRF1651_PACTLR_APC, + TRF6151_PACTRL_APCEN, + TRF6151_TRANSMITTER, + TRF6151_REGULATORS, +}; + +enum trf6151_gsm_band { + GSM900 = 1, + GSM1800 = 2, + GSM850_LOW = 4, + GSM850_HIGH = 5, + GSM1900 = 6, +}; + +static inline void trf6151_reset(void) +{ + /* pull the nRESET line low */ + tsp_act_disable((1 << 0)); + tpu_enq_wait(50); + /* release nRESET */ + tsp_act_enable((1 << 0)); +} + +void trf6151_init(void) +{ + /* Configure TSPEN0, which is connected to TWL3025, + * FIXME: why is this here and not in the TWL3025 driver? */ + tsp_setup(0, 1, 0, 0); + /* Configure TSPEN2, which is connected ot TRF6151 STROBE */ + tsp_setup(TRF6151_TSP_UID, 0, 1, 1); + + trf6151_reset(); + + /* configure TRF6151 for operation */ + trf6151_power(1); + trf6151_reg_write(REG_CFG, TRF6151_PACTRL_CFG | CFG_ILOGIC_INIT_DIS); + + /* FIXME: Uplink / Downlink Calibration */ +} + +void trf6151_power(int on) +{ + if (on) { + trf6151_reg_write(REG_PWR, PWR_REGUL_ON | PWR_BANDGAP_ON); + /* wait until regulators are stable (25ms == 27100 qbits) */ + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(2100); + } else + trf6151_reg_write(REG_PWR, PWR_BANDGAP_ON); +} + +/* Set the operational mode of the TRF6151 chip */ +void trf6151_set_mode(enum trf6151_mode mode) +{ + uint16_t pwr = (PWR_REGUL_ON | PWR_BANDGAP_ON | (rf_band<<6)); + + switch (mode) { + case TRF6151_IDLE: + /* should we switch of the RF gain for power saving? */ + break; + case TRF6151_RX: + pwr |= (PWR_SYNTHE_RX_ON | PWR_RX_MODE); + break; + case TRF6151_TX: +#if 0 + pwr |= (PWR_SYNTHE_TX_ON | PWR_TX_MODE); +#else // Dieter: we should turn power control on (for TPU: check timing and order !) + pwr |= (PWR_SYNTHE_TX_ON | PWR_TX_MODE | PWR_PACTRL_APC | PWR_PACTRL_APCEN); // Dieter: TODO +#endif + break; + } + trf6151_reg_write(REG_PWR, pwr); +} + +static void trf6151_band_select(enum trf6151_gsm_band band) +{ + uint16_t pwr = trf6151_reg_cache[REG_PWR]; + + pwr &= ~(3 << 6); + pwr |= (band << 6); + + trf6151_reg_write(REG_PWR, pwr); +} + +/* Set ARFCN. Takes 2 reg_write, i.e. 8 TPU instructions */ +void trf6151_set_arfcn(uint16_t arfcn, int uplink) +{ + uint32_t freq_khz; + + switch (gsm_arfcn2band(arfcn)) { + case GSM_BAND_850: + rf_band = GSM850_LOW; /* FIXME: what about HIGH */ + break; + case GSM_BAND_900: + rf_band = GSM900; + break; + case GSM_BAND_1800: + rf_band = GSM1800; + break; + case GSM_BAND_1900: + rf_band = GSM1900; + break; + case GSM_BAND_450: + case GSM_BAND_480: + case GSM_BAND_750: + case GSM_BAND_810: + printf("Unsupported rf_band.\n"); + break; + } + + trf6151_band_select(rf_band); + + freq_khz = gsm_arfcn2freq10(arfcn, uplink) * 100; + printd("ARFCN %u -> %u kHz\n", arfcn, freq_khz); + + if (uplink == 0) + trf6151_reg_write(REG_PLL, trf6151_pll_rx(freq_khz)); + else { + if (rf_band != GSM900 && rf_band != GSM1800) { + printf("TX only supports GSM900/1800\n"); + return; + } + trf6151_reg_write(REG_PLL, trf6151_pll_tx(freq_khz)); + } + + rf_arfcn = arfcn; // TODO: arfcn is referenced at other places +} + +void trf6151_calib_dc_offs(void) +{ + uint16_t rx = trf6151_reg_cache[REG_RX]; + + /* Set RX CAL Mode bit, it will re-set automatically */ + trf6151_reg_write(REG_RX, rx | RX_CAL_MODE); + /* DC offset calibration can take up to 50us, i.e. 54.16 * 923ns*/ + tpu_enq_wait(55); +} + +/* Frontend gain can be switched high or low (dB) */ +#define TRF6151_FE_GAIN_LOW 7 +#define TRF6151_FE_GAIN_HIGH 27 + +/* VGA at baseband can be adjusted in this range (dB) */ +#define TRF6151_VGA_GAIN_MIN 14 +#define TRF6151_VGA_GAIN_MAX 40 + +uint8_t trf6151_get_gain(void) +{ + uint16_t vga, reg_rx = trf6151_reg_cache[REG_RX]; + uint8_t gain = 0; + + switch ((reg_rx >> 9) & 3) { + case 0: + gain += TRF6151_FE_GAIN_LOW; + break; + case 3: + gain += TRF6151_FE_GAIN_HIGH; + break; + } + + vga = (reg_rx >> RX_VGA_GAIN_SHIFT) & 0x1f; + if (vga < 6) + vga = 6; + + gain += TRF6151_VGA_GAIN_MIN + (vga - 6) * 2; + + return gain; +} + +void trf6151_test(uint16_t arfcn) +{ + /* Select ARFCN 871 downlink */ + trf6151_set_arfcn(arfcn, 0); + + trf6151_set_mode(TRF6151_RX); + //trf6151_reg_write(REG_PWR, (PWR_SYNTHE_RX_ON | PWR_RX_MODE | PWR_REGUL_ON | (rf_band<<6) | PWR_BANDGAP_ON)); + /* Wait for PLL stabilization (170us max) */ + tpu_enq_wait(TRF6151_RX_PLL_DELAY); + + /* Use DC offset calibration after RX mode has been switched on + * (might not be needed) */ + trf6151_calib_dc_offs(); + + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +void trf6151_tx_test(uint16_t arfcn) +{ + /* Select ARFCN uplink */ + trf6151_set_arfcn(arfcn, 1); + + trf6151_set_mode(TRF6151_TX); + tpu_enq_wait(TRF6151_RX_PLL_DELAY); + + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +#define TRF6151_REGWR_QBITS 8 /* 4 GSM qbits + 4 TPU instructions */ +#define TRF6151_RX_TPU_INSTR 4 /* set_gain(1), set_arfcn(2), set_mode(1) */ + +/* delay caused by this driver programming the TPU for RX mode */ +#define TRF6151_RX_TPU_DELAY (TRF6151_RX_TPU_INSTR * TRF6151_REGWR_QBITS) + +/* prepare a Rx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_rx_window(int16_t start_qbits, uint16_t arfcn) +{ + int16_t start_pll_qbits; + + /* power up at the right time _before_ the 'start_qbits' point in time */ + start_pll_qbits = add_mod5000(start_qbits, -(TRF6151_RX_PLL_DELAY + TRF6151_RX_TPU_DELAY)); + tpu_enq_at(start_pll_qbits); + + /* Set the AGC and PLL registers */ + trf6151_set_arfcn(arfcn, 0); + trf6151_set_gain(trf6151_vga_dbm, trf6151_gain_high); + trf6151_set_mode(TRF6151_RX); + + /* FIXME: power down at the right time again */ +} + +/* prepare a Tx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_tx_window(int16_t start_qbits, uint16_t arfcn) +{ +#ifdef CONFIG_TX_ENABLE + int16_t start_pll_qbits; + + /* power up at the right time _before_ the 'start_qbits' point in time */ + start_pll_qbits = add_mod5000(start_qbits, -(TRF6151_TX_PLL_DELAY + TRF6151_RX_TPU_DELAY)); + tpu_enq_at(start_pll_qbits); + + trf6151_set_arfcn(arfcn, 1); + trf6151_set_mode(TRF6151_TX); + + /* FIXME: power down at the right time again */ +#endif +} + +/* Given the expected input level of exp_inp dBm and the target of target_bb + * dBm, configure the RF Frontend with the respective gain */ +void trf6151_compute_gain(int16_t exp_inp, int16_t target_bb) +{ + /* TRF6151 VGA gain between 14 to 40 dB, plus 20db high/low */ + int16_t exp_bb_dbm8, delta_dbm8; + int16_t exp_inp_dbm8 = to_dbm8(exp_inp); + int16_t target_bb_dbm8 = to_dbm8(target_bb); + int16_t vga_gain = TRF6151_VGA_GAIN_MIN; + int high = 0; + + /* calculate the dBm8 that we expect at the baseband */ + exp_bb_dbm8 = exp_inp_dbm8 + to_dbm8(system_inherent_gain); + + /* calculate the error that we expect. */ + delta_dbm8 = target_bb_dbm8 - exp_bb_dbm8; + + /* If this is negative or less than TRF6151_GAIN_MIN, we are pretty + * much lost as we cannot reduce the system inherent gain. If it is + * positive, it corresponds to the gain that we need to configure */ + if (delta_dbm8 < to_dbm8(TRF6151_FE_GAIN_LOW + TRF6151_VGA_GAIN_MIN)) { + printd("AGC Input level overflow\n"); + high = 0; + vga_gain = TRF6151_VGA_GAIN_MIN; + } else if (delta_dbm8 > to_dbm8(TRF6151_FE_GAIN_HIGH + + TRF6151_VGA_GAIN_MIN)) { + high = 1; + delta_dbm8 -= to_dbm8(TRF6151_FE_GAIN_HIGH); + } + vga_gain = delta_dbm8/8; + if (vga_gain > TRF6151_VGA_GAIN_MAX) + vga_gain = TRF6151_VGA_GAIN_MAX; + + /* update the static global variables which are used when programming + * the window */ + trf6151_vga_dbm = vga_gain; + trf6151_gain_high = high; +} diff --git a/src/target/ui-experiment/README b/src/target/ui-experiment/README new file mode 100644 index 00000000..81b34d72 --- /dev/null +++ b/src/target/ui-experiment/README @@ -0,0 +1,11 @@ + +This is some exploratory and experimental code related +to the development of a proper lcd-based ui. + +It has been written by Ingo Albrecht <prom@berlin.ccc.de> +and, lacking further arrangements, should be considered to +be licensed under the GNU GPL v2 or later. + +I have placed this here due to general interest. All of it +can safely be removed once we have a proper ui. + diff --git a/src/target/ui-experiment/display.h b/src/target/ui-experiment/display.h new file mode 100644 index 00000000..55913700 --- /dev/null +++ b/src/target/ui-experiment/display.h @@ -0,0 +1,46 @@ + +#ifndef _UI_DISPLAY_H +#define _UI_DISPLAY_H + +#include <ui/pixel.h> +#include <ui/image.h> + +/** + * Displays - physical display devices + * + * This layer is introduced tentatively, expecting use + * of OSMOCOM on multi-display phones, most likely + * with a main screen and a cover screen. + * + */ +struct display { + const char *name; + + pxtype_t pixeltype; + pxsize_t width; + pxsize_t height; + + /* We always operate on an in-memory frame buffer that + * can be put on display using damage functions provided + * by the image class. + */ + struct image *fbuf; + + /* + * We display a top-level widget. + */ + struct widget *widget; + + /* + * We hold a graphics context, configured for the target + * pixel format. + */ + struct graphics *graphics; + + + void (*draw) (struct display *display); + + void *priv; +}; + +#endif diff --git a/src/target/ui-experiment/font.h b/src/target/ui-experiment/font.h Binary files differnew file mode 100644 index 00000000..5dc6eae7 --- /dev/null +++ b/src/target/ui-experiment/font.h diff --git a/src/target/ui-experiment/image.h b/src/target/ui-experiment/image.h new file mode 100644 index 00000000..13c11f5b --- /dev/null +++ b/src/target/ui-experiment/image.h @@ -0,0 +1,166 @@ + +#ifndef _UI_IMAGE_H +#define _UI_IMAGE_H + +/* for exit() */ +#include <stdlib.h> + +#include <ui/pixel.h> +#include <ui/font.h> + +struct image { + pxtype_t type; + pxdims_t size; + unsigned char *data; +}; + + +struct image font_img_v = { + .type = PXTYPE_MONO_V8, + .size = {8, 2048}, + .data = &fontdata_r8x8, +}; + +struct image font_img_h = { + .type = PXTYPE_MONO_H8, + .size = {8, 2048}, + .data = &fontdata_r8x8_horiz, +}; + +px_t +image_get_pixel(struct image *img, pxoff_t x, pxoff_t y) { + unsigned stride, base, offset; + const uint8_t *p8; + const uint16_t *p16; + + switch(img->type) { + case PXTYPE_MONO_V8: + stride = img->size.w; + base = y / 8; + offset = y % 8; + p8 = (uint8_t*)(img->data + x + base * stride); + return px_from_mono(((*p8) >> offset) & 1); + case PXTYPE_MONO_H8: + stride = img->size.w / 8; + base = x / 8; + offset = x % 8; + p8 = (uint8_t*)(img->data + base + y * stride); + return px_from_mono(((*p8) >> offset) & 1); + case PXTYPE_RGB444: + stride = img->size.w * 2; + p16 = (uint16_t*)(img->data + x * 2 + y * stride); + return px_from_rgb444(*p16); + } + + return 0; +} + +void +image_set_pixel(struct image *img, pxoff_t x, pxoff_t y, px_t v) { + unsigned stride, base, offset; + uint8_t *p8; + uint16_t *p16; + + switch(img->type) { + case PXTYPE_MONO_V8: + stride = img->size.w; + base = y / 8; + offset = y % 8; + p8 = (uint8_t*)(img->data + x + base * stride); + *p8 |= (px_to_mono(v) << offset); + break; + case PXTYPE_MONO_H8: + stride = img->size.w / 8; + base = x / 8; + offset = x % 8; + p8 = (uint8_t*)(img->data + base + y * stride); + *p8 |= (px_to_mono(v) << offset); + break; + case PXTYPE_RGB444: + stride = img->size.w * 2; + p16 = (uint16_t*)(img->data + x * 2 + y * stride); + *p16 = px_to_rgb444(v); + break; + } +} + +void +image_blit(struct image *dst, pxposn_t dstp, + struct image *src, pxposn_t srcp, + pxdims_t d) +{ + unsigned x, y, s; + + printf("blit %dx%d from %dx%d to %dx%d\n", d.w, d.h, srcp.x, srcp.y, dstp.x, dstp.y); + + // *cough* slow. + for(y = 0; y < d.h; y++) { + for(x = 0; x < d.w; x++) { + px_t p = image_get_pixel(src, srcp.x + x, srcp.y + y); + image_set_pixel(dst, dstp.x + x, dstp.y + y, p); + } + } +} + +void +image_draw_char(struct image *dst, pxposn_t p, char chr) { + unsigned char c = (unsigned char)chr; + pxposn_t pf = {0,c*8}; + pxdims_t d = {8,8}; + image_blit(dst, p, &font_img_h, pf, d); +} + +void +image_draw_string(struct image *dst, pxposn_t p, char *str) { + while(*str) { + image_draw_char(dst, p, *str); + p.x += 8; + str++; + } +} + +static void +image_fill_rect_rgb444(struct image *dst, pxrect_t rect, uint16_t color) { + unsigned x, y, s; + uint16_t *p; + + unsigned stride = dst->size.w * 2; + + for(y = rect.p.y; y < rect.p.y + rect.d.h; y++) { + for(x = rect.p.x; x < rect.p.x + rect.d.w; x++) { + p = (uint16_t*)&dst->data[x * 2 + y * stride]; + *p = color; + } + } +} + +void +image_fill_rect(struct image *dst, pxrect_t rect, px_t color) +{ + switch(dst->type) { + case PXTYPE_MONO_V8: + break; + case PXTYPE_MONO_H8: + break; + case PXTYPE_RGB444: + image_fill_rect_rgb444(dst, rect, px_to_rgb444(color)); + break; + } +} + +void +image_draw_hline(struct image *dst, pxposn_t posn, pxoff_t len, px_t color) +{ +} + +void +image_draw_vline(struct image *dst, pxposn_t posn, pxoff_t len, px_t color) +{ +} + +void +image_draw_rect(struct image *dst, pxrect_t rect, px_t color) +{ +} + +#endif diff --git a/src/target/ui-experiment/menu.h b/src/target/ui-experiment/menu.h new file mode 100644 index 00000000..d9cc97b3 --- /dev/null +++ b/src/target/ui-experiment/menu.h @@ -0,0 +1,18 @@ + +/** + * Menus - menus and menu items + * + * We represent both menus and menu items in a single structure. + * + * They share the properties of having a title as well as having + * interaction callbacks such as on_select. + * + * Menus have a child item array that is indexed by menu position. + * The position of items in this array is used for numeric menu navigation. + * + */ +struct menu { + const char *title; + void (*on_select)(void); + struct menu *children[10]; +}; diff --git a/src/target/ui-experiment/pixel.h b/src/target/ui-experiment/pixel.h new file mode 100644 index 00000000..dde28e2a --- /dev/null +++ b/src/target/ui-experiment/pixel.h @@ -0,0 +1,113 @@ + +#ifndef _UI_PIXEL_H +#define _UI_PIXEL_H + +#include <stdint.h> +#include <stdio.h> + +/** Supported pixel types */ +typedef enum { + _PXTYPE_INVALID, + + /** "Generic" pixel type (24/32bit RGB) */ + PXTYPE_GENERIC, + + /** 8 horizontal mono pixels per byte */ + PXTYPE_MONO_H8, + + /** 8 vertical mono pixels per byte */ + PXTYPE_MONO_V8, + + /** 12bit RGB444 colors */ + PXTYPE_RGB444, + +} pxtype_t; + + +/** Generic pixel type */ +typedef uint32_t px_t; + +#define PX_R(p) ((uint8_t)((v) >> 16 & 0xFF)) +#define PX_G(p) ((uint8_t)((v) >> 8 & 0xFF)) +#define PX_B(p) ((uint8_t)((v) >> 0 / 0xFF)) + +#define PX_RGB(r,g,b) ((px_t)((r)<<16|(g)<<8|(b))) + +#define PX_BLACK ((px_t)0x000000) +#define PX_RED ((px_t)0xFF0000) +#define PX_GREEN ((px_t)0x00FF00) +#define PX_BLUE ((px_t)0x0000FF) +#define PX_WHITE ((px_t)0xFFFFFF) + + +/* Mono types */ +typedef uint8_t px_mono_t; + +#define PX_MONO_BLACK ((px_mono_t)0) +#define PX_MONO_WHITE ((px_mono_t)1) + +inline px_t +px_from_mono(uint8_t v) { + return v ? PX_WHITE : PX_BLACK; +} + +inline uint8_t +px_to_mono(px_t v) { + uint16_t a = (PX_R(v) + PX_G(v) + PX_B(v)) / 3; + return (a >= 0x7f) ? 1 : 0; +} + +/* RGB444 */ +typedef uint16_t px_rgb444_t; + +#define PX_RGB444_R(p) ((p) >> 8 & 0xf) +#define PX_RGB444_G(p) ((p) >> 4 & 0xf) +#define PX_RGB444_B(p) ((p) >> 0 & 0xf) + +#define PX_RGB444_RGB(r,g,b) ((px_rgb444_t)((r)<<8|(g)<<4|(b))) + +inline px_t +px_from_rgb444(px_rgb444_t v) { + return + PX_RGB444_R(v) << 16 | PX_RGB444_R(v) << 20 + | PX_RGB444_G(v) << 8 | PX_RGB444_G(v) << 12 + | PX_RGB444_B(v) << 0 | PX_RGB444_B(v) << 4; +} + +inline uint16_t +px_to_rgb444(px_t v) { + uint8_t r = (v >> 20) & 0xF; + uint8_t g = (v >> 12) & 0xF; + uint8_t b = (v >> 4) & 0xF; + + uint16_t res = (r<< 8) | (g << 4) | (b << 0); + + return res; +} + + +/** Size in pixels */ +typedef uint16_t pxsize_t; + +/** Offset in pixels */ +typedef int16_t pxoff_t; + +/** 2D position in pixels */ +typedef struct { + pxoff_t x; + pxoff_t y; +} pxposn_t; + +/** 2D dimensions in pixels */ +typedef struct { + pxsize_t w; + pxsize_t h; +} pxdims_t; + +/** 2D rectangle in pixels */ +typedef struct { + pxposn_t p; + pxdims_t d; +} pxrect_t; + +#endif diff --git a/src/target/ui-experiment/png2tiny.c b/src/target/ui-experiment/png2tiny.c new file mode 100644 index 00000000..d314b9a2 --- /dev/null +++ b/src/target/ui-experiment/png2tiny.c @@ -0,0 +1,51 @@ + +#include <stdlib.h> + +#include <SDL_image.h> + +enum { + FORMAT_NONE, + FORMAT_C +}; + +void +version(const char *name) { + puts(name); + //printf("%s rev %s\n", name, REVISION); + exit(2); +} + +void +usage(const char *name) { + printf("Usage: %s [-hv] [-f outfmt] [-s outsym] <infile> <outfile>\n"); + exit(2); +} + +int +main(int argc, char **argv) { + int opt, outfmt; + const char *outsym = NULL; + SDL_Surface *img; + + while((opt = getopt(argc, argv, "f:s:hv")) != -1) { + switch(opt) { + case 'f': + if(!strcmp(optarg, "c")) { + outfmt = FORMAT_C; + } + break; + case 's': + outsym = optarg; + break; + case 'v': + version(argv[0]); + break; + case 'h': + default: + usage(argv[0]); + break; + } + } + + return 0; +} diff --git a/src/target/ui-experiment/screen.h b/src/target/ui-experiment/screen.h new file mode 100644 index 00000000..1174d9e7 --- /dev/null +++ b/src/target/ui-experiment/screen.h @@ -0,0 +1,21 @@ + +/** + * Screens - full-screen dialogs + * + * These compose the first level of interaction in the UI. + * + * There is always exactly one active screen, which is in + * control of the entire display on which it is displayed. + * + * Screen activations are stacked, providing interaction depth. + * + */ +struct screen { + const char *name; + void (*on_enter)(void); + void (*on_leave)(void); + void (*on_render)(void); + void (*on_key_press)(void); + void (*on_key_release)(void); +}; + diff --git a/src/target/ui-experiment/sdl.c b/src/target/ui-experiment/sdl.c new file mode 100644 index 00000000..e6daac70 --- /dev/null +++ b/src/target/ui-experiment/sdl.c @@ -0,0 +1,250 @@ + +#include <ui/display.h> +#include <ui/image.h> +#include <ui/sdl.h> + +#include <stdio.h> + +#include <SDL.h> + +#define SDL_PRIV(d) ((struct sdl_display*)(d)->priv) + +#define REFRESH_INTERVAL_MSEC 50 + +struct sdl_display { + SDL_Surface *display; + SDL_TimerID refresh; + unsigned width; + unsigned height; + unsigned scale; +}; + +static Uint32 sdl_redraw_callback(Uint32 interval, void *param) { + struct display *display = (struct display*)param; + + display->draw(display); + + return interval; +} + +void +sdl_init(struct display *display, + unsigned width, unsigned height, unsigned scale) +{ + if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)) { + printf("Failed to initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + + atexit(&SDL_Quit); + + struct sdl_display *priv = SDL_PRIV(display); + + priv->width = width; + priv->height = height; + priv->scale = scale; + + priv->display = SDL_SetVideoMode(width * scale, height * scale, 32, 0); + if(!priv->display) { + printf("Failed to set SDL video mode: %s\n", SDL_GetError()); + exit(1); + } + + priv->refresh = SDL_AddTimer(REFRESH_INTERVAL_MSEC, + &sdl_redraw_callback, display); + if(!priv->refresh) { + printf("Failed to add refresh timer: %s\n", SDL_GetError()); + exit(1); + } +} + +void +sdl_draw(struct display *display) +{ + struct sdl_display *priv = SDL_PRIV(display); + + struct image *img = display->fbuf; + + SDL_Rect r; + + r.w = priv->scale; + r.h = priv->scale; + + if(img->type == PXTYPE_RGB444) { + unsigned stride = img->size.w * 2; + + unsigned x, y; + for(y = 0; y < img->size.h; y++) { + for(x = 0; x < img->size.w; x++) { + px_t color = image_get_pixel(img, x, y); + + r.x = x * priv->scale; + r.y = y * priv->scale; + + SDL_FillRect(priv->display, &r, color); + } + } + } else { + puts("Unsupported framebuffer type for SDL emulator."); + exit(1); + } + + SDL_UpdateRect(priv->display, 0, 0, 0, 0); +} + + +static struct sdl_display display_sdl_priv; + +uint8_t sdl_fbuf[96*64*2]; + +struct image display_sdl_fbuf = { + .type = PXTYPE_RGB444, + .size = {96, 64}, + .data = &sdl_fbuf +}; + +struct display display_sdl = { + .name = "Main Display", + .fbuf = &display_sdl_fbuf, + .priv = &display_sdl_priv, + .draw = &sdl_draw +}; + +uint16_t fnord_buf[] = { + 0x0F00, + 0x00F0, + 0x000F, + 0x00FF, + 0x0F00, + 0x00F0, + 0x000F, + 0x00FF, + 0x0F00, + 0x00F0, + 0x000F, + 0x00FF, + 0x0F00, + 0x00F0, + 0x000F, + 0x00FF, + 0x0F00, + 0x00F0, + 0x000F, + 0x00FF, + 0x0F00, + 0x00F0, + 0x000F, + 0x00FF, + 0x0F00, + 0x00F0, + 0x000F, + 0x00FF, + 0x0F00, + 0x00F0, + 0x000F, + 0x00FF + +}; + +struct image fnord = { + .type = PXTYPE_RGB444, + .size = {8,4}, + .data = &fnord_buf +}; + +uint8_t fubar_img[] = { + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x0f + +}; + +struct image fubar = { + .type = PXTYPE_MONO_V8, + .size = {8,16}, + .data = &fubar_img +}; + +void +sdl_run(void) +{ + int r; + SDL_Event e; + + while((r = SDL_WaitEvent(&e))) { + + if(e.type == SDL_KEYDOWN) { + if(e.key.keysym.sym == SDLK_ESCAPE) { + puts("Bloody quitter!"); + break; + } + if(e.key.keysym.sym == SDLK_SPACE) { + pxposn_t dp = {0,0}; + pxposn_t sp = {0,0}; + pxdims_t d = {8,4}; + + image_blit(&display_sdl_fbuf, dp, + &fnord, sp, + d); + + sp.x = 0; + sp.y = 0; + dp.x = 5; + dp.y = 10; + d.w = 8; + d.h = 16; + + image_blit(&display_sdl_fbuf, dp, + &fubar, sp, + d); + + pxrect_t r = {{12,0},{40,20}}; + image_fill_rect(&display_sdl_fbuf, + r, + 0xFF00FF); + + +#if 0 + dp.x = 0; + dp.y = 0; + + image_draw_string(&display_sdl_fbuf, dp, + "ABCDEFGHI"); + + dp.y += 10; + + image_draw_string(&display_sdl_fbuf, dp, + "abcdefghi"); + +#endif + + sdl_draw(&display_sdl); + + } + } + + switch(e.type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + printf("Key %d %d\n", e.key.keysym.sym, e.key.state); + break; + } + } + + if(!r) { + printf("Failed to wait for SDL event: %s\n", SDL_GetError()); + exit(1); + } + +} + +int +main(void) +{ + sdl_init(&display_sdl, 96, 64, 4); + + sdl_run(); + + return 0; +} diff --git a/src/target/ui-experiment/sdl.h b/src/target/ui-experiment/sdl.h new file mode 100644 index 00000000..5a3d4752 --- /dev/null +++ b/src/target/ui-experiment/sdl.h @@ -0,0 +1,9 @@ + +#ifndef _UI_SDL_H +#define _UI_SDL_H + +#include <ui/display.h> + +extern struct display display_sdl; + +#endif diff --git a/src/target/ui-experiment/ui.h b/src/target/ui-experiment/ui.h new file mode 100644 index 00000000..9bc7c324 --- /dev/null +++ b/src/target/ui-experiment/ui.h @@ -0,0 +1,81 @@ + +/****** MESSAGING MENU ******/ + +struct menu menu_message_compose = { + .title = "Compose", + .help = "Write a new text message." +}; + +struct menu menu_message_inbox = { + .title = "Inbox", + .help = "Incoming text messages" +}; + +struct menu menu_message_outbox = { + .title = "Outbox", + .help = "Outgoing text messages" +}; + +struct menu menu_message_sent = { + .title = "Sent", + .help = "Previously sent text messages" +}; + +struct menu menu_messages = { + .title = "Messages", + .help = "Short message service options", + .children = { + [0] = &menu_message_compose, + [1] = &menu_message_inbox, + [2] = &menu_message_outbox, + [3] = &menu_message_sent + } +}; + +/****** NETWORK MENU ******/ + +struct menu menu_network_about = { + .title = "About this network", + .help = "Information about your current network", +}; + +struct menu menu_network = { + .title = "Network", + .help = "Network interaction options", + .children = { + } +}; + +/****** SETTINGS MENU ******/ + +struct menu menu_settings = { + .title = "Settings", + .help = "Configure your phone", + .children = { + } +}; + +/****** MAIN MENU ******/ + +struct menu menu_about = { + .title = "About", + .help = "Information about this phone", +}; + +struct menu menu_main = { + .title = "Main Menu", + .children = { + [0] = &menu_messages, + [7] = &menu_network, + [8] = &menu_settings, + [9] = &menu_about, + }, +}; + + + +int +main(void) { + &menu_main; + return 0; +}; diff --git a/src/target_dsp/.gitignore b/src/target_dsp/.gitignore new file mode 100644 index 00000000..5cf144ea --- /dev/null +++ b/src/target_dsp/.gitignore @@ -0,0 +1,4 @@ +*.o +*.a +*.coff +*.bin diff --git a/src/target_dsp/calypso/Makefile b/src/target_dsp/calypso/Makefile new file mode 100644 index 00000000..40ee4ec5 --- /dev/null +++ b/src/target_dsp/calypso/Makefile @@ -0,0 +1,15 @@ +all: dsp_dump.bin + +CROSS=tic54x-coff- + +%.o: %.S + $(CROSS)as $< -o $@ + +%.bin: %.coff + $(CROSS)objcopy -j .text -O binary $< $@ + +dsp_dump.coff: bl_stage3.o dsp_dump.lds + $(CROSS)ld --script dsp_dump.lds bl_stage3.o -o $@ + +clean: + rm -f *.o *.bin *.coff diff --git a/src/target_dsp/calypso/bin2cfile.py b/src/target_dsp/calypso/bin2cfile.py new file mode 100755 index 00000000..9456a6ac --- /dev/null +++ b/src/target_dsp/calypso/bin2cfile.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +import struct +import sys + +def group_by_n(s, n, do_join=True): + return ( ''.join(x) for x in zip(*[s[i::n] for i in range(n)]) ) + + +def main(pn, filename): + # Get all bytes + f = open(filename, 'r') + d = f.read() + f.close() + + # Get the data + ops = ''.join([ + '0x%04x,%s' % ( + struct.unpack('=H', x)[0], + '\n\t\t\t' if (i&3==3) else ' ' + ) + for i, x + in enumerate(group_by_n(d, 2)) + ])[:-1] + + ops = '\t\t\t' + ops + if ops[-1] == '\t': + ops = ops[:-4] + + # Name + name = filename.split('.',1)[0] + + # Header / footer + print """ +#define _SA_DECL (const uint16_t *)&(const uint16_t []) + +static const struct dsp_section %s[] = { + { + .addr = 0x, + .size = 0x%04x, + .data = _SA_DECL { +%s + }, + }, + { /* Guard */ + .addr = 0, + .size = 0, + .data = NULL, + }, +}; + +#undef _SA_DECL +""" % (name, len(d)/2, ops) + + +if __name__ == "__main__": + main(*sys.argv) diff --git a/src/target_dsp/calypso/bl_stage3.S b/src/target_dsp/calypso/bl_stage3.S new file mode 100644 index 00000000..402c3c59 --- /dev/null +++ b/src/target_dsp/calypso/bl_stage3.S @@ -0,0 +1,142 @@ + +BCSR .equ 0x29 + +CMD_IDLE .equ 1 ; Do nothing / DSP ready for commands +CMD_COPY_BLOCK .equ 2 ; (if size == 0, then exec) +CMD_COPY_MODE .equ 4 ; Select copy mode + ; (0=code write, 1=data write, + ; 2=code read, 3=data read, + ; 4=prom read, 5=drom read) +CMD_VERSION .equ 0xffff ; API_RAM[0] = bootloader version + +VERSION .equ 0x0100 ; 1.00 + + + .section .apiram + + .org 0x07fc +bl_addr_hi .ds 1 +bl_size .ds 1 +bl_addr_lo .ds 1 +bl_status .ds 1 + + + .text + .mmregs +_start: + orm #2, *(BCSR) ; ? + + ld #0x1f, DP + stm #0x1100, SP + stm #0, AR4 +_done: + stm #_api_ram, AR2 + st #CMD_IDLE, @bl_status +_loop: + ; Version + cmpm @bl_status, #CMD_VERSION + bc 1f, ntc + + bd _done + st #VERSION, *AR2 +1: + + ; Select copy mode + cmpm @bl_status, #CMD_COPY_MODE + bc 1f, ntc + + bd _done + mvdm @_api_ram, AR4 +1: + + ; Copy + cmpm @bl_status, #CMD_COPY_BLOCK + bc _loop, ntc + + ; Capture values for copy operations + ; A = full address + ; AR1 size-1 + ; AR2 api_ram (set previously) + ; AR3 data/code address + ; AR4 mode + + ldu @bl_addr_lo, A + stlm A, AR3 + add @bl_addr_hi, 16, A + + ldu @bl_size, B + stlm B, AR1 + ; mar *AR1- ; We do this in a delay slot later on ... + + ; Start + bc 1f, bneq ; B still contains size + bacc A + +1: + ; Select + stm #AR4, AR5 ; AR5 = &AR4 + bit *AR5, 13 ; Test mode(2) + bcd _read_rom, tc + mar *AR1- + bit *AR5, 15 ; Test mode(0) lsb + bcd _copy_data, tc + bit *AR5, 14 ; Test mode(1) + nop + + ; Copy to/from Program space +_copy_prog: + bc _read_prog, tc + + ; Copy from API -> prog space (mode 0) +_write_prog: + rpt *(AR1) + writa *AR2+ + b _done + + ; Copy from prog space -> API (mode 2) +_read_prog: + rpt *(AR1) + reada *AR2+ + b _done + + ; Copy to/from Data space +_copy_data: + bc _read_data, tc + + ; Copy from API -> data space (mode 1) +_write_data: + rpt *(AR1) + mvdd *AR2+, *AR3+ + b _done + + ; Copy from data space -> API (mode 3) +_read_data: + rpt *(AR1) + mvdd *AR3+, *AR2+ + b _done + + ; Read from {D,P}ROM bypassing protection +_read_rom: + ldm AR1, B ; Can't put those two ops in the delay slot of + stlm B, BRC ; 'bc' because of unprotected pipeline conflicts + bc _read_rom_data, tc + +_read_rom_prog: + rptb 1f - 1 + call prom_read_xplt +1: + b _done + +_read_rom_data: + rptb 1f - 1 + call drom_read_xplt +1: + b _done + + +drom_read_xplt .equ 0xe4b8 +prom_read_xplt .equ 0x7213 + + + .end + diff --git a/src/target_dsp/calypso/dsp_dump.lds b/src/target_dsp/calypso/dsp_dump.lds new file mode 100644 index 00000000..56633026 --- /dev/null +++ b/src/target_dsp/calypso/dsp_dump.lds @@ -0,0 +1,22 @@ +OUTPUT_FORMAT("coff1-c54x") +OUTPUT_ARCH("") +MEMORY +{ + apiram (RWXI) : ORIGIN = 0x0800, LENGTH = 0x2000 +} +SECTIONS +{ + . = 0x0800; + + .apiram : + { + PROVIDE(_api_ram = .); + *(.apiram) + } > apiram + + .text : + { + *(.text) + } > apiram +} + diff --git a/src/target_dsp/calypso/dump2coff.py b/src/target_dsp/calypso/dump2coff.py new file mode 100755 index 00000000..67a49e8e --- /dev/null +++ b/src/target_dsp/calypso/dump2coff.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python + +from collections import namedtuple +import re +import sys +import struct + +DATA = 0 +DATA = 1 + + +class Section(object): + + DATA = 0 + CODE = 1 + + STYP_NOLOAD = 0x0002 + STYP_TEXT = 0x0020 + STYP_DATA = 0x0040 + STYP_BSS = 0x0080 + + def __init__(self, name, type, start, size, data=None): + self.name = name + self.type = type + self.start = start + self.size = size + self.data = data + + @property + def flags(self): + if self.type == Section.DATA: + return Section.STYP_DATA if self.data else Section.STYP_BSS + else: + return Section.STYP_TEXT if self.data else Section.STYP_NOLOAD + + +class CalypsoCOFF(object): + + F_RELFLG = 0x0001 # Relocation information stripped from the file + F_EXEC = 0x0002 # File is executable (i.e., no unresolved external references) + F_LNNO = 0x0004 # Line numbers stripped from the file + F_LSYMS = 0x0010 # Local symbols stripped from the file + F_LITTLE = 0x0100 # Little endian + + def __init__(self, data_seg_base=0x80000000): + self.sections = {} + self.data_seg_base = data_seg_base + self.ver_magic = 0x00c1 + self.tgt_magic = 0x0098 + self.flags = \ + CalypsoCOFF.F_RELFLG | \ + CalypsoCOFF.F_EXEC | \ + CalypsoCOFF.F_LNNO | \ + CalypsoCOFF.F_LSYMS | \ + CalypsoCOFF.F_LITTLE + + def _data_pack(self, d): + return ''.join(struct.pack('<H', x) for x in d) + + def save(self, filename): + # Formats + HDR_FILE = '<HHlllHHH' + HDR_SECTIONS = '<8sLLllllHHHcc' + + # Optional header + oh = '' + + # File header + fh = struct.pack(HDR_FILE, + self.ver_magic, # unsigned short f_ver_magic; /* version magic number */ + len(self.sections), # unsigned short f_nscns; /* number of section */ + 0, # long f_timdat; /* time and date stamp */ + 0, # long f_symptr; /* file ptr to symbol table */ + 0, # long f_nsyms; /* number entries in the sym table */ + len(oh), # unsigned short f_opthdr; /* size of optional header */ + self.flags, # unsigned short f_flags; /* flags */ + self.tgt_magic, # unsigned short f_tgt_magic; /* target magic number */ + ) + + # File header size + #sections * sizeof(section header) + dptr = struct.calcsize(HDR_FILE) + len(oh) + len(self.sections) * struct.calcsize(HDR_SECTIONS) + + # Section headers + sh = [] + sd = [] + + sk = lambda x: self.data_seg_base + x.start if x.type==Section.DATA else x.start + + for s in sorted(self.sections.values(), key=sk): + # Values + if s.type == Section.DATA: + mp = 0x80 + sa = s.start + else: + mp = 0 + sa = s.start + sptr = dptr if s.data else 0 + + # Header + sh.append(struct.pack(HDR_SECTIONS, + s.name, # char[8] s_name; /* 8-character null padded section name */ + sa, # long int s_paddr; /* Physical address of section */ + sa, # long int s_vaddr; /* Virtual address of section */ + s.size, # long int s_size; /* Section size in bytes */ + sptr, # long int s_scnptr; /* File pointer to raw data */ + 0, # long int s_relptr; /* File pointer to relocation entries */ + 0, # long int s_lnnoptr;/* File pointer to line number entries */ + 0, # unsigned short s_nreloc; /* Number of relocation entrie */ + 0, # unsigned short s_nlnno; /* Number of line number entries */ + s.flags,# unsigned short s_flags; /* Flags (see ``Section header flags'') */ + '\x00', # / + chr(mp),# char s_mempage;/* Memory page number */ + )) + + # Data + if s.data: + sd.append(self._data_pack(s.data)) + dptr += s.size * 2 + + # Write the thing + f = open(filename, 'wb') + + f.write(fh) + f.write(oh) + f.write(''.join(sh)) + f.write(''.join(sd)) + + f.close() + + def add_section(self, name, type, addr, size, data=None): + self.sections[name] = Section(name, type, addr, size, data=data) + + +# ---------------------------------------------------------------------------- +# Dump loading +# ---------------------------------------------------------------------------- + +RE_DUMP_HDR = re.compile( + r"^DSP dump: (\w*) \[([0-9a-fA-F]{5})-([0-9a-fA-F]{5})\]$" +) + + +def _file_strip_gen(f): + while True: + l = f.readline() + if not l: + return + yield l.strip() + + +def dump_load_section(fg, sa, ea): + data = [] + ca = sa + for l in fg: + if not l: + break + + ra = int(l[0:5], 16) + if ra != ca: + raise ValueError('Invalid dump address %05x != %05x', ra, ca) + + v = l[8:].split() + if len(v) != 16: + raise ValueError('Invalid dump format') + + v = [int(x,16) for x in v] + data.extend(v) + + ca += 0x10 + + if ca != ea: + raise ValueError('Missing dump data %05x != %05x', ra, ea) + + return data + + +def dump_load(filename): + # Open file + f = open(filename, 'r') + fg = _file_strip_gen(f) + + # Scan line by line for a dump header line + sections = [] + + for l in fg: + m = RE_DUMP_HDR.match(l) + if not m: + continue + + name = m.group(1) + sa = int(m.group(2), 16) + ea = int(m.group(3), 16) + 1 + + sections.append(( + name, sa, ea, + dump_load_section(fg, sa, ea), + )) + + # Done + f.close() + + return sections + + +# ---------------------------------------------------------------------------- +# Main +# ---------------------------------------------------------------------------- + +def main(pname, dump_filename, out_filename): + + # Section to place in the COFF + sections = [ + # name type start size + ('.regs', Section.DATA, 0x00000, 0x0060), + ('.scratch', Section.DATA, 0x00060, 0x0020), + ('.drom', Section.DATA, 0x09000, 0x5000), + ('.pdrom', Section.CODE, 0x0e000, 0x2000), + ('.prom0', Section.CODE, 0x07000, 0x7000), + ('.prom1', Section.CODE, 0x18000, 0x8000), + ('.prom2', Section.CODE, 0x28000, 0x8000), + ('.prom3', Section.CODE, 0x38000, 0x2000), + ('.daram0', Section.DATA, 0x00080, 0x0780), + ('.api', Section.DATA, 0x00800, 0x2000), + ('.daram1', Section.DATA, 0x02800, 0x4800), + ] + + # COFF name -> dump name + dump_mapping = { + # '.regs' : 'Registers', + '.drom' : 'DROM', + '.pdrom' : 'PDROM', + '.prom0' : 'PROM0', + '.prom1' : 'PROM1', + '.prom2' : 'PROM2', + '.prom3' : 'PROM3', + } + + # Load the dump + dump_sections = dict([(s[0], s) for s in dump_load(dump_filename)]) + + # Create the COFF + coff = CalypsoCOFF() + + # Add each section (with data if we have some) + for name, type, start, size in sections: + # Dumped data ? + d_data = None + if (name in dump_mapping) and (dump_mapping[name] in dump_sections): + d_name, d_sa, d_ea, d_data = dump_sections[dump_mapping[name]] + + # Add sections + coff.add_section(name, type, start, size, d_data) + + # Save result + coff.save(out_filename) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(*sys.argv)) diff --git a/src/target_dsp/calypso/ida/README.txt b/src/target_dsp/calypso/ida/README.txt new file mode 100644 index 00000000..a7939083 --- /dev/null +++ b/src/target_dsp/calypso/ida/README.txt @@ -0,0 +1,73 @@ +Here's a few steps to get started quickly and get something readable: + + - Compile a patched for the IDA TMS320C54 module + + I made several enhancement to it to support the calypso better (the tms320c54 + module is part of the SDK and can be modded and recompiled) : + + - Add support for memory mappings so that the same memory zone can + 'appear' at several place in the address space (to handle data & code + overlay) + - Fix the section handling when loading a file: + . to set XPC properly, + . to not override section name + . to support more than 2 sections + - Fix a bug in cross reference detection when dealing with section + having selectors != 0 + - Add stub support for the type system. This allows loading of a .h + header file with the NDB structure definition + - Add definition for the IO ports so that they are symbolically + displayed + + I can't publically distribute the IDA processor module modification + because even just the patch contains some hex-rays code, so I'll handle + this on a case by case basis. (just ask me privately and we'll work it out) + + - Dump the DSP ROM + + Using the compal_dsp_dump.bin, you must create a text dump of the DSP ROM, + just piping the console output to a text file. + + - Generate COFF image + + The dump2coff.py script can convert the text dump into a usable COFF file + containing all the correct sections and addresses. + + - Load this COFF image into IDA + + In the load dialog make sure : + - Uncheck the 'Fill segment gaps (COFF)' checkbox + - Select 'TMS320C54' in 'Change processor' + - In 'Analysis Options/Processor specific analysis options' : + - 'Choose device name': CALYPSO + - 'Data segment address': 0x80000000 + - 'Add mapping' (do it several time) + - From 0x00000060 -> 0x80000060 size 0x6FA0 + - From 0x00010060 -> 0x80000060 size 0x6FA0 + - From 0x00020060 -> 0x80000060 size 0x6FA0 + - From 0x00030060 -> 0x80000060 size 0x6FA0 + - From 0x8000E000 -> 0x0000E000 size 0x2000 + + - Set 'stub' compiler options to allow the type system to load .h files + + In 'Options/Compiler': + - Compiler: 'GNU C++' + - Calling convention: 'Cdecl' + - Memory model: 'Code Near, Data Near' + - Pointer size: 'Near 16bit, Far 32bit' + - Include directory: '/usr/include' (or a directory with your includes + ... needs to exist) + + - Load the NDB types + + - Load the ndb.h file + - In the local types view, import all structure / enum into the database + - Then declare the following symbol and set them as struct type + appropriately. + + 0x80000800 api_w_page_0 db_mcu_to_dsp + 0x80000814 api_w_page_1 db_mcu_to_dsp + 0x80000828 api_r_page_0 db_dsp_to_mcu + 0x8000083c api_r_page_1 db_dsp_to_mcu + 0x800008d4 ndb ndb_mcu_dsp + diff --git a/src/target_dsp/calypso/ida/ndb.h b/src/target_dsp/calypso/ida/ndb.h new file mode 100644 index 00000000..ad9c1056 --- /dev/null +++ b/src/target_dsp/calypso/ida/ndb.h @@ -0,0 +1,294 @@ +typedef unsigned char API; +typedef signed char API_SIGNED; + +struct db_mcu_to_dsp +{ + API d_task_d; + API d_burst_d; + API d_task_u; + API d_burst_u; + API d_task_md; + API d_background; + API d_debug; + API d_task_ra; + API d_fn; + API d_ctrl_tch; + API hole; + API d_ctrl_abb; + API a_a5fn[2]; + API d_power_ctl; + API d_afc; + API d_ctrl_system; +}; + +struct db_dsp_to_mcu +{ + API d_task_d; + API d_burst_d; + API d_task_u; + API d_burst_u; + API d_task_md; + API d_background; + API d_debug; + API d_task_ra; + API a_serv_demod[4]; + API a_pm[3]; + API a_sch[5]; +}; + +struct param_mcu_dsp +{ + API_SIGNED d_transfer_rate; + API_SIGNED d_lat_mcu_bridge; + API_SIGNED d_lat_mcu_hom2sam; + API_SIGNED d_lat_mcu_bef_fast_access; + API_SIGNED d_lat_dsp_after_sam; + API_SIGNED d_gprs_install_address; + API_SIGNED d_misc_config; + API_SIGNED d_cn_sw_workaround; + API_SIGNED d_hole2_param[4]; + API_SIGNED d_fb_margin_beg; + API_SIGNED d_fb_margin_end; + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + API_SIGNED d_v42b_switch_hyst; + API_SIGNED d_v42b_switch_min; + API_SIGNED d_v42b_switch_max; + API_SIGNED d_v42b_reset_delay; + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + API_SIGNED d_sd_min_thr_tchefs; + API_SIGNED d_ma_min_thr_tchefs; + API_SIGNED d_md_max_thr_tchefs; + API_SIGNED d_md1_max_thr_tchefs; + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + API_SIGNED d_facch_thr; + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + API_SIGNED d_gsm_bgd_mgt; + API a_fir_holes[4]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; +}; + +struct ndb_mcu_dsp +{ + API d_dsp_page; + API d_error_status; + API d_spcx_rif; + API d_tch_mode; + API d_debug1; + API d_dsp_test; + API d_version_number1; + API d_version_number2; + API d_debug_ptr; + API d_debug_bk; + API d_pll_config; + API p_debug_buffer; + API d_debug_buffer_size; + API d_debug_trace_type; + API d_dsp_state; + API d_hole1_ndb[2]; + API d_hole_debug_amr; + API d_hole2_ndb[1]; + API d_mcsi_select; + API d_apcdel1_bis; + API d_apcdel2_bis; + API d_apcdel2; + API d_vbctrl2; + API d_bulgcal; + API d_afcctladd; + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + API d_vbctrl1; + API d_bbctrl; + API d_fb_det; + API d_fb_mode; + API a_sync_demod[4]; + API a_sch26[5]; + API d_audio_gain_ul; + API d_audio_gain_dl; + API d_audio_compressor_ctrl; + API d_audio_init; + API d_audio_status; + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + API d_shiftdl; + API d_shiftul; + API d_aec_ctrl; + API d_es_level_api; + API d_mu_api; + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + API d_melody_selection; + API a_melo_holes[3]; + API d_sr_status; + API d_sr_param; + API d_sr_bit_exact_test; + API d_sr_nb_words; + API d_sr_db_level; + API d_sr_db_noise; + API d_sr_mod_size; + API a_n_best_words[4]; + API a_n_best_score[8]; + API a_dd_1[22]; + API a_du_1[22]; + API d_v42b_nego0; + API d_v42b_nego1; + API d_v42b_control; + API d_v42b_ratio_ind; + API d_mcu_control; + API d_mcu_control_sema; + API d_background_enable; + API d_background_abort; + API d_background_state; + API d_max_background; + API a_background_tasks[16]; + API a_back_task_io[16]; + API d_gea_mode_ovly; + API a_gea_kc_ovly[4]; + API d_hole3_ndb[7]; + API d_thr_usf_detect; + API d_a5mode; + API d_sched_mode_gprs_ovly; + API d_hole4_ndb[5]; + API a_ramp[16]; + API a_cd[15]; + API a_fd[15]; + API a_dd_0[22]; + API a_cu[15]; + API a_fu[15]; + API a_du_0[22]; + API d_rach; + API a_kc[4]; + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + API a_tty_holes[8]; + API a_sr_holes0[414]; + API a_new_aec_holes[12]; + // API a_sr_holes1[145]; + struct param_mcu_dsp params; + API d_cport_init; + API d_cport_ctrl; + API a_cport_cfr[2]; + API d_cport_tcl_tadt; + API d_cport_tdat; + API d_cport_tvs; + API d_cport_status; + API d_cport_reg_value; + API a_cport_holes[1011]; + API a_model[1041]; + API a_eotd_holes[22]; + API a_amr_config[4]; + API a_ratscch_ul[6]; + API a_ratscch_dl[6]; + API d_amr_snr_est; + API a_voice_memo_amr_holes[1]; + API d_thr_onset_afs; + API d_thr_sid_first_afs; + API d_thr_ratscch_afs; + API d_thr_update_afs; + API d_thr_onset_ahs; + API d_thr_sid_ahs; + API d_thr_ratscch_marker; + API d_thr_sp_dgr; + API d_thr_soft_bits; + API d_holes[61]; +}; + +enum dsp_error { + DSP_ERR_RHEA = 0x0001, + DSP_ERR_IQ_SAMPLES = 0x0004, + DSP_ERR_DMA_PROG = 0x0008, + DSP_ERR_DMA_TASK = 0x0010, + DSP_ERR_DMA_PEND = 0x0020, + DSP_ERR_VM = 0x0080, + DSP_ERR_DMA_UL_TASK = 0x0100, + DSP_ERR_DMA_UL_PROG = 0x0200, + DSP_ERR_DMA_UL_PEND = 0x0400, + DSP_ERR_STACK_OV = 0x0800, +}; diff --git a/src/target_dsp/calypso/ida/tms320c54.cfg b/src/target_dsp/calypso/ida/tms320c54.cfg new file mode 100644 index 00000000..7962bee2 --- /dev/null +++ b/src/target_dsp/calypso/ida/tms320c54.cfg @@ -0,0 +1,136 @@ +; Append this to the tms320c54.cfg shipped with IDA
+
+.CALYPSO
+
+; entry _reset 0xff80 Reset vector
+
+; RIF
+RIF_DXR 0x0000
+RIF_DRR 0x0001
+RIF_SPCX 0x0002
+RIF_SPCR 0x0003
+
+; CYPHER
+CYPHER_CNTL 0x2800
+CYPHER_CNTL.START 0
+CYPHER_CNTL.RESETSW 1
+CYPHER_CNTL.MODE0 2
+CYPHER_CNTL.MODE1 3
+CYPHER_CNTL.CLK_EN 4
+CYPHER_CNTL.CYPHER_ONLY 5
+
+CYPHER_STATUS_IRQ 0x2801
+CYPHER_STATUS_IRQ.LT_FIN 0
+
+CYPHER_STATUS_WORK 0x2802
+CYPHER_STATUS_WORK.WORKING 0
+
+CYPHER_KC_1 0x2803
+CYPHER_KC_2 0x2804
+CYPHER_KC_3 0x2805
+CYPHER_KC_4 0x2806
+CYPHER_COUNT_1 0x2807
+CYPHER_COUNT_2 0x2808
+CYPHER_DECI_1 0x2809
+CYPHER_DECI_2 0x280A
+CYPHER_DECI_3 0x280B
+CYPHER_DECI_4 0x280C
+CYPHER_DECI_5 0x280D
+CYPHER_DECI_6 0x280E
+CYPHER_DECI_7 0x280F
+CYPHER_DECI_8 0x2810
+CYPHER_ENCI_1 0x2811
+CYPHER_ENCI_2 0x2812
+CYPHER_ENCI_3 0x2813
+CYPHER_ENCI_4 0x2814
+CYPHER_ENCI_5 0x2815
+CYPHER_ENCI_6 0x2816
+CYPHER_ENCI_7 0x2817
+CYPHER_ENCI_8 0x2818
+
+; MCSI
+MCSI_CONTROL 0x0800
+MCSI_MAIN-PARAMETERS 0x0801
+MCSI_INTERRUPTS 0x0802
+MCSI_CHANNEL-USED 0x0803
+MCSI_OVER-CLK 0x0804
+MCSI_CLK-FREQ 0x0805
+MCSI_STATUS 0x0806
+MCSI_TX0 0x0820
+MCSI_TX1 0x0821
+MCSI_TX2 0x0822
+MCSI_TX3 0x0823
+MCSI_TX4 0x0824
+MCSI_TX5 0x0825
+MCSI_TX6 0x0826
+MCSI_TX7 0x0827
+MCSI_TX8 0x0828
+MCSI_TX9 0x0829
+MCSI_TX10 0x082A
+MCSI_TX11 0x082B
+MCSI_TX12 0x082C
+MCSI_TX13 0x082D
+MCSI_TX14 0x082E
+MCSI_TX15 0x082F
+MCSI_RX0 0x0830
+MCSI_RX1 0x0831
+MCSI_RX2 0x0832
+MCSI_RX3 0x0833
+MCSI_RX4 0x0834
+MCSI_RX5 0x0835
+MCSI_RX6 0x0836
+MCSI_RX7 0x0837
+MCSI_RX8 0x0838
+MCSI_RX9 0x0839
+MCSI_RX10 0x083A
+MCSI_RX11 0x083B
+MCSI_RX12 0x083C
+MCSI_RX13 0x083D
+MCSI_RX14 0x083E
+MCSI_RX15 0x083F
+
+; RHEA
+RHEA_TRANSFER_RATE 0xF800
+
+RHEA_BRIDGE-CTRL 0xF801
+RHEA_BRIDGE-CTRL.TIMEOUT_ENABLE 8
+RHEA_BRIDGE-CTRL.NSUPV 9
+
+; API
+API_CONF 0xF900
+API_CONF.RESERVED0 0
+API_CONF.API_HOM 1
+API_CONF.BRIDGE_CLK_EN 2
+
+; Interrupts
+INT_CNTRL 0xFA00
+INT_CLEAR 0xFA01
+
+; DMA
+DMA_CONTROLLER_CONF 0xFC00
+DMA_ALLOC_CONFIG 0xFC02
+DMA1_RAD 0xFC10
+DMA1_RDPTH 0xFC12
+DMA1_AAD 0xFC14
+DMA1_ALGTH 0xFC16
+DMA1_CTRL 0xFC18
+DMA1_CUR_OFFSET_API 0xFC1A
+DMA2_RAD 0xFC20
+DMA2_RDPTH 0xFC22
+DMA2_AAD 0xFC24
+DMA2_ALGTH 0xFC26
+DMA2_CTRL 0xFC28
+DMA2_CUR_OFFSET_API 0xFC2A
+DMA3_RAD 0xFC30
+DMA3_RDPTH 0xFC32
+DMA3_AAD 0xFC34
+DMA3_ALGTH 0xFC36
+DMA3_CTRL 0xFC38
+DMA3_CUR_OFFSET_API 0xFC3A
+DMA4_RAD 0xFC40
+DMA4_RDPTH 0xFC42
+DMA4_AAD 0xFC44
+DMA4_ALGTH 0xFC46
+DMA4_CTRL 0xFC48
+DMA4_CUR_OFFSET_API 0xFC4A
+
diff --git a/src/wireshark/gsmtap.patch b/src/wireshark/gsmtap.patch new file mode 100644 index 00000000..019c8b49 --- /dev/null +++ b/src/wireshark/gsmtap.patch @@ -0,0 +1,445 @@ +Index: epan/dissectors/packet-gsmtap.c +=================================================================== +--- /dev/null ++++ epan/dissectors/packet-gsmtap.c +@@ -0,0 +1,345 @@ ++/* packet-gsmtap.c ++ * Routines for GSMTAP captures ++ * ++ * (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> ++ * ++ * $Id$ ++ * ++ * Wireshark - Network traffic analyzer ++ * By Gerald Combs <gerald@wireshark.org> ++ * Copyright 1998 Gerald Combs ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ++ * 02111-1307, USA. ++ * ++ */ ++ ++/* GSMTAP is a generic header format for GSM protocol captures, ++ * it uses the IANA-assigned UDP port number 4729 and carries ++ * payload in various formats of GSM interfaces such as Um MAC ++ * blocks or Um bursts. ++ * ++ * Example programs generating GSMTAP data are airprobe ++ * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/) ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include <glib.h> ++#include <epan/packet.h> ++#include <epan/prefs.h> ++ ++#include "packet-gsmtap.h" ++ ++static int proto_gsmtap = -1; ++ ++static int hf_gsmtap_version = -1; ++static int hf_gsmtap_hdrlen = -1; ++static int hf_gsmtap_type = -1; ++static int hf_gsmtap_timeslot = -1; ++static int hf_gsmtap_subslot = -1; ++static int hf_gsmtap_arfcn = -1; ++static int hf_gsmtap_uplink = -1; ++static int hf_gsmtap_noise_dbm = -1; ++static int hf_gsmtap_signal_dbm = -1; ++static int hf_gsmtap_frame_nr = -1; ++static int hf_gsmtap_burst_type = -1; ++static int hf_gsmtap_channel_type = -1; ++static int hf_gsmtap_antenna = -1; ++ ++static int hf_sacch_l1h_power_lev = -1; ++static int hf_sacch_l1h_fpc = -1; ++static int hf_sacch_l1h_ta = -1; ++ ++static gint ett_gsmtap = -1; ++ ++enum { ++ GSMTAP_SUB_DATA = 0, ++ GSMTAP_SUB_UM, ++ GSMTAP_SUB_UM_LAPDM, ++ GSMTAP_SUB_ABIS, ++ ++ GSMTAP_SUB_MAX ++}; ++ ++static dissector_handle_t sub_handles[GSMTAP_SUB_MAX]; ++ ++static const value_string gsmtap_bursts[] = { ++ { GSMTAP_BURST_UNKNOWN, "UNKNOWN" }, ++ { GSMTAP_BURST_FCCH, "FCCH" }, ++ { GSMTAP_BURST_PARTIAL_SCH, "PARTIAL SCH" }, ++ { GSMTAP_BURST_SCH, "SCH" }, ++ { GSMTAP_BURST_CTS_SCH, "CTS SCH" }, ++ { GSMTAP_BURST_COMPACT_SCH, "COMPACT SCH" }, ++ { GSMTAP_BURST_NORMAL, "NORMAL" }, ++ { GSMTAP_BURST_DUMMY, "DUMMY" }, ++ { GSMTAP_BURST_ACCESS, "RACH" }, ++ { 0, NULL }, ++}; ++ ++static const value_string gsmtap_channels[] = { ++ { GSMTAP_CHANNEL_UNKNOWN, "UNKNOWN" }, ++ { GSMTAP_CHANNEL_BCCH, "BCCH" }, ++ { GSMTAP_CHANNEL_CCCH, "CCCH" }, ++ { GSMTAP_CHANNEL_RACH, "RACH" }, ++ { GSMTAP_CHANNEL_AGCH, "AGCH" }, ++ { GSMTAP_CHANNEL_PCH, "PCH" }, ++ { GSMTAP_CHANNEL_SDCCH, "SDCCH" }, ++ { GSMTAP_CHANNEL_SDCCH4, "SDCCH/4" }, ++ { GSMTAP_CHANNEL_SDCCH8, "SDCCH/8" }, ++ { GSMTAP_CHANNEL_TCH_F, "FACCH/F" }, ++ { GSMTAP_CHANNEL_TCH_H, "FACCH/H" }, ++ { GSMTAP_CHANNEL_ACCH| ++ GSMTAP_CHANNEL_SDCCH, "LSACCH" }, ++ { GSMTAP_CHANNEL_ACCH| ++ GSMTAP_CHANNEL_SDCCH4, "SACCH/4" }, ++ { GSMTAP_CHANNEL_ACCH| ++ GSMTAP_CHANNEL_SDCCH8, "SACCH/8" }, ++ { GSMTAP_CHANNEL_ACCH| ++ GSMTAP_CHANNEL_TCH_F, "SACCH/F" }, ++ { GSMTAP_CHANNEL_ACCH| ++ GSMTAP_CHANNEL_TCH_F, "SACCH/H" }, ++ { 0, NULL }, ++}; ++ ++static const value_string gsmtap_types[] = { ++ { GSMTAP_TYPE_UM, "GSM Um (MS<->BTS)" }, ++ { GSMTAP_TYPE_ABIS, "GSM Abis (BTS<->BSC)" }, ++ { GSMTAP_TYPE_UM_BURST, "GSM Um burst (MS<->BTS)" }, ++ { 0, NULL }, ++}; ++ ++/* dissect a SACCH L1 header which is included in the first 2 bytes ++ * of every SACCH frame (according to TS 04.04) */ ++static void ++dissect_sacch_l1h(tvbuff_t *tvb, proto_tree *tree) ++{ ++ proto_item *ti; ++ proto_tree *l1h_tree = NULL; ++ ++ if (!tree) ++ return; ++ ++ ti = proto_tree_add_protocol_format(tree, proto_gsmtap, tvb, 0, 2, ++ "SACCH L1 Header, Power Level: %u, Timing Advance: %u", ++ tvb_get_guint8(tvb, 0) & 0x1f, ++ tvb_get_guint8(tvb, 1)); ++ l1h_tree = proto_item_add_subtree(ti, ett_gsmtap); ++ /* Power Level */ ++ proto_tree_add_item(l1h_tree, hf_sacch_l1h_power_lev, tvb, 0, 1, FALSE); ++ /* Fast Power Control */ ++ proto_tree_add_item(l1h_tree, hf_sacch_l1h_fpc, tvb, 0, 1, FALSE); ++ /* Acutal Timing Advance */ ++ proto_tree_add_item(l1h_tree, hf_sacch_l1h_ta, tvb, 1, 1, FALSE); ++} ++ ++/* dissect a GSMTAP header and hand payload off to respective dissector */ ++static void ++dissect_gsmtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) ++{ ++ int sub_handle, len, offset = 0; ++ proto_item *ti; ++ proto_tree *gsmtap_tree = NULL; ++ tvbuff_t *payload_tvb, *l1h_tvb = NULL; ++ guint8 hdr_len, type, sub_type; ++ guint16 arfcn; ++ ++ len = tvb_length(tvb); ++ ++ hdr_len = tvb_get_guint8(tvb, offset + 1) <<2; ++ type = tvb_get_guint8(tvb, offset + 2); ++ sub_type = tvb_get_guint8(tvb, offset + 12); ++ arfcn = tvb_get_ntohs(tvb, offset + 4); ++ ++ /* In case of a SACCH, there is a two-byte L1 header in front ++ * of the packet (see TS 04.04) */ ++ if (type == GSMTAP_TYPE_UM && ++ sub_type & GSMTAP_CHANNEL_ACCH) { ++ l1h_tvb = tvb_new_subset(tvb, hdr_len, 2, 2); ++ payload_tvb = tvb_new_subset(tvb, hdr_len+2, len-(hdr_len+2), ++ len-(hdr_len+2)); ++ } else { ++ payload_tvb = tvb_new_subset(tvb, hdr_len, len-hdr_len, ++ len-hdr_len); ++ } ++ ++ /* We don't want any UDP related info left in the INFO field, as the ++ * gsm_a_dtap dissector will not clear but only append */ ++ col_clear(pinfo->cinfo, COL_INFO); ++ ++ col_set_str(pinfo->cinfo, COL_PROTOCOL, "GSMTAP"); ++ ++ if (arfcn & GSMTAP_ARFCN_F_UPLINK) { ++ col_append_str(pinfo->cinfo, COL_RES_NET_SRC, "MS"); ++ col_append_str(pinfo->cinfo, COL_RES_NET_DST, "BTS"); ++ /* p2p_dir is used by the LAPDm dissector */ ++ pinfo->p2p_dir = P2P_DIR_SENT; ++ } else { ++ col_set_str(pinfo->cinfo, COL_RES_NET_SRC, "BTS"); ++ switch (sub_type & ~GSMTAP_CHANNEL_ACCH) { ++ case GSMTAP_CHANNEL_BCCH: ++ case GSMTAP_CHANNEL_CCCH: ++ case GSMTAP_CHANNEL_PCH: ++ case GSMTAP_CHANNEL_AGCH: ++ col_set_str(pinfo->cinfo, COL_RES_NET_DST, "Broadcast"); ++ break; ++ default: ++ col_set_str(pinfo->cinfo, COL_RES_NET_DST, "MS"); ++ break; ++ } ++ /* p2p_dir is used by the LAPDm dissector */ ++ pinfo->p2p_dir = P2P_DIR_RECV; ++ } ++ ++ if (tree) { ++ ti = proto_tree_add_protocol_format(tree, proto_gsmtap, tvb, 0, hdr_len, ++ "GSM TAP Header, ARFCN: %u (%s), TS: %u, Channel: %s (%u)", ++ arfcn & GSMTAP_ARFCN_MASK, ++ arfcn & GSMTAP_ARFCN_F_UPLINK ? "Uplink" : "Downlink", ++ tvb_get_guint8(tvb, offset+3), ++ match_strval(tvb_get_guint8(tvb, offset+12), gsmtap_channels), ++ tvb_get_guint8(tvb, offset+14)); ++ gsmtap_tree = proto_item_add_subtree(ti, ett_gsmtap); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_version, ++ tvb, offset, 1, FALSE); ++ proto_tree_add_uint_format(gsmtap_tree, hf_gsmtap_hdrlen, ++ tvb, offset+1, 1, hdr_len, ++ "Header length: %u bytes", hdr_len); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_type, ++ tvb, offset+2, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_timeslot, ++ tvb, offset+3, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_arfcn, ++ tvb, offset+4, 2, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_uplink, ++ tvb, offset+4, 2, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_noise_dbm, ++ tvb, offset+6, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_signal_dbm, ++ tvb, offset+7, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_frame_nr, ++ tvb, offset+8, 4, FALSE); ++ if (type == GSMTAP_TYPE_UM_BURST) ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_burst_type, ++ tvb, offset+12, 1, FALSE); ++ else if (type == GSMTAP_TYPE_UM) ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_channel_type, ++ tvb, offset+12, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_antenna, ++ tvb, offset+13, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_subslot, ++ tvb, offset+14, 1, FALSE); ++ } ++ ++ switch (type) { ++ case GSMTAP_TYPE_UM: ++ if (l1h_tvb) ++ dissect_sacch_l1h(l1h_tvb, tree); ++ switch (sub_type & ~GSMTAP_CHANNEL_ACCH) { ++ case GSMTAP_CHANNEL_BCCH: ++ case GSMTAP_CHANNEL_CCCH: ++ case GSMTAP_CHANNEL_PCH: ++ case GSMTAP_CHANNEL_AGCH: ++ /* FIXME: we might want to skip idle frames */ ++ sub_handle = GSMTAP_SUB_UM; ++ break; ++ case GSMTAP_CHANNEL_SDCCH: ++ case GSMTAP_CHANNEL_SDCCH4: ++ case GSMTAP_CHANNEL_SDCCH8: ++ case GSMTAP_CHANNEL_TCH_F: ++ case GSMTAP_CHANNEL_TCH_H: ++ sub_handle = GSMTAP_SUB_UM_LAPDM; ++ break; ++ case GSMTAP_CHANNEL_RACH: ++ default: ++ sub_handle = GSMTAP_SUB_DATA; ++ break; ++ } ++ break; ++ case GSMTAP_TYPE_UM_BURST: ++ default: ++ sub_handle = GSMTAP_SUB_DATA; ++ break; ++ } ++ call_dissector(sub_handles[sub_handle], payload_tvb, pinfo, tree); ++} ++ ++static const true_false_string sacch_l1h_fpc_mode_vals = { ++ "In use", ++ "Not in use" ++}; ++ ++void ++proto_register_gsmtap(void) ++{ ++ static hf_register_info hf[] = { ++ { &hf_gsmtap_version, { "Version", "gsmtap.version", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_hdrlen, { "Header Length", "gsmtap.hdr_len", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_type, { "Payload Type", "gsmtap.type", ++ FT_UINT8, BASE_DEC, VALS(gsmtap_types), 0, NULL, HFILL } }, ++ { &hf_gsmtap_timeslot, { "Time Slot", "gsmtap.ts", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_arfcn, { "ARFCN", "gsmtap.arfcn", ++ FT_UINT16, BASE_DEC, NULL, GSMTAP_ARFCN_MASK, NULL, HFILL } }, ++ { &hf_gsmtap_uplink, { "Uplink", "gsmtap.uplink", ++ FT_UINT16, BASE_DEC, NULL, GSMTAP_ARFCN_F_UPLINK, NULL, HFILL } }, ++ { &hf_gsmtap_noise_dbm, { "Signal/Noise Ratio (dB)", "gsmtap.snr_db", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_signal_dbm, { "Signal Level (dBm)", "gsmtap.signal_dbm", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_frame_nr, { "GSM Frame Number", "gsmtap.frame_nr", ++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_burst_type, { "Burst Type", "gsmtap.burst_type", ++ FT_UINT8, BASE_DEC, VALS(gsmtap_bursts), 0, NULL, HFILL }}, ++ { &hf_gsmtap_channel_type, { "Channel Type", "gsmtap.chan_type", ++ FT_UINT8, BASE_DEC, VALS(gsmtap_channels), 0, NULL, HFILL }}, ++ { &hf_gsmtap_antenna, { "Antenna Number", "gsmtap.antenna", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_subslot, { "Sub-Slot", "gsmtap.sub_slot", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ ++ { &hf_sacch_l1h_power_lev, { "MS power level", "gsmtap.sacch_l1.power_lev", ++ FT_UINT8, BASE_DEC, NULL, 0x1f, NULL, HFILL } }, ++ { &hf_sacch_l1h_fpc, { "FPC", "gsmtap.sacch_l1.fpc", ++ FT_BOOLEAN, 8, TFS(&sacch_l1h_fpc_mode_vals), 0x04, ++ NULL, HFILL } }, ++ { &hf_sacch_l1h_ta, { "Actual Timing Advance", "gsmtap.sacch_l1.ta", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ }; ++ static gint *ett[] = { ++ &ett_gsmtap ++ }; ++ ++ proto_gsmtap = proto_register_protocol("GSM Radiotap", "GSMTAP", "gsmtap"); ++ proto_register_field_array(proto_gsmtap, hf, array_length(hf)); ++ proto_register_subtree_array(ett, array_length(ett)); ++} ++ ++void ++proto_reg_handoff_gsmtap(void) ++{ ++ dissector_handle_t gsmtap_handle; ++ ++ sub_handles[GSMTAP_SUB_DATA] = find_dissector("data"); ++ sub_handles[GSMTAP_SUB_UM] = find_dissector("gsm_a_ccch"); ++ sub_handles[GSMTAP_SUB_UM_LAPDM] = find_dissector("lapdm"); ++ sub_handles[GSMTAP_SUB_ABIS] = find_dissector("gsm_a_dtap"); ++ gsmtap_handle = create_dissector_handle(dissect_gsmtap, proto_gsmtap); ++ dissector_add("udp.port", GSMTAP_UDP_PORT, gsmtap_handle); ++} +Index: epan/dissectors/packet-gsmtap.h +=================================================================== +--- /dev/null ++++ epan/dissectors/packet-gsmtap.h +@@ -0,0 +1,70 @@ ++#ifndef _GSMTAP_H ++#define _GSMTAP_H ++ ++/* gsmtap header, pseudo-header in front of the actua/ GSM payload */ ++ ++/* GSMTAP is a generic header format for GSM protocol captures, ++ * it uses the IANA-assigned UDP port number 4729 and carries ++ * payload in various formats of GSM interfaces such as Um MAC ++ * blocks or Um bursts. ++ * ++ * Example programs generating GSMTAP data are airprobe ++ * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/) ++ */ ++ ++#define GSMTAP_TYPE_UM 0x01 ++#define GSMTAP_TYPE_ABIS 0x02 ++#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */ ++ ++#define GSMTAP_BURST_UNKNOWN 0x00 ++#define GSMTAP_BURST_FCCH 0x01 ++#define GSMTAP_BURST_PARTIAL_SCH 0x02 ++#define GSMTAP_BURST_SCH 0x03 ++#define GSMTAP_BURST_CTS_SCH 0x04 ++#define GSMTAP_BURST_COMPACT_SCH 0x05 ++#define GSMTAP_BURST_NORMAL 0x06 ++#define GSMTAP_BURST_DUMMY 0x07 ++#define GSMTAP_BURST_ACCESS 0x08 ++#define GSMTAP_BURST_NONE 0x09 ++ ++#define GSMTAP_CHANNEL_UNKNOWN 0x00 ++#define GSMTAP_CHANNEL_BCCH 0x01 ++#define GSMTAP_CHANNEL_CCCH 0x02 ++#define GSMTAP_CHANNEL_RACH 0x03 ++#define GSMTAP_CHANNEL_AGCH 0x04 ++#define GSMTAP_CHANNEL_PCH 0x05 ++#define GSMTAP_CHANNEL_SDCCH 0x06 ++#define GSMTAP_CHANNEL_SDCCH4 0x07 ++#define GSMTAP_CHANNEL_SDCCH8 0x08 ++#define GSMTAP_CHANNEL_TCH_F 0x09 ++#define GSMTAP_CHANNEL_TCH_H 0x0a ++#define GSMTAP_CHANNEL_ACCH 0x80 ++ ++#define GSMTAP_ARFCN_F_PCS 0x8000 ++#define GSMTAP_ARFCN_F_UPLINK 0x4000 ++#define GSMTAP_ARFCN_MASK 0x3fff ++ ++#define GSMTAP_UDP_PORT 4729 ++ ++/* This is the header as it is used by gsmtap-generating software. ++ * It is not used by the wireshark dissector and provided for reference only. ++struct gsmtap_hdr { ++ guint8 version; // version, set to 0x01 currently ++ guint8 hdr_len; // length in number of 32bit words ++ guint8 type; // see GSMTAP_TYPE_* ++ guint8 timeslot; // timeslot (0..7 on Um) ++ ++ guint16 arfcn; // ARFCN (frequency) ++ gint8 signal_dbm; // signal level in dBm ++ gint8 snr_db; // signal/noise ratio in dB ++ ++ guint32 frame_number; // GSM Frame Number (FN) ++ ++ guint8 sub_type; // Type of burst/channel, see above ++ guint8 antenna_nr; // Antenna Number ++ guint8 sub_slot; // sub-slot within timeslot ++ guint8 res; // reserved for future use (RFU) ++} ++ */ ++ ++#endif /* _GSMTAP_H */ +Index: epan/dissectors/Makefile.common +=================================================================== +--- epan/dissectors/Makefile.common.orig ++++ epan/dissectors/Makefile.common +@@ -484,6 +484,7 @@ + packet-gsm_sms.c \ + packet-gsm_sms_ud.c \ + packet-gsm_um.c \ ++ packet-gsmtap.c \ + packet-gssapi.c \ + packet-gtp.c \ + packet-gtpv2.c \ +@@ -1072,6 +1073,7 @@ + packet-gsm_a_common.h \ + packet-gsm_map.h \ + packet-gsm_sms.h \ ++ packet-gsmtap.h \ + packet-gssapi.h \ + packet-gtp.h \ + packet-h223.h \ diff --git a/src/wireshark/smscb.patch b/src/wireshark/smscb.patch new file mode 100644 index 00000000..7cfbd5d9 --- /dev/null +++ b/src/wireshark/smscb.patch @@ -0,0 +1,507 @@ +Wireshark patch for SMSCB dissection support in LAPDm + +Create a new gsm_smscb dissector module for SMSCB as defined in GSM TS +04.12. Call it from packet-lapdm when the Link Protocol Discriminator +has the value "0 1". + +The dissector supports reassembly of SMSCB Message blocks. Schedule +block reassmebly or dissection is not yet implemented. + +Signed-off-by: Alex Badea <vamposdecampos@gmail.com> +--- + Makefile.common | 1 + packet-gsm_smscb.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + packet-lapdm.c | 18 +- + 3 files changed, 420 insertions(+), 3 deletions(-) + +diff --git a/epan/dissectors/Makefile.common b/epan/dissectors/Makefile.common +index 1c1e60b..86bc856 100644 +--- a/epan/dissectors/Makefile.common ++++ b/epan/dissectors/Makefile.common +@@ -500,6 +500,7 @@ DISSECTOR_SRC = \ + packet-gsm_bssmap_le.c \ + packet-gsm_sms.c \ + packet-gsm_sms_ud.c \ ++ packet-gsm_smscb.c \ + packet-gsm_um.c \ + packet-gsmtap.c \ + packet-gssapi.c \ +diff --git a/epan/dissectors/packet-gsm_smscb.c b/epan/dissectors/packet-gsm_smscb.c +new file mode 100644 +index 0000000..c591564 +--- /dev/null ++++ b/epan/dissectors/packet-gsm_smscb.c +@@ -0,0 +1,404 @@ ++/* packet-gsm_smscb.c ++ * Routines for GSM SMSCB (GSM 04.12) dissection ++ * Copyright 2010, Alex Badea <vamposdecampos@gmail.com> ++ * ++ * $Id$ ++ * ++ * Wireshark - Network traffic analyzer ++ * By Gerald Combs <gerald@wireshark.org> ++ * Copyright 1998 Gerald Combs ++ * ++ * Copied from WHATEVER_FILE_YOU_USED (where "WHATEVER_FILE_YOU_USED" ++ * is a dissector file; if you just copied this from README.developer, ++ * don't bother with the "Copied from" - you don't even need to put ++ * in a "Copied from" if you copied an existing dissector, especially ++ * if the bulk of the code in the new dissector is your code) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include <glib.h> ++ ++#include <epan/packet.h> ++#include <epan/prefs.h> ++#include <epan/reassemble.h> ++#include <epan/asn1.h> ++#include "packet-gsm_map.h" ++#include "packet-gsm_sms.h" ++ ++static gint proto_gsm_smscb = -1; ++static gint hf_smscb_addr = -1; ++static gint hf_smscb_addr_lb = -1; ++static gint hf_smscb_addr_seq = -1; ++static gint hf_smscb_serial_gs = -1; ++static gint hf_smscb_serial_mcode = -1; ++static gint hf_smscb_serial_updnum = -1; ++static gint hf_smscb_page_num = -1; ++static gint hf_smscb_page_cnt = -1; ++static gint hf_smscb_msgid = -1; ++static gint hf_smscb_content = -1; ++static gint hf_smscb_fragments = -1; ++static gint hf_smscb_fragment = -1; ++static gint hf_smscb_fragment_overlap = -1; ++static gint hf_smscb_fragment_overlap_conflicts = -1; ++static gint hf_smscb_fragment_multiple_tails = -1; ++static gint hf_smscb_fragment_too_long_fragment = -1; ++static gint hf_smscb_fragment_error = -1; ++static gint hf_smscb_reassembled_in = -1; ++static gint hf_smscb_reassembled_length = -1; ++ ++static gint ett_smscb = -1; ++static gint ett_smscb_addr = -1; ++static gint ett_smscb_dcs = -1; ++static gint ett_smscb_fragment = -1; ++static gint ett_smscb_fragments = -1; ++ ++static GHashTable *smscb_fragment_table = NULL; ++static GHashTable *smscb_reassembled_table = NULL; ++ ++static gboolean reassemble_smscb = TRUE; ++ ++static dissector_handle_t data_handle; ++ ++#define SMSCB_HDR_MINLEN 1 ++ ++/* ++ * Bits in the address field. ++ */ ++#define SMSCB_ADDR_LB 0x10 /* Address Last Bit */ ++#define SMSCB_ADDR_SEQ 0x0f /* Address sequence number */ ++#define SMSCB_SERIAL_GS 0xc000 /* CBS Serial Number - Geographical Scope */ ++#define SMSCB_SERIAL_MCODE 0x3ffc /* CBS Serial Number - Message Code */ ++#define SMSCB_SERIAL_UPDNUM 0x0003 /* CBS Serial Number - Update Number */ ++#define SMSCB_PAGE_NUM 0xf0 /* Page number */ ++#define SMSCB_PAGE_CNT 0x0f /* Page total count */ ++ ++#define SMSCB_SEQ_LAST 3 ++ ++/* 04.12 section 3.3.1 */ ++static const value_string smscb_addr_lb_vals[] = { ++ { 0, "More blocks" }, ++ { 1, "Last block" }, ++ { 0, NULL } ++}; ++ ++/* 04.12 section 3.3.1 */ ++static const value_string smscb_addr_seq_vals[] = { ++ { 0, "First block" }, ++ { 1, "Second block" }, ++ { 2, "Third block" }, ++ { 3, "Fourth block" }, ++ { 8, "First schedule block" }, ++ { 15, "Null message" }, ++ { 0, NULL } ++}; ++ ++/* 03.41 section 9.3.2.1 */ ++static const value_string smscb_serial_gs_vals[] = { ++ { 0, "Cell wide (immediate)" }, ++ { 1, "PLMN wide" }, ++ { 2, "Location Area wide" }, ++ { 3, "Cell wide" }, ++ { 0, NULL } ++}; ++ ++ ++static const fragment_items smscb_frag_items = { ++ /* Fragment subtrees */ ++ &ett_smscb_fragment, ++ &ett_smscb_fragments, ++ /* Fragment fields */ ++ &hf_smscb_fragments, ++ &hf_smscb_fragment, ++ &hf_smscb_fragment_overlap, ++ &hf_smscb_fragment_overlap_conflicts, ++ &hf_smscb_fragment_multiple_tails, ++ &hf_smscb_fragment_too_long_fragment, ++ &hf_smscb_fragment_error, ++ /* Reassembled in field */ ++ &hf_smscb_reassembled_in, ++ /* Reassembled length field */ ++ &hf_smscb_reassembled_length, ++ /* Tag */ ++ "fragments" ++}; ++ ++static void smscb_defragment_init(void) ++{ ++ fragment_table_init(&smscb_fragment_table); ++ reassembled_table_init(&smscb_reassembled_table); ++} ++ ++/* [3GPP TS 03.41 section 9.3] */ ++static int dissect_smscb_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) ++{ ++ proto_item *ti; ++ gint offset = 0; ++ gint length, out_len, textlen; ++ guint8 encoding; ++ gchar msgbuf[88 + 1]; ++ gchar *utf8_text, *p; ++ ++ proto_tree_add_item(tree, hf_smscb_serial_gs, tvb, offset, 1, ENC_NA); ++ proto_tree_add_item(tree, hf_smscb_serial_mcode, tvb, offset, 2, ENC_NA); ++ offset++; ++ proto_tree_add_item(tree, hf_smscb_serial_updnum, tvb, offset, 1, ENC_NA); ++ offset++; ++ proto_tree_add_item(tree, hf_smscb_msgid, tvb, offset, 2, ENC_BIG_ENDIAN); ++ offset += 2; ++ ti = proto_tree_add_text(tree, tvb, offset, 1, "Data Coding Scheme"); ++ encoding = dissect_cbs_data_coding_scheme( ++ tvb_new_subset(tvb, offset, 1, -1), pinfo, ++ proto_item_add_subtree(ti, ett_smscb_dcs)); ++ offset++; ++ proto_tree_add_item(tree, hf_smscb_page_num, tvb, offset, 1, ENC_NA); ++ proto_tree_add_item(tree, hf_smscb_page_cnt, tvb, offset, 1, ENC_NA); ++ offset++; ++ ++ length = tvb_length(tvb) - offset; ++ switch (encoding) { ++ case SMS_ENCODING_7BIT: ++ case SMS_ENCODING_7BIT_LANG: ++ out_len = gsm_sms_char_7bit_unpack(0, length, sizeof(msgbuf) - 1, ++ tvb_get_ptr(tvb, offset, length), msgbuf); ++ msgbuf[out_len] = '\0'; ++ utf8_text = gsm_sms_chars_to_utf8(msgbuf, out_len); ++ textlen = strlen(utf8_text); ++ break; ++ /* TODO: UCS2? */ ++ case SMS_ENCODING_8BIT: ++ default: ++ utf8_text = tvb_get_ephemeral_string(tvb, offset, length); ++ textlen = length; ++ break; ++ } ++ ++ proto_tree_add_string(tree, hf_smscb_content, tvb, offset, length, utf8_text); ++ ++ /* strip padding */ ++ if ((p = strchr( utf8_text, '\r'))) ++ *p = 0; ++ col_append_fstr(pinfo->cinfo, COL_INFO, " \"%s\"", utf8_text); ++ ++ return tvb_length(tvb); ++} ++ ++static int dissect_gsm_smscb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) ++{ ++ proto_item *ti; ++ proto_tree *gsm_smscb_tree = NULL; ++ proto_tree *subtree = NULL; ++ guint8 addr, seq, more; ++ tvbuff_t *payload; ++ ++ if (tvb_length(tvb) < SMSCB_HDR_MINLEN) ++ return 0; ++ ++ addr = tvb_get_guint8(tvb, 0); ++ seq = addr & SMSCB_ADDR_SEQ; ++ more = !(addr & SMSCB_ADDR_LB) && seq < SMSCB_SEQ_LAST; ++ ++ col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMSCB"); ++ ++ col_clear(pinfo->cinfo, COL_INFO); ++ col_append_fstr(pinfo->cinfo, COL_INFO, "seq=%d", seq); ++ ++ if (tree) { ++ ti = proto_tree_add_item(tree, proto_gsm_smscb, tvb, 0, -1, ENC_NA); ++ gsm_smscb_tree = proto_item_add_subtree(ti, ett_smscb); ++ ++ ti = proto_tree_add_item(gsm_smscb_tree, hf_smscb_addr, tvb, 0, 1, ENC_NA); ++ subtree = proto_item_add_subtree(ti, ett_smscb_addr); ++ proto_tree_add_item(subtree, hf_smscb_addr_lb, tvb, 0, 1, ENC_NA); ++ proto_tree_add_item(subtree, hf_smscb_addr_seq, tvb, 0, 1, ENC_NA); ++ } ++ ++ payload = tvb_new_subset(tvb, 1, -1, -1); ++ ++ if (reassemble_smscb && seq <= SMSCB_SEQ_LAST) { ++ fragment_data *frag; ++ guint32 frag_id = 0x42; ++ guchar expected_seq = 0; ++ tvbuff_t *reassembled = NULL; ++ gboolean save_fragmented = pinfo->fragmented; ++ ++ frag = fragment_get(pinfo, frag_id, smscb_fragment_table); ++ if (frag) { ++ while (frag->next) ++ frag = frag->next; ++ expected_seq = frag->offset + 1; ++ } ++ ++ /* ++ * TS 03.41 section 8 says we should discard sequences ++ * which do not consist of consecutive blocks ++ */ ++ if (seq != expected_seq) ++ g_free(fragment_delete(pinfo, frag_id, smscb_fragment_table)); ++ ++ pinfo->fragmented = more; ++ frag = fragment_add_seq_check(payload, 0, pinfo, frag_id, ++ smscb_fragment_table, ++ smscb_reassembled_table, seq, ++ tvb_length(payload), ++ more); ++ ++ reassembled = process_reassembled_data(payload, 0, pinfo, ++ "Reassembled SMSCB", frag, &smscb_frag_items, NULL, ++ gsm_smscb_tree); ++ ++ if (frag && pinfo->fd->num == frag->reassembled_in) { ++ dissect_smscb_message(reassembled, pinfo, gsm_smscb_tree); ++ } else { ++ col_append_str(pinfo->cinfo, COL_INFO, " (Fragment)"); ++ proto_tree_add_text(gsm_smscb_tree, payload, 0, -1, "Fragment Data"); ++ } ++ ++ pinfo->fragmented = save_fragmented; ++ } else if (seq == 0) { ++ dissect_smscb_message(payload, pinfo, gsm_smscb_tree); ++ } else { ++ /* TODO: reassemble & dissect Schedule messages */ ++ call_dissector(data_handle, payload, pinfo, tree); ++ } ++ ++ return tvb_length(tvb); ++} ++ ++static hf_register_info hf[] = { ++ { &hf_smscb_addr, { ++ "Address Field", "smscb.addr", FT_UINT8, BASE_HEX, ++ NULL, 0x0, ++ "Address", HFILL, ++ }}, ++ { &hf_smscb_addr_lb, { ++ "LB", "smscb.addr.lb", FT_UINT8, BASE_DEC, ++ VALS(smscb_addr_lb_vals), SMSCB_ADDR_LB, ++ "Last Block bit", HFILL, ++ }}, ++ { &hf_smscb_addr_seq, { ++ "SEQ", "smscb.addr.seq", FT_UINT8, BASE_DEC, ++ VALS(smscb_addr_seq_vals), SMSCB_ADDR_SEQ, ++ "Sequence Number", HFILL, ++ }}, ++ ++ { &hf_smscb_serial_gs, { ++ "Geographic Scope", "smscb.serial.gs", FT_UINT16, BASE_DEC, ++ VALS(smscb_serial_gs_vals), SMSCB_SERIAL_GS, NULL, HFILL, ++ }}, ++ { &hf_smscb_serial_mcode, { ++ "Message Code", "smscb.serial.mcode", FT_UINT16, BASE_DEC, ++ NULL, SMSCB_SERIAL_MCODE, NULL, HFILL, ++ }}, ++ { &hf_smscb_serial_updnum, { ++ "Update Number", "smscb.serial.updnum", FT_UINT16, BASE_DEC, ++ NULL, SMSCB_SERIAL_UPDNUM, NULL, HFILL, ++ }}, ++ ++ { &hf_smscb_msgid, { ++ "Message Identifier", "smscb.msgid", FT_UINT16, BASE_DEC, ++ NULL, 0, NULL, HFILL, ++ }}, ++ ++ { &hf_smscb_page_num, { ++ "Page number", "smscb.page.num", FT_UINT8, BASE_DEC, ++ NULL, SMSCB_PAGE_NUM, NULL, HFILL, ++ }}, ++ { &hf_smscb_page_cnt, { ++ "Total pages", "smscb.page.cnt", FT_UINT8, BASE_DEC, ++ NULL, SMSCB_PAGE_CNT, NULL, HFILL, ++ }}, ++ { &hf_smscb_content, { ++ "Content of Message", "smscb.content", FT_STRING, BASE_NONE, ++ NULL, 0x00, NULL, HFILL, ++ }}, ++ ++ /* Fragment reassembly */ ++ { &hf_smscb_fragments, { ++ "Message fragments", "smscb.fragments", ++ FT_NONE, BASE_NONE, NULL, 0x00, ++ "SMSCB Message fragments", HFILL, ++ }}, ++ { &hf_smscb_fragment, { ++ "Message fragment", "smscb.fragment", ++ FT_FRAMENUM, BASE_NONE, NULL, 0x00, ++ "SMSCB Message fragment", HFILL, ++ }}, ++ { &hf_smscb_fragment_overlap, { ++ "Message fragment overlap", "smscb.fragment.overlap", ++ FT_BOOLEAN, BASE_NONE, NULL, 0x0, ++ "SMSCB Message fragment overlaps with other fragment(s)", HFILL, ++ }}, ++ { &hf_smscb_fragment_overlap_conflicts, { ++ "Message fragment overlapping with conflicting data", "smscb.fragment.overlap.conflicts", ++ FT_BOOLEAN, BASE_NONE, NULL, 0x0, ++ "SMSCB Message fragment overlaps with conflicting data", HFILL, ++ }}, ++ { &hf_smscb_fragment_multiple_tails, { ++ "Message has multiple tail fragments", "smscb.fragment.multiple_tails", ++ FT_BOOLEAN, BASE_NONE, NULL, 0x0, ++ "SMSCB Message fragment has multiple tail fragments", HFILL, ++ }}, ++ { &hf_smscb_fragment_too_long_fragment, { ++ "Message fragment too long", "smscb.fragment.too_long_fragment", ++ FT_BOOLEAN, BASE_NONE, NULL, 0x0, ++ "SMSCB Message fragment data goes beyond the packet end", HFILL, ++ }}, ++ { &hf_smscb_fragment_error, { ++ "Message defragmentation error", "smscb.fragment.error", ++ FT_FRAMENUM, BASE_NONE, NULL, 0x00, ++ "SMSCB Message defragmentation error due to illegal fragments", HFILL, ++ }}, ++ { &hf_smscb_reassembled_in, { ++ "Reassembled in", "smscb.reassembled.in", ++ FT_FRAMENUM, BASE_NONE, NULL, 0x00, ++ "SMSCB Message has been reassembled in this packet.", HFILL, ++ }}, ++ { &hf_smscb_reassembled_length, { ++ "Reassembled SMSCB length", "smscb.reassembled.length", ++ FT_UINT32, BASE_DEC, NULL, 0x00, ++ "The total length of the reassembled payload", HFILL, ++ }}, ++}; ++ ++static gint *ett[] = { ++ &ett_smscb, ++ &ett_smscb_addr, ++ &ett_smscb_dcs, ++ &ett_smscb_fragment, ++ &ett_smscb_fragments, ++}; ++ ++void proto_reg_handoff_gsm_smscb(void) ++{ ++ data_handle = find_dissector("data"); ++} ++ ++void proto_register_gsm_smscb(void) ++{ ++ proto_gsm_smscb = proto_register_protocol( ++ "Short Message Service Cell Broadcast", ++ "SMSCB", "gsm_smscb"); ++ ++ proto_register_field_array(proto_gsm_smscb, hf, array_length(hf)); ++ proto_register_subtree_array(ett, array_length(ett)); ++ ++ new_register_dissector("gsm_smscb", dissect_gsm_smscb, proto_gsm_smscb); ++ register_init_routine(smscb_defragment_init); ++} +diff --git a/epan/dissectors/packet-lapdm.c b/epan/dissectors/packet-lapdm.c +index dbeac85..add859d 100644 +--- a/epan/dissectors/packet-lapdm.c ++++ b/epan/dissectors/packet-lapdm.c +@@ -110,6 +110,7 @@ static GHashTable *lapdm_reassembled_table = NULL; + static dissector_table_t lapdm_sapi_dissector_table; + + static dissector_handle_t data_handle; ++static dissector_handle_t smscb_handle; + + static gboolean reassemble_lapdm = TRUE; + +@@ -121,6 +122,7 @@ static gboolean reassemble_lapdm = TRUE; + #define LAPDM_CR 0x02 /* Command/Response bit */ + #define LAPDM_EA 0x01 /* First Address Extension bit */ + #define LAPDM_LPD 0x60 /* Link Protocol Discriminator */ ++#define LAPDM_LPD_CB 0x20 /* Cell Broadcast LPD */ + + /* + * Bits in the length field. +@@ -219,6 +221,7 @@ dissect_lapdm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) + tvbuff_t *payload; + int available_length; + gboolean is_response = FALSE; ++ gboolean is_cbs = FALSE; + + /* Check that there's enough data */ + if (tvb_length(tvb) < LAPDM_HEADER_LEN) +@@ -229,6 +232,7 @@ dissect_lapdm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) + addr = tvb_get_guint8(tvb, 0); + length = tvb_get_guint8(tvb, 2); + ++ is_cbs = (addr & LAPDM_LPD) == LAPDM_LPD_CB; + cr = addr & LAPDM_CR; + if (pinfo->p2p_dir == P2P_DIR_RECV) { + is_response = cr ? FALSE : TRUE; +@@ -245,15 +249,22 @@ dissect_lapdm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) + addr_tree = proto_item_add_subtree(addr_ti, ett_lapdm_address); + + proto_tree_add_uint(addr_tree, hf_lapdm_lpd, tvb, 0, 1, addr); +- proto_tree_add_uint(addr_tree, hf_lapdm_sapi, tvb, 0, 1, addr); +- proto_tree_add_uint(addr_tree, hf_lapdm_cr, tvb, 0, 1, addr); +- proto_tree_add_uint(addr_tree, hf_lapdm_ea, tvb, 0, 1, addr); ++ if (!is_cbs) { ++ proto_tree_add_uint(addr_tree, hf_lapdm_sapi, tvb, 0, 1, addr); ++ proto_tree_add_uint(addr_tree, hf_lapdm_cr, tvb, 0, 1, addr); ++ proto_tree_add_uint(addr_tree, hf_lapdm_ea, tvb, 0, 1, addr); ++ } + } + else { + lapdm_ti = NULL; + lapdm_tree = NULL; + } + ++ if (is_cbs) { ++ call_dissector(smscb_handle, tvb, pinfo, tree); ++ return; ++ } ++ + control = dissect_xdlc_control(tvb, 1, pinfo, lapdm_tree, hf_lapdm_control, + ett_lapdm_control, &lapdm_cf_items, NULL /* LAPDm doesnt support extended */, NULL, NULL, + is_response, FALSE, FALSE); +@@ -486,5 +497,6 @@ void + proto_reg_handoff_lapdm(void) + { + data_handle = find_dissector("data"); ++ smscb_handle = find_dissector("gsm_smscb"); + } + |