#!/usr/bin/python # -*- coding: utf-8 -*- import os import errno import subprocess try: from subprocess import getoutput except ImportError: from commands import getoutput import shutil import sys # Variables xorgfile = "/etc/X11/xorg.conf" lspci = '/usr/sbin/lspci' nvidia_settings = "/usr/share/applications/nvidia-settings.desktop" device_id_prefix = "ArgentVga" nvidia_option_prefix = "--nvidia-opt--" screen_layout_sections = [] device_sections = [] xorg_conf_structure = """ Section "Module" SubSection "extmod" Option "omit xfree86-dga" EndSubSection Load "i2c" Load "ddc" Load "vbe" Load "dri" Load "glx" Load "synaptics" EndSection Section "ServerFlags" Option "AllowMouseOpenFail" "true" EndSection Section "Monitor" Identifier "Generic Monitor" VertRefresh 43 - 60 HorizSync 28 - 80 EndSection __device_section__ __screen_section__ Section "DRI" Mode 0666 EndSection Section "ServerLayout" Identifier "Main Layout" __screen_layout_section__ EndSection Section "Extensions" #Option "Composite" "Enable" EndSection """ screen_sections = [] screen_section = """ Section "Screen" Identifier "Screen __screen_id__" Device "%s__screen_id__" Monitor "Generic Monitor" %sOption "AddARGBGLXVisuals" "true" %sOption "RegistryDwords" "EnableBrightnessControl=1" DefaultDepth 24 SubSection "Display" Depth 8 ViewPort 0 0 #Modes "1024x768" "800x600" "640x480" EndSubsection SubSection "Display" Depth 16 ViewPort 0 0 #Modes "1024x768" "800x600" "640x480" EndSubsection SubSection "Display" Depth 24 ViewPort 0 0 #Modes "1024x768" "800x600" "640x480" EndSubsection EndSection """ % (device_id_prefix, nvidia_option_prefix, nvidia_option_prefix,) # cmdlines options = sys.argv[1:] dryrun = False noproprietary = False nvidia_forcefail = False nvidia_disablelegacy = False legacy = False livecd = False steps = [] forced_xdriver = '' current_arch = os.uname()[4] nomodeset = False noefi = False fglrx_supported = sorted(getoutput( "modinfo fglrx | grep alias | grep pci | " "cut -d':' -f 3 | cut -d'*' -f 1 | " "sed 's/.*1002d//' | sed 's/^0000//' | sed 's/sv$//'" ).lower().split()) nvidia_71xx_supported = ['0020', '0028', '0029', '002c', '002d', '00a0', '0100', '0101', '0103', '0150', '0151', '0152', '0153'] nvidia_96xx_supported = ['0110', '0111', '0112', '0113', '0170', '0171', '0172', '0173', '0174', '0175', '0176', '0177', '0178', '0179', '017a', '017c', '017d', '0181', '0182', '0183', '0185', '0188', '018a', '018b', '018c', '01a0', '01f0', '0200', '0201', '0202', '0203', '0250', '0251', '0253', '0258', '0259', '025b', '0280', '0281', '0282', '0286', '0288', '0289', '028c'] nvidia_173xx_supported = ['00fa', '00fb', '00fc', '00fd', '00fe', '0301', '0302', '0308', '0309', '0311', '0312', '0314', '031a', '031b', '031c', '0320', '0321', '0322', '0323', '0324', '0325', '0326', '0327', '0328', '032a', '032b', '032c', '032d', '0330', '0331', '0332', '0333', '0334', '0338', '033f', '0341', '0342', '0343', '0344', '0347', '0348', '034c', '034e'] # Taken from here: # http://www.nvidia.com/object/IO_32667.html nvidia_304xx_supported = ['0040', '0041', '0042', '0043', '0044', '0045', '0046', '0047', '0048', '004e', '0090', '0091', '0092', '0093', '0095', '0098', '0099', '009d', '00c0', '00c1', '00c2', '00c3', '00c8', '00c9', '00cc', '00cd', '00ce', '00f1', '00f2', '00f3', '00f4', '00f5', '00f6', '00f8', '00f9', '0140', '0141', '0142', '0143', '0144', '0145', '0146', '0147', '0148', '0149', '014a', '014c', '014d', '014e', '014f', '0160', '0161', '0162', '0163', '0164', '0165', '0166', '0167', '0168', '0169', '016a', '01d0', '01d1', '01d2', '01d3', '01d6', '01d7', '01d8', '01da', '01db', '01dc', '01dd', '01de', '01df', '0211', '0212', '0215', '0218', '0221', '0222', '0240', '0241', '0242', '0244', '0245', '0247', '0290', '0291', '0292', '0293', '0294', '0295', '0297', '0298', '0299', '029a', '029b', '029c', '029d', '029e', '029f', '02e0', '02e1', '02e2', '02e3', '02e4', '038b', '0390', '0391', '0392', '0393', '0394', '0395', '0397', '0398', '0399', '039c', '039e', '03d0', '03d1', '03d2', '03d5', '03d6', '0531', '0533', '053a', '053b', '053e', '07e0', '07e1', '07e2', '07e3', '07e5'] savage_supported = ['8a20', '8a21', '8a22', '9102', '8c10', '8c11', '8c12', '8c13', '8c22', '8c24', '8c26', '8c2a', '8c2b', '8c2c', '8c2d', '8c2e', '8c2f', '8a25', '8a26', '8d01', '8d02', '8d03', '8d04'] unichrome_supported = ['3108', '3118', '3157', '3343', '3344', '7205'] lspci_output = '' for option in options: if option == "--dry-run": dryrun = True elif option.startswith('--with-lspci=') and len(option.split("=")) >= 2: option = option.split("=")[1:] option = "=".join(option) if option.startswith('"'): option = option[1:] if option.startswith("'"): option = option[1:] if option.endswith("'"): option = option[:len(option)-1] if option.endswith('"'): option = option[:len(option)-1] lspci_output = option elif option.startswith('--forced-xdriver=') and len(option.split("=")) == 2: forced_xdriver = option.split("=")[1] if not lspci_output: lspci_output = getoutput(lspci+' -mm -n') # parse cmdline with open("/proc/cmdline","r") as f: cmdline = f.readline().split() for cmd in cmdline: if cmd == "noproprietary": noproprietary = True elif cmd == "nomodeset": nomodeset = True elif cmd == "nvidia=forcefail": nvidia_forcefail = True elif cmd == "nvidia=disablelegacy": nvidia_disablelegacy = True elif cmd == "legacy": legacy = True elif cmd == "cdroot": livecd = True elif cmd == "noefi": noefi = True elif cmd.startswith("xdriver=") and (len(cmd.split("=")) == 2): if not forced_xdriver: forced_xdriver = cmd.split("=")[1] # --forced-xdriver= owns def openrc_running(): return os.path.isfile("/run/openrc/softlevel") def systemd_running(): return os.path.isdir("/run/systemd/system") def remove_proprietary_opengl(bumblebee): if not dryrun: if not bumblebee: os.system(""" mount -t tmpfs none /usr/lib/opengl/ati &> /dev/null mount -t tmpfs none /usr/lib/opengl/nvidia &> /dev/null sed -i '/LIBGL_DRIVERS_PATH/ s/.*//' /etc/profile.env """) fix_possible_opengl_misconfiguration('xorg-x11') else: print("Bumblebee enabled, not deactivating proprietary drivers") else: print("I was about to remove proprietary OpenGL libraries") def get_kernel_version(): try: return int(os.uname()[2].replace(".", "")[:3]) except (ValueError, TypeError) as err: print("get_kernel_version: ouch: %s" % (err,)) return None def setup_radeon_kms(): # Starting from kernel 3.6, we have CONFIG_DRM_RADEON_KMS=y kver = get_kernel_version() if kver is None: kver = 360 # assume new kernel if not dryrun and kver < 360: os.system(""" modprobe -r radeon &> /dev/null modprobe radeon modeset=1 && touch /tmp/.radeon.kms """) else: print("I was about to modprobe radeon modeset=1") def generate_fglrx_steps(videocard, cardnumber, total_cards, bus_id): print("AMD!") print("total supported AMD cards: %s" % (len(fglrx_supported),)) print("supported list:", fglrx_supported) supported = card_id in fglrx_supported if supported: print("fglrx driver supports this card") # check if nomodeset is enabled for >=3.6.0 kernel kver = get_kernel_version() if kver is None: kver = 360 # assume new kernel if not nomodeset and kver >= 360: print("however, nomodeset is not set, though KMS is active," " defaulting to OSS driver") supported = False if supported: if noproprietary: steps.append((drop_kernel_mod, "fglrx",)) steps.append((setup_radeon_kms,)) else: steps.append((fix_possible_opengl_misconfiguration, "ati")) steps.append((copy_ati_settings_on_desktop,)) steps.append((opengl_activate, "ati")) steps.append((set_xorg_device, "fglrx", cardnumber, total_cards, bus_id,)) else: # video card not supported by fglrx print("using OSS 'ati' drivers") generate_generic_steps() # This works for Mach64, Rage128 # Radeon and in future RadeonHD driver steps.append((drop_kernel_mod, "fglrx",)) steps.append((setup_radeon_kms,)) def check_if_driver_is_available(xdriver): drv_path = "/usr/lib/xorg/modules/drivers/" + xdriver + "_drv.so" if os.path.isfile(drv_path): print("check_if_driver_is_available for " + xdriver + ": available") return True print("check_if_driver_is_available for " + xdriver + ": not available") return False def check_if_proprietary_driver_system_is_healthy(kernelmod): rc = subprocess.call(["modprobe", kernelmod]) if rc == 0: if kernelmod == "nvidia": if os.path.exists("/usr/lib/opengl/nvidia/lib"): print("check_if_proprietary_driver_system_is_healthy:" " nvidia healthy") return True print("check_if_proprietary_driver_system_is_healthy:" " nvidia NOT healthy") return False elif kernelmod == "fglrx": kver = get_kernel_version() if kver is None: kver = 360 # assume new kernel if not nomodeset and kver >= 360: print("check_if_proprietary_driver_system_is_healthy:" " fglrx (ati) NOT healthy, 'nomodeset' boot argument" " is mising") return False if os.path.exists("/usr/lib/opengl/ati/lib"): print("check_if_proprietary_driver_system_is_healthy:" " fglrx (ati) healthy") return True print("check_if_proprietary_driver_system_is_healthy:" " fglrx (ati) NOT healthy") return False return False def deploy_nvidia_xxxxxx_drivers(ver): if dryrun: print("I was about to run deploy_nvidia_xxxxxx_drivers" ", ver: %s" % (ver,)) return False drivers_dir = "/install-data/drivers" # are they available ? we're on livecd... if not os.path.isdir(drivers_dir): print("drivers_dir not available") return False packages = os.listdir(drivers_dir) _packages = [] for pkg in packages: if not pkg.endswith(".tbz2"): continue if pkg.startswith("x11-drivers:nvidia-drivers-" + ver): _packages.append(pkg) elif pkg.startswith("x11-drivers:nvidia-userspace-" + ver): _packages.append(pkg) packages = [os.path.join(drivers_dir, x) for x in _packages] if not packages: return False rc = subprocess.call(["/usr/bin/equo", "install", "--nodeps"] + packages) if rc: return False # try to check driver status now return check_if_proprietary_driver_system_is_healthy("nvidia") efivars_loaded = False def is_efi(): """ Return whether the system boots from EFI """ global efivars_loaded if noefi: return False if not efivars_loaded: subprocess.call(["modprobe", "efivars"]) efivars_loaded = True return os.path.exists("/sys/firmware/efi") def get_vesa_driver(): """ Return either "vesa" or "fbdev" as the fallback vesa-like X driver. """ if is_efi(): # vesa does not work return "fbdev" return "vesa" def set_xorg_device(xdriver, cardnum, total_cards, bus_id): if (xdriver not in ("nvidia", "fglrx",)) and \ (not check_if_driver_is_available(xdriver)): xdriver = get_vesa_driver() # fallback to vesa bus_id_mark = "#" if total_cards > 1: bus_id_mark = "" device_sections.append(""" Section "Device" Identifier "%s%s" Driver "%s" %sBusID "%s" #Option "RenderAccel" "on" #Option "XAANoOffscreenPixmaps" #Option "BusType" "PCI" #Option "ColorTiling" "on" #Option "EnablePageFlip" "on" # UseEvents is causing segmentation faults with # NVIDIA 6xxx, 7xxx and >=275.xx.xx drivers #Option "UseEvents" "True" Option "LogoPath" "/usr/share/backgrounds/argentlinux-nvidia.png" EndSection """ % (device_id_prefix, cardnum, xdriver, bus_id_mark, bus_id,)) my_screen_section = screen_section.replace("__screen_id__", str(cardnum)) # setup Option AddARGBVisuals # especially needed for legacy nvidia drivers, but works # on all of them if xdriver == "nvidia": my_screen_section = my_screen_section.replace(nvidia_option_prefix, "") else: my_screen_section = my_screen_section.replace(nvidia_option_prefix, "#") screen_sections.append(my_screen_section) screen_layout_sections.append('Screen %s "Screen %s"' % ( cardnum, cardnum,)) def opengl_activate(profile, force=False): if not dryrun: if not force: current = opengl_show() if current == profile: print("OpenGL profile is already set to: " + profile) return subprocess.call(["eselect", "opengl", "set", profile]) else: print("I was about to set opengl subsystem to: " + profile) def opengl_show(): return getoutput("eselect opengl show").split("\n")[0].strip() def fix_possible_opengl_misconfiguration(profile): # get current subsystem current = opengl_show() if not dryrun: if (profile in ("ati","nvidia","xorg-x11")) and (profile != current): if profile == "ati" or profile == "nvidia": subprocess.call(["umount", "/usr/lib/opengl/" + profile]) subprocess.call(["umount", "/usr/lib/opengl/" + profile]) opengl_activate(profile) else: print("I was about to fix OpenGL subsystem to: " + \ profile + " while the current implementation is: " + \ current) def copy_nvidia_settings_on_desktop(): homes = [] if os.path.isfile(nvidia_settings): _homes = os.listdir("/home") homes += [x for x in os.listdir("/home") \ if os.path.isdir("/home/" + x + "/Desktop")] for home in homes: try: full_home = os.path.join("/home", home) st = os.stat(full_home) dest_path = "/home/" + home + "/Desktop/" + \ os.path.basename(nvidia_settings) shutil.copy2(nvidia_settings, dest_path) os.chmod(dest_path, 0o755) os.chown(dest_path, st.st_uid, st.st_gid) if os.path.isdir("/etc/skel/Desktop"): dest_path = os.path.join( "/etc/skel/Desktop", os.path.basename(nvidia_settings)) shutil.copy2(nvidia_settings, dest_path) os.chmod(dest_path, 0o755) except Exception: pass def copy_ati_settings_on_desktop(): desktop_files = getoutput( 'equo query files ati-drivers --quiet | grep ".desktop"').split("\n") desktop_files = [x for x in desktop_files if os.path.isfile(x)] print("copy_ati_settings_on_desktop: found files: "+str(desktop_files)) for ati_settings in desktop_files: homes = os.listdir("/home") homes = [x for x in homes if os.path.isdir("/home/" + x + "/Desktop")] for home in homes: try: full_home = os.path.join("/home", home) st = os.stat(full_home) dest_path = "/home/" + home + "/Desktop/" + \ os.path.basename(ati_settings) shutil.copy2(ati_settings, dest_path) os.chmod(dest_path, 0o755) os.chown(dest_path, st.st_uid, st.st_gid) if os.path.isdir("/etc/skel/Desktop"): dest_path = os.path.join( "/etc/skel/Desktop", os.path.basename(ati_settings)) shutil.copy2(ati_settings, dest_path) os.chmod(dest_path, 0o755) except Exception: pass def setup_nvidia_drivers(card_id): drv_string = '' done_legacy = False drivers_map = ( ("304", nvidia_304xx_supported,), ("173", nvidia_173xx_supported,), ("96", nvidia_173xx_supported,), ("71", nvidia_173xx_supported,), ) if not nvidia_disablelegacy: for ver, lst in drivers_map: if card_id not in lst: continue print("NVIDIA %s driver selected" % (ver,)) drv_string = ver if livecd: rc = deploy_nvidia_xxxxxx_drivers(ver) if rc: print("NVIDIA %s deployed correctly" % (ver,)) done_legacy = True break if not done_legacy: drv_string = '[latest]' print("latest and greatest NVIDIA driver selected or unsupported") healthy = check_if_proprietary_driver_system_is_healthy("nvidia") if healthy: print("NVIDIA proprietary driver %s is loaded" % (drv_string,)) if done_legacy: try: os.makedirs("/lib/nvidia/legacy") except OSError as err: if err.errno != errno.EEXIST: raise with open("/lib/nvidia/legacy/running", "w") as f: f.write("%s" % (drv_string,)) return done_legacy, healthy def generate_nvidia_bumblebee_steps(v3dcard, company_id, card_id): done_legacy, healthy = setup_nvidia_drivers(card_id) if not healthy: print("NVIDIA drivers couldn't be loaded, cannot enable bumblebee") return if dryrun: print("Was about to start bumblebee") return if not livecd: print("LiveCD mode off, not starting bumblebee service") return # This is used by our Installer with open("/tmp/.bumblebee.enabled", "w") as f: pass if openrc_running(): os.system("/etc/init.d/bumblebee start") elif systemd_running(): os.system("/usr/bin/systemctl start bumblebeed") def generate_nvidia_steps(videocard, cardnumber, total_cards, bus_id): comp_id, card_id = extract_pci_ids(videocard) done_legacy, healthy = setup_nvidia_drivers(card_id) if healthy: if done_legacy: # then activate nvidia opengl subsystem after resetting it steps.append((opengl_activate, "xorg-x11")) steps.append((opengl_activate, "nvidia")) steps.append((set_xorg_device, "nvidia", cardnumber, total_cards, bus_id,)) steps.append((fix_possible_opengl_misconfiguration, "nvidia")) steps.append((copy_nvidia_settings_on_desktop,)) else: steps.append((fix_possible_opengl_misconfiguration, "nvidia")) steps.append((copy_nvidia_settings_on_desktop,)) steps.append((opengl_activate, "nvidia")) steps.append((set_xorg_device, "nvidia", cardnumber, total_cards, bus_id,)) else: print("NVIDIA drivers couldn't be loaded, switchting to nv driver") steps.append((opengl_activate, "xorg-x11")) def generate_generic_steps(): steps.append((remove_proprietary_opengl, bb_enabled)) steps.append((opengl_activate, "xorg-x11",)) def drop_kernel_mod(kmod): return subprocess.call(["modprobe", "-r", kmod]) def extract_pci_ids(videocard_str): videocard_split = [x.strip() for x in videocard_str.strip().split('"') \ if x.strip()] try: card_id = videocard_split[3].split()[-1].lower().strip("[]") except IndexError: card_id = None try: company_id = videocard_split[2].split()[-1].lower().strip("[]") except IndexError: company_id = None return company_id, card_id def extract_vga_cards(lspci_list): cards = [] for item in lspci_list: try: class_type = item.split()[1].strip('"') if class_type == "0300": cards.append(item) except IndexError: continue return cards def extract_3d_cards(lspci_list): # bumblebee support cards = [] for item in lspci_list: try: class_type = item.split()[1].strip('"') if class_type == "0302": cards.append(item) except IndexError: continue return cards # Create videocards list lspci_out_split = lspci_output.split("\n") videocards = extract_vga_cards(lspci_out_split) v3dcards = extract_3d_cards(lspci_out_split) # Run the program cardnumber = -1 total_cards = len(videocards) forced_monitor_modes = False steps = [] bb_enabled = False write_config = False for v3dcard in v3dcards: company_id, card_id = extract_pci_ids(v3dcard) if company_id == "10de": print("NVIDIA Optimus 3D Acceleration detected, enabling bumblebee") generate_nvidia_bumblebee_steps(v3dcard, company_id, card_id) bb_enabled = True for videocard in videocards: # setup card number cardnumber += 1 print("Card Number: " + str(cardnumber)) try: bus_id = "PCI:%s" % ( videocard.split()[0].split(".", 1)[0] ) except (IndexError,ValueError,TypeError,): bus_id = None if forced_xdriver: print("You have chosen to force the X driver: " + forced_xdriver) if forced_xdriver == "fglrx": if check_if_proprietary_driver_system_is_healthy("fglrx") \ or noproprietary: steps.append((opengl_activate, "xorg-x11")) forced_xdriver = "ati" steps.append((drop_kernel_mod, "fglrx",)) else: steps.append((fix_possible_opengl_misconfiguration, "ati")) steps.append((copy_ati_settings_on_desktop,)) steps.append((opengl_activate, "ati")) elif forced_xdriver == "nvidia" and (not noproprietary): generate_nvidia_steps(videocard, cardnumber, total_cards, bus_id) elif forced_xdriver == "vesa": forced_monitor_modes = True else: generate_generic_steps() steps.append((set_xorg_device, forced_xdriver, cardnumber, total_cards, bus_id,)) write_config = True else: company_id, card_id = extract_pci_ids(videocard) print("[%s] company_id: %s | card_id: %s" % ( cardnumber, company_id, card_id,)) if company_id == "10de": # NVIDIA if noproprietary: steps.append((set_xorg_device, "nv", cardnumber, total_cards, bus_id,)) else: generate_nvidia_steps( videocard, cardnumber, total_cards, bus_id) print("NVIDIA!") write_config = True elif company_id == "1002": generate_fglrx_steps( videocard, cardnumber, total_cards, bus_id) write_config = True else: generate_generic_steps() print("GPU will be automatically detected by X.Org and udevd") # now create the file for args in steps: func, args = args[0], args[1:] func(*args) if write_config: config = xorg_conf_structure.replace( '__device_section__', '\n\n'.join(device_sections)) config = config.replace( '__screen_section__', '\n\n'.join(screen_sections)) config = config.replace( '__screen_layout_section__', '\n '.join(screen_layout_sections)) if forced_monitor_modes: config = config.replace('#Modes', 'Modes') if not dryrun: with open(xorgfile, "w") as f: f.write(config) f.flush() else: try: os.remove(xorgfile) except (OSError, IOError): pass raise SystemExit(0)