#!/usr/bin/python
# vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:tw=0

  #############################################################################
  #
  # Copyright (c) 2005 Dell Computer Corporation
  # Dual Licenced under GNU GPL and OSL
  #
  #############################################################################
"""smbios-wireless-ctl"""

from __future__ import generators

# import arranged alphabetically
import gettext
import locale
import os
import sys
import traceback

# the following vars are all substituted on install
# this bin isnt byte-compiled, so this is ok
__VERSION__="uninstalled-version"
pythondir=os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "..", "python")
clidir=os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "..", "py-cli")
# end vars

# import all local modules after this.
sys.path.insert(0,pythondir)
sys.path.insert(0,clidir)

import cli
from libsmbios_c import token, smbios, smi, system_info as sysinfo, localedir, GETTEXT_PACKAGE
from libsmbios_c.trace_decorator import decorate, traceLog, getLog

locale.setlocale(locale.LC_ALL, '')
gettext.install(GETTEXT_PACKAGE, localedir, unicode=1)

moduleLog = getLog()
verboseLog = getLog(prefix="verbose.")

class RuntimeControlUnsupported(Exception): pass

def command_parse():
    parser = cli.OptionParser(usage=__doc__, version=__VERSION__)

    parser.add_option('--info', action="store_true", default=False, help= _("Show wireless configuration (default)"))

    parser.add_option('--boot',    action="store_true", default=False, dest="boot", help= _("Set BIOS boot-time setting."))
    parser.add_option('--no-boot', action="store_false",               dest="boot", help= _("Do not set BIOS boot-time setting."))

    parser.add_option('--runtime',    action="store_true",  default=True, dest="runtime", help= _("Set BIOS runtime setting (default)."))
    parser.add_option( "--no-runtime", "--boot-only", '--boot_only',
                                      action="store_false",               dest="runtime", help= _("Do not set BIOS runtime setting."))

    parser.add_option('--wlan', action="store", type="int", default=None, dest="wlan",
            help= _("Set radio runtime status for wireless LAN"))
    parser.add_option('--bt',   action="store", type="int", default=None, dest="bt",
            help= _("Set radio runtime status for Bluetooth"))
    parser.add_option('--wwan', action="store", type="int", default=None, dest="wwan",
            help= _("Set radio runtime status for cellular (wireless WAN)"))

    parser.add_option('--sw_wlan', action="store", type="int", default=None, dest="sw_wlan",
            help= _("Set hardware switch so that it controls radio for wireless LAN"))
    parser.add_option('--sw_bt',   action="store", type="int", default=None, dest="sw_bt",
            help= _("Set hardware switch so that it controls radio for Bluetooth"))
    parser.add_option('--sw_wwan', action="store", type="int", default=None, dest="sw_wwan",
            help= _("Set hardware switch so that it controls radio for cellular (wireless WAN)"))
    parser.add_option('--sw_locator', action="store", type="int", default=None, dest="sw_locator",
            help= _("Enable or disable the WIFI locator switch"))

    parser.add_option('--st_wlan', action="store_true", default=False, dest="st_wlan",
            help= _("Get status for wireless LAN. 0 = enabled. 1 = disabled. 2 = not present. 3 = unsupported. 4 = unknown."))
    parser.add_option('--st_bt',   action="store_true", default=False, dest="st_bt",
            help= _("Get status for Bluetooth. 0 = enabled. 1 = disabled. 2 = not present. 3 = unsupported. 4 = unknown."))
    parser.add_option('--st_wwan', action="store_true", default=False, dest="st_wwan",
            help= _("Get status for cellular (wireless WAN). 0 = enabled. 1 = disabled. 2 = not present. 3 = unsupported. 4 = unknown."))

    parser.add_option('--st_locator', action="store", type="int", default=None, dest="st_locator",
            help= _("Get status for locator switch"))

    cli.addStdOptions(parser, passwordOpts=True, securityKeyOpt=True)
    return parser.parse_args()

radios = {
    "wlan": {
            "radioEnable" : 0x0180, "radioDisable" : 0x017F, "radioRuntimeId": 1,
            "switchEnable": 0x0186, "switchDisable": 0x0185, "switchRuntime" : (1, 1),
            "switch_bit": 0,
             "supported_bit": 2, "installed_bit": 8, "disabled_bit": 17, "name": _("WLAN"),
             "legacyEnable": 0x010c, "legacyDisable": 0x010d,
             "legacySwitchEnable": 0x0116, "legacySwitchDisable": 0x0115,
            },

    "bt":   {
            "radioEnable" : 0x0152, "radioDisable" : 0x0153, "radioRuntimeId": 2,
            "switchEnable": 0x0182, "switchDisable": 0x0181, "switchRuntime" : (1, 2),
            "switch_bit": 1,
             "supported_bit": 3, "installed_bit": 9, "disabled_bit": 18, "name": _("Bluetooth"),
            },

    "wwan": {
            "radioEnable" : 0x017C, "radioDisable" : 0x017B, "radioRuntimeId": 3,
            "switchEnable": 0x0184, "switchDisable": 0x0183, "switchRuntime" : (1, 4),
            "switch_bit": 2,
             "supported_bit": 4, "installed_bit": 10, "disabled_bit": 19, "name": _("WWAN"),
            },

    "locator": {
            "name": _("Locator"),
            "switch_bit": 8,
            "switchEnable": 0x017E, "switchDisable": 0x017D, "switchRuntime": (2, 1),
            },
    }

def radioBootCtl(radio, enable):
    print _("Set boot settings for %s") % radio["name"]
    try:
        tok = radio["radioDisable"]
        legacy = radio.get("legacyDisable", None)
        if enable:
            tok = radio["radioEnable"]
            legacy = radio.get("legacyEnable", None)

        tokenTable = token.TokenTable()
        try:
            tokenTable[ tok ].activate()
        except IndexError, e:
            tokenTable[ legacy ].activate()
    except IndexError:
        print _("\tThis system does not support boot time settings for %s") % radio["name"]

class InvalidConfig(Exception): pass

def radioRuntimeCtl(radio, enable):
    try:
        print _("Set runtime settings for %s") % radio['name']
        tokenTable = token.TokenTable()
        if enable and not tokenTable[radio["radioEnable"]].isActive():
            raise InvalidConfig( _("Runtime setting has no effect when boot time config is disabled. Ignoring.") )

        disable = 1
        if enable: disable=0
        res = smi.simple_ci_smi( 17, 11, (1 | ((radio['radioRuntimeId'])<<8) | ((disable)<<16)) )
        if res[smi.cbRES1] != 0:
            raise RuntimeControlUnsupported( _("This system does not support runtime control of wireless settings.") )

    except (InvalidConfig, RuntimeControlUnsupported), e:
        print "\t%s" % e
        raise

def switchBootCtl(radio, enable):
    print _("Set boot switch settings for %s") % radio["name"]

    try:
        switch = radio["switchDisable"]
        legacy = radio.get("legacySwitchDisable", None)
        if enable:
            switch = radio["switchEnable"]
            legacy = radio.get("legacySwitchEnable", None)

        tokenTable = token.TokenTable()
        try:
            tokenTable[switch].activate()
        except IndexError, e:
            tokenTable[legacy].activate()

    except IndexError:
        print "\t%s" % _("This system does not support boot-time wireless switch configuration for %s.") % radio["name"]
        

def switchRuntimeCtl(radio, enable):
    print _("\nSet runtime switch settings for %s") % radio["name"]
    try:
        whichConfig, whichSwitch = radio["switchRuntime"]
        verboseLog.debug("config: %s switch: %s" % (whichConfig, whichSwitch))
        oldconfig=0
        if whichConfig == 1:
            res = smi.simple_ci_smi( 17, 11, 2 )
            if res[smi.cbRES1] != 0:
                raise RuntimeControlUnsupported( _("This system does not support runtime control of wireless switch settings.") )
            oldconfig = res[smi.cbRES2] & 0xFF

        verboseLog.debug("oldconfig %s" % oldconfig)
        newconfig = (oldconfig & ~whichSwitch)
        if enable:
            verboseLog.debug("enabling: 0x%08x -> 0x%08x" % (newconfig, (newconfig|whichSwitch))) 
            newconfig = newconfig | whichSwitch

        verboseLog.debug("set: 0x%08x" % (0x2 | (whichConfig << 8) | (newconfig << 16)))
        res = smi.simple_ci_smi( 17, 11, (0x2 | (whichConfig << 8) | (newconfig << 16)))
        verboseLog.debug("cbRES1: 0x%04x" % res[smi.cbRES1])
        verboseLog.debug("cbRES2: 0x%04x" % res[smi.cbRES2])
        verboseLog.debug("cbRES3: 0x%04x" % res[smi.cbRES3])
        verboseLog.debug("cbRES4: 0x%04x" % res[smi.cbRES4])
        if res[smi.cbRES1] != 0:
            raise RuntimeControlUnsupported( _("This system does not support runtime control of wireless switch settings.") )
    except (RuntimeControlUnsupported,), e:
        print "\t%s" % e

def onOff(thing, trueval="on", falseval="off"):
    if thing:
        return trueval
    else:
        return falseval

STATUS_ENABLED = 0
STATUS_DISABLED = 1
STATUS_NOT_PRESENT = 2
STATUS_UNSUPPORTED = 3
STATUS_UNKNOWN = 4

def wirelessInfo(radio):
    print _("\nRadio Status for %s:") % radio["name"];
    ret = STATUS_UNKNOWN
    tokenTable = token.TokenTable()
    try:
        res = smi.simple_ci_smi( 17, 11 )
        if res[smi.cbRES1] != 0:
            raise RuntimeControlUnsupported( _("This system does not support runtime control of wireless settings.") )

        print onOff(tokenTable[radio["radioEnable"]].isActive(), _("\t%s enabled at boot"), _("\t%s disabled at boot")) % radio["name"]

        if res[smi.cbRES2] & (1 << radio["supported_bit"]):
            print _("\t%s supported") % radio["name"]

            if res[smi.cbRES2] & (1<< radio["disabled_bit"]):
                ret = STATUS_DISABLED;
                print _("\t%s disabled") % radio["name"]
            else:
                ret = STATUS_ENABLED;
                print _("\t%s enabled") % radio["name"]

            if res[smi.cbRES2] & (1<< radio["installed_bit"]):
                print _("\t%s installed") % radio["name"]
            else:
                ret = STATUS_NOT_PRESENT;
                print _("\t%s not installed") % radio["name"]

            try:
                print onOff(tokenTable[radio["switchEnable"]].isActive(), _("\t%s controlled by wireless switch at boot"), _("\t%s not controlled by wireless switch at boot")) % radio["name"]
            except (IndexError,), e:
                print _("\t%s boot-time wireless switch setting not present") % radio["name"]

            switch = smi.simple_ci_smi( 17, 11, 2 )
            print onOff((switch[smi.cbRES2] & (1<<radio["switch_bit"])), _("\t%s runtime switch control currently enabled"), _("\t%s runtime switch control currently disabled")) % radio["name"]

        else:
            ret = STATUS_UNSUPPORTED
            print _("\t%s not supported") % radio["name"]
    except RuntimeControlUnsupported, e:
        print "\t%s" % e

    # print legacy status, if avail:
    try:
        print onOff(tokenTable[radio["legacyEnable"]], "\t%s legacy boot setting enabled", "\t%s legacy boot setting disabled" ) % radio["name"]
        print onOff(tokenTable[radio["legacySwitchEnable"]], _("\t%s legacy switch enabled"), _("\t%s legacy switch disabled") ) % radio["name"]
    except (IndexError, KeyError): 
        # token doesnt exist, or we dont have legacy settings for this radio, it's ok.
        pass

    print _("\tStatus Code: %d") % ret

def outputWirelessInfo():
    print _("Wireless Info:")
    try:
        res = smi.simple_ci_smi( 17, 11 )
        if res[smi.cbRES1] != 0:
            raise RuntimeControlUnsupported( _("This system does not support runtime control of wireless settings.") )
        if res[smi.cbRES2] & (1<<0):
            print _("\tHardware switch supported")
            print "\tHardware switch is %s" % onOff(res[smi.cbRES2] & (1 << 16), "On", "Off")
        else:
            print _("\tHardware switch not supported")

        print onOff(res[smi.cbRES2] & (1<<1), _("\tWiFi Locator supported"), _("\tWiFi Locator not supported"))
        print onOff(res[smi.cbRES2] & (1<<5), _("\tWireless Keyboard supported"), _("\tWireless Keyboard not supported"))
        print _("\tNVRAM Size: %d bytes") % res[smi.cbRES3]
        print _("\tNVRAM format version: %d") % (res[smi.cbRES4] & 0xFF)
    except RuntimeControlUnsupported, e:
        print "\t%s" % e

def outputSwitchStatus():
    print _("\nWireless Switch Configuration")
    try:
        res = smi.simple_ci_smi( 17, 11, 2 )
        if res[smi.cbRES1] != 0:
            raise RuntimeControlUnsupported( _("This system does not support runtime control of wireless switch settings.") )
        print _("\tWLAN switch control: %s") %      onOff((res[smi.cbRES2] & (1<<0)), _("on"), _("off"))
        print _("\tBluetooth switch control: %s") % onOff((res[smi.cbRES2] & (1<<1)), _("on"), _("off"))
        print _("\tWWAN switch control: %s") %      onOff((res[smi.cbRES2] & (1<<2)), _("on"), _("off"))
        print _("\tswitch config: %s") %            onOff((res[smi.cbRES2] & (1<<7)), _("locked"), _("not locked"))
        print _("\tWiFi locator: %s") %             onOff((res[smi.cbRES2] & (1<<8)), _("enabled"), _("disabled"))
        print _("\tWiFi Locator config: %s") %      onOff((res[smi.cbRES2] & (1<<15)), _("locked"), _("not locked"))
    except RuntimeControlUnsupported, e:
        print "\t%s" % e


def main():
    exit_code = 0
    (options, args) = command_parse()
    cli.setup_std_options(options)

    # need to do one or the other.
    if not options.runtime:
        options.boot=1

    try:
        if options.info:
            print _("Libsmbios version : %s") % sysinfo.get_library_version_string()
            print _("smbios-wireless-ctl version : %s") % __VERSION__
            outputWirelessInfo()
            options.st_wlan = 1
            options.st_bt = 1
            options.st_wwan = 1
            options.st_locator = 1

        for i in ("wlan", "bt", "wwan"):
            if getattr(options, i, None) is not None:
                if options.boot:
                    radioBootCtl(radios[i], getattr(options, i))
                if options.runtime:
                    radioRuntimeCtl(radios[i], getattr(options, i))
                setattr(options, "st_%s" % i, 1)

            if getattr(options, "sw_%s" % i, None) is not None:
                if options.boot:
                    switchBootCtl(radios[i], getattr(options, "sw_%s" % i))
                if options.runtime:
                    switchRuntimeCtl(radios[i], getattr(options, "sw_%s" % i))
                setattr(options, "st_%s" % i, 1)

            if getattr(options, "st_%s" % i, None):
                wirelessInfo(radios[i])

        if options.sw_locator is not None:
            switchRuntimeCtl(radios["locator"], options.sw_locator)
            options.st_locator = 1

        if options.st_locator:
            outputSwitchStatus()


    except (smi.SMIExecutionError, ), e:
        exit_code=3
        moduleLog.info( _("ERROR: Could not execute SMI.") )
        verboseLog.info( _("The smi library returned this error:") )
        verboseLog.info( str(e) )
        moduleLog.info( cli.standardFailMessage )

    except (smbios.TableParseError, token.TokenTableParseError), e:
        exit_code=3
        moduleLog.info( _("ERROR: Could not parse system SMBIOS table.") )
        verboseLog.info( _("The smbios library returned this error:") )
        verboseLog.info( str(e) )
        moduleLog.info( cli.standardFailMessage )

    except (token.TokenManipulationFailure,), e:
        exit_code=4
        moduleLog.info( _("ERROR: Could not manipulate system token.") )
        verboseLog.info( _("The token library returned this error:") )
        verboseLog.info( str(e) )
        moduleLog.info( cli.standardFailMessage )


    return exit_code

if __name__ == "__main__":
    sys.exit( main() )



# cbClass 17
# cbSelect 11
# WiFi Control
# Entry/return values grouped by the value of cbARG1, byte0 which indicates the function to perform:

# 0x1 = Set QuickSet Radio Disable Flag
#  cbARG1, byte1    Radio ID value:
#      0 Radio Status
#      1 WLAN ID
#      2 BT ID
#      3 WWAN ID
#  cbARG1, byte2   Flag bits:
#      0 QuickSet disables radio (1)
#      1-7 Reserved (0)
#  cbRES1      Standard return codes (0, -1, -2)
#  cbRES2      QuickSet (QS) radio disable bit map:
#      0 QS disables WLAN
#      1 QS disables BT
#      2 QS disables WWAN
#      3-31 Reserved (0)

# 0x2 = Wireless Switch Configuration
#  cbARG1, byte1   Subcommand:
#      0 Get config
#      1 Set config
#      2 Set WiFi locator enable/disable
#  cbARG1,byte2    Switch settings (if byte 1==1):
#      0 WLAN switch control (1)
#      1 BT switch control (1)
#      2 WWAN switch control (1)
#      3-7 Reserved (0)
#  cbARG1, byte2   Enable bits (if byte 1==2):
#      0 Enable WiFi locator (1)
#  cbRES1      Standard return codes (0, -1, -2)
#  cbRES2      QuickSet radio disable bit map:
#      0 WLAN controlled by switch (1)
#      1 BT controlled by switch (1)
#      2 WWAN controlled by switch (1)
#      3-6 Reserved (0)
#      7 Wireless switch config locked (1)
#      8 WiFi locator enabled (1)
#      9-14 Reserved (0)
#      15 WiFi locator setting locked (1)
#      16-31 Reserved (0)

