diff --git a/NvidiaDetector/nvidiadetector.py b/NvidiaDetector/nvidiadetector.py index 66b3289..104ba14 100644 --- a/NvidiaDetector/nvidiadetector.py +++ b/NvidiaDetector/nvidiadetector.py @@ -144,6 +144,7 @@ class NvidiaDetection(object): ''' self.drivers = {} vendor_product_re = re.compile('pci:v0000(.+)d0000(.+)sv') + package_re = re.compile('nvidia(?:\-driver|)\-([0-9]+)(:?\-server|)(:?\:i386|)') for package in apt.Cache(): if (not package.name.startswith('nvidia-') or @@ -161,7 +162,14 @@ class NvidiaDetection(object): # package names can be like "nvidia-173:i386" and we need to # extract the driver flavour from the name e.g. "173" - stripped_package_name = package.name.split('-')[-1].split(':', 1)[0] + package_match = package_re.match(package.name) + if package_match: + stripped_package_name = package_match.group(1) + else: + logging.error('%s package has unexpected name scheme. Skipping' % ( + package.name)) + continue + driver_version = self.__get_value_from_name(stripped_package_name) try: diff --git a/UbuntuDrivers/detect.py b/UbuntuDrivers/detect.py index 41c66db..f1515b2 100644 --- a/UbuntuDrivers/detect.py +++ b/UbuntuDrivers/detect.py @@ -20,7 +20,7 @@ import apt from UbuntuDrivers import kerneldetection system_architecture = apt.apt_pkg.get_architectures()[0] - +lookup_cache = {} def system_modaliases(sys_path=None): '''Get modaliases present in the system. @@ -222,6 +222,21 @@ def _pkg_get_support(pkg): return support +def _is_runtimepm_supported(pkg, alias): + '''Check if the package supports runtimepm for the given modalias''' + try: + m = pkg.candidate.record['PmAliases'] + except (KeyError, AttributeError, UnicodeDecodeError): + return False + else: + if m.find('nvidia(') != 0: + return False + + n = m[m.find('(')+1: m.find(')')] + modaliases = n.split(', ') + return any(fnmatch.fnmatch(alias.lower(), regex.lower()) for regex in modaliases) + + def _is_manual_install(pkg): '''Determine if the kernel module from an apt.Package is manually installed.''' @@ -239,13 +254,18 @@ def _is_manual_install(pkg): if not module: return False - modinfo = subprocess.Popen(['modinfo', module], stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - modinfo.communicate() - if modinfo.returncode == 0: - logging.debug('_is_manual_install %s: builds module %s which is available, manual install', - pkg.name, module) - return True + try: + modinfo = subprocess.Popen(['modinfo', module], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + modinfo.communicate() + except (OSError, FileNotFoundError, subprocess.CalledProcessError) as e: + logging.debug('_is_manual_install failed: %s', str(e)) + return False + else: + if modinfo.returncode == 0: + logging.debug('_is_manual_install %s: builds module %s which is available, manual install', + pkg.name, module) + return True logging.debug('_is_manual_install %s: builds module %s which is not available, no manual install', pkg.name, module) @@ -316,10 +336,15 @@ def system_driver_packages(apt_cache=None, sys_path=None, freeonly=False, includ versions; these have this flag, where exactly one has recommended == True, and all others False. ''' + global lookup_cache modaliases = system_modaliases(sys_path) if not apt_cache: - apt_cache = apt.Cache() + try: + apt_cache = apt.Cache() + except Exception as ex: + logging.error(ex) + return {} packages = {} for alias, syspath in modaliases.items(): @@ -334,6 +359,7 @@ def system_driver_packages(apt_cache=None, sys_path=None, freeonly=False, includ 'free': _is_package_free(p), 'from_distro': _is_package_from_distro(p), 'support': _pkg_get_support(p), + 'runtimepm': _is_runtimepm_supported(p, alias) } (vendor, model) = _get_db_name(syspath, alias) if vendor is not None: @@ -344,19 +370,16 @@ def system_driver_packages(apt_cache=None, sys_path=None, freeonly=False, includ # Add "recommended" flags for NVidia alternatives nvidia_packages = [p for p in packages if p.startswith('nvidia-')] if nvidia_packages: + # Create a cache for looking up drivers to pick the best + # candidate + for key, value in packages.items(): + if key.startswith('nvidia-'): + lookup_cache[key] = value nvidia_packages.sort(key=functools.cmp_to_key(_cmp_gfx_alternatives)) recommended = nvidia_packages[-1] for p in nvidia_packages: packages[p]['recommended'] = (p == recommended) - # Add "recommended" flags for fglrx alternatives - fglrx_packages = [p for p in packages if p.startswith('fglrx-')] - if fglrx_packages: - fglrx_packages.sort(key=functools.cmp_to_key(_cmp_gfx_alternatives)) - recommended = fglrx_packages[-1] - for p in fglrx_packages: - packages[p]['recommended'] = (p == recommended) - # add available packages which need custom detection code for plugin, pkgs in detect_plugin_packages(apt_cache).items(): for p in pkgs: @@ -457,7 +480,11 @@ def system_device_specific_metapackages(apt_cache=None, sys_path=None, include_o modaliases = system_modaliases(sys_path) if not apt_cache: - apt_cache = apt.Cache() + try: + apt_cache = apt.Cache() + except Exception as ex: + logging.error(ex) + return {} packages = {} for alias, syspath in modaliases.items(): @@ -509,11 +536,16 @@ def system_gpgpu_driver_packages(apt_cache=None, sys_path=None): versions; these have this flag, where exactly one has recommended == True, and all others False. ''' + global lookup_cache vendors_whitelist = ['10de'] modaliases = system_modaliases(sys_path) if not apt_cache: - apt_cache = apt.Cache() + try: + apt_cache = apt.Cache() + except Exception as ex: + logging.error(ex) + return {} packages = {} for alias, syspath in modaliases.items(): @@ -540,6 +572,11 @@ def system_gpgpu_driver_packages(apt_cache=None, sys_path=None): # Add "recommended" flags for NVidia alternatives nvidia_packages = [p for p in packages if p.startswith('nvidia-')] if nvidia_packages: + # Create a cache for looking up drivers to pick the best + # candidate + for key, value in packages.items(): + if key.startswith('nvidia-'): + lookup_cache[key] = value nvidia_packages.sort(key=functools.cmp_to_key(_cmp_gfx_alternatives_gpgpu)) recommended = nvidia_packages[-1] for p in nvidia_packages: @@ -603,7 +640,11 @@ def system_device_drivers(apt_cache=None, sys_path=None, freeonly=False): ''' result = {} if not apt_cache: - apt_cache = apt.Cache() + try: + apt_cache = apt.Cache() + except Exception as ex: + logging.error(ex) + return {} # copy the system_driver_packages() structure into the by-device structure for pkg, pkginfo in system_driver_packages(apt_cache, sys_path, @@ -826,7 +867,11 @@ def detect_plugin_packages(apt_cache=None): return packages if apt_cache is None: - apt_cache = apt.Cache() + try: + apt_cache = apt.Cache() + except Exception as ex: + logging.error(ex) + return {} for fname in os.listdir(plugindir): if not fname.endswith('.py'): @@ -860,26 +905,32 @@ def detect_plugin_packages(apt_cache=None): return packages +def _pkg_support_from_cache(x): + '''Look up driver package and return their support level''' + if lookup_cache.get(x): + return lookup_cache.get(x).get('support') + return None + + def _cmp_gfx_alternatives(x, y): - '''Compare two graphics driver names in terms of preference. + '''Compare two graphics driver names in terms of preference. (desktop) - -updates always sort after non-updates, as we prefer the stable driver and - only want to offer -updates when the one from release does not support the - card. We never want to recommend -experimental unless it's the only one - available, so sort this last. + NFB (New Feature Branch) or the latest is what we recommend on the desktop. -server always sorts after non-server. + LTSB (Long Term Support Branch) always sorts before NFB (New Feature Branch). + Legacy always sorts before Beta. ''' - if x.endswith('-updates') and not y.endswith('-updates'): + if _pkg_support_from_cache(x) == 'Legacy' and _pkg_support_from_cache(y) != 'Legacy': return -1 - if not x.endswith('-updates') and y.endswith('-updates'): + if _pkg_support_from_cache(x) != 'Legacy' and _pkg_support_from_cache(y) == 'Legacy': return 1 - if x.endswith('-server') and not y.endswith('-server'): + if _pkg_support_from_cache(x) == 'Beta' and _pkg_support_from_cache(y) != 'Beta': return -1 - if not x.endswith('-server') and y.endswith('-server'): + if _pkg_support_from_cache(x) != 'Beta' and _pkg_support_from_cache(y) == 'Beta': return 1 - if 'experiment' in x and 'experiment' not in y: + if x.endswith('-server') and not y.endswith('-server'): return -1 - if 'experiment' not in x and 'experiment' in y: + if not x.endswith('-server') and y.endswith('-server'): return 1 if x < y: return -1 @@ -890,25 +941,27 @@ def _cmp_gfx_alternatives(x, y): def _cmp_gfx_alternatives_gpgpu(x, y): - '''Compare two graphics driver names in terms of preference. + '''Compare two graphics driver names in terms of preference. (server) - -updates always sort after non-updates, as we prefer the stable driver and - only want to offer -updates when the one from release does not support the - card. We never want to recommend -experimental unless it's the only one - available, so sort this last. -server always sorts before non-server. + LTSB (Long Term Support Branch) always sorts before NFB (New Feature Branch). + Legacy always sorts before Beta. ''' - if x.endswith('-updates') and not y.endswith('-updates'): - return -1 - if not x.endswith('-updates') and y.endswith('-updates'): - return 1 if x.endswith('-server') and not y.endswith('-server'): return 1 if not x.endswith('-server') and y.endswith('-server'): return -1 - if 'experiment' in x and 'experiment' not in y: + if _pkg_support_from_cache(x) == 'LTSB' and _pkg_support_from_cache(y) != 'LTSB': + return 1 + if _pkg_support_from_cache(x) != 'LTSB' and _pkg_support_from_cache(y) == 'LTSB': + return -1 + if _pkg_support_from_cache(x) == 'Legacy' and _pkg_support_from_cache(y) != 'Legacy': + return -1 + if _pkg_support_from_cache(x) != 'Legacy' and _pkg_support_from_cache(y) == 'Legacy': + return 1 + if _pkg_support_from_cache(x) == 'Beta' and _pkg_support_from_cache(y) != 'Beta': return -1 - if 'experiment' not in x and 'experiment' in y: + if _pkg_support_from_cache(x) != 'Beta' and _pkg_support_from_cache(y) == 'Beta': return 1 if x < y: return -1 diff --git a/debian/changelog b/debian/changelog index 63221b3..bdfd7f6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +ubuntu-drivers-common (1:0.8.6) UNRELEASED; urgency=medium + + * + + -- Alberto Milone Mon, 31 Aug 2020 17:42:08 +0200 + ubuntu-drivers-common (1:0.8.5.4) groovy; urgency=medium * ubuntu-drivers: diff --git a/debian/control b/debian/control index e20b397..14363e3 100644 --- a/debian/control +++ b/debian/control @@ -9,7 +9,7 @@ Build-Depends: debhelper (>= 9.20160709), python3-all (>= 3.2), python3-setuptools, python3-click, - libpciaccess-dev (>= 0.12.1-2), + libpci-dev, lib32gcc-s1 [amd64], libc6-i386 [amd64], linux-libc-dev, pkg-config, @@ -42,7 +42,6 @@ Depends: ${python3:Depends}, ${misc:Depends}, ${shlibs:Depends}, debconf (>= 0.5.00) | debconf-2.0, - pciutils, python3-apt, python3-xkit, python3-click, diff --git a/debian/copyright b/debian/copyright index f3d7897..1799938 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,13 +1,17 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Files: * -Copyright: Copyright (c) 2008 Alberto Milone +Copyright: Copyright (c) 2008 Alberto Milone License: GPL-2+ Files: UbuntuDrivers/* tests/ubuntu_drivers.py ubuntu-drivers Copyright: Copyright (c) 2012 Canonical Ltd. License: GPL-2+ +Files: share/hybrid/json-parser/* +Copyright: Copyright (c) 2012, 2013 James McLaughlin et al. +License: BSD + License: GPL-2+ 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 @@ -16,3 +20,18 @@ License: GPL-2+ . The full text of the GPL is distributed as in /usr/share/common-licenses/GPL-2 on Debian systems. + +License: BSD + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + The full text of the BSD is distributed as in + /usr/share/common-licenses/BSD on Debian systems. diff --git a/debian/rules b/debian/rules index d2442b8..44d30d6 100755 --- a/debian/rules +++ b/debian/rules @@ -48,4 +48,5 @@ override_dh_clean: rm -rf *.egg-info rm -f debian/ubuntu-drivers-common.maintscript find $(CURDIR) -name "__pycache__" -exec rm -rf "{}" \; + make -C share/hybrid clean dh_clean diff --git a/share/hybrid/Makefile b/share/hybrid/Makefile index ad5af01..e5e12b0 100644 --- a/share/hybrid/Makefile +++ b/share/hybrid/Makefile @@ -3,12 +3,17 @@ PROGRAM = gpu-manager PROGRAM_FILES = gpu-manager.c CC = gcc -CFLAGS =-g -Wall $(shell pkg-config --cflags --libs pciaccess libdrm libkmod) +CFLAGS =-g -Wall $(shell pkg-config --cflags libpci libdrm libkmod) -Ijson-parser json-parser/json.c +LDFLAGS =$(shell pkg-config --libs libpci libdrm libkmod) -lm all: build -build: - $(CC) -o $(PROGRAM) $(PROGRAM_FILES) $(CFLAGS) +gpu-manager: gpu-manager.o + $(CC) $(CFLAGS) gpu-manager.o $(LDFLAGS) -o gpu-manager +gpu-manager.o: + $(CC) $(CFLAGS) -c gpu-manager.c + +build: gpu-manager clean: - @rm -f $(PROGRAM) + @rm -f $(PROGRAM) $(PROGRAM).o json.o diff --git a/share/hybrid/gpu-manager.c b/share/hybrid/gpu-manager.c index a983ef1..c039e2d 100644 --- a/share/hybrid/gpu-manager.c +++ b/share/hybrid/gpu-manager.c @@ -14,6 +14,8 @@ * * Copyright (c) 1997-2003 by The XFree86 Project, Inc. * + * PCI code based on example.c from pciutils, by Martin Mares. + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation @@ -48,7 +50,7 @@ #include #include #include -#include +#include #include #include #include @@ -61,6 +63,7 @@ #include #include "xf86drm.h" #include "xf86drmMode.h" +#include "json.h" static inline void freep(void *); static inline void fclosep(FILE **); @@ -74,8 +77,8 @@ static inline void pclosep(FILE **); #define PCI_CLASS_DISPLAY_OTHER 0x0380 #define PCIINFOCLASSES(c) \ - ( (((c) & 0x00ff0000) \ - == (PCI_CLASS_DISPLAY << 16)) ) + ( (( ((c) >> 8) & 0xFF) \ + == PCI_CLASS_DISPLAY) ) #define LAST_BOOT "/var/lib/ubuntu-drivers-common/last_gfx_boot" #define OFFLOADING_CONF "/var/lib/ubuntu-drivers-common/requires_offloading" @@ -123,10 +126,8 @@ static char *modprobe_d_path = NULL; static char *xorg_conf_d_path = NULL; static prime_intel_drv prime_intel_driver = SNA; static prime_mode_settings prime_mode = OFF; - -static struct pci_slot_match match = { - PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 0 -}; +static bool nvidia_runtimepm_supported = false; +static bool nvidia_runtimepm_enabled = false; struct device { int boot_vga; @@ -141,6 +142,17 @@ struct device { }; +/* Struct we use to check PME */ +struct pme_dev { + struct pme_dev *next; + struct pci_dev *dev; + /* Cache */ + unsigned int config_cached, config_bufsize; + u8 *config; /* Cached configuration space data */ + u8 *present; /* Maps which configuration bytes are present */ +}; + + static bool is_file(char *file); static bool is_dir(char *directory); static bool is_dir_empty(char *directory); @@ -163,6 +175,180 @@ static inline void pclosep(FILE **file) { pclose(*file); } +/* Beginning of the parts from lspci.{c|h} */ +static int +pme_config_fetch(struct pme_dev *pm_dev, unsigned int pos, unsigned int len) +{ + unsigned int end = pos+len; + int result; + + while (pos < pm_dev->config_bufsize && len && pm_dev->present[pos]) + pos++, len--; + while (pos+len <= pm_dev->config_bufsize && len && pm_dev->present[pos+len-1]) + len--; + if (!len) + return 1; + + if (end > pm_dev->config_bufsize) { + int orig_size = pm_dev->config_bufsize; + while (end > pm_dev->config_bufsize) + pm_dev->config_bufsize *= 2; + pm_dev->config = realloc(pm_dev->config, pm_dev->config_bufsize); + if (! pm_dev->config) { + fprintf(log_handle, "Error: failed to reallocate pm_dev config\n"); + return 0; + } + pm_dev->present = realloc(pm_dev->present, pm_dev->config_bufsize); + if (! pm_dev->present) { + fprintf(log_handle, "Error: failed to reallocate pm_dev present\n"); + return 0; + } + memset(pm_dev->present + orig_size, 0, pm_dev->config_bufsize - orig_size); + } + result = pci_read_block(pm_dev->dev, pos, pm_dev->config + pos, len); + if (result) + memset(pm_dev->present + pos, 1, len); + return result; +} + + +/* Config space accesses */ +static void +check_conf_range(struct pme_dev *pm_dev, unsigned int pos, unsigned int len) +{ + while (len) + if (!pm_dev->present[pos]) { + fprintf(log_handle, + "Internal bug: Accessing non-read configuration byte at position %x\n", + pos); + } + else { + pos++, len--; + } +} + + +static u8 +get_conf_byte(struct pme_dev *pm_dev, unsigned int pos) +{ + check_conf_range(pm_dev, pos, 1); + return pm_dev->config[pos]; +} + + +static u16 +get_conf_word(struct pme_dev *pm_dev, unsigned int pos) +{ + check_conf_range(pm_dev, pos, 2); + return pm_dev->config[pos] | (pm_dev->config[pos+1] << 8); +} + + +static bool get_d3_substates(struct pme_dev *d, bool *d3cold, bool *d3hot) { + bool status = false; + int where = PCI_CAPABILITY_LIST; + + if (get_conf_word(d, PCI_STATUS) & PCI_STATUS_CAP_LIST) { + u8 been_there[256]; + where = get_conf_byte(d, where) & ~3; + memset(been_there, 0, 256); + while (where) { + int id, cap; + /* Look for the capabilities */ + if (!pme_config_fetch(d, where, 4)) { + fprintf(log_handle, "Warning: access to PME Capabilities was denied\n"); + break; + } + id = get_conf_byte(d, where + PCI_CAP_LIST_ID); + cap = get_conf_word(d, where + PCI_CAP_FLAGS); + if (been_there[where]++) { + /* chain looped */ + break; + } + if (id == 0xff) { + /* chain broken */ + break; + } + switch (id) { + case PCI_CAP_ID_NULL: + break; + case PCI_CAP_ID_PM: + *d3hot = (cap & PCI_PM_CAP_PME_D3_HOT); + *d3cold = (cap & PCI_PM_CAP_PME_D3_COLD); + status = true; + break; + } + } + } + return status; +} + + +static struct pme_dev * +scan_device(struct pci_dev *p) { + struct pme_dev *pm_dev = NULL; + + pm_dev = malloc(sizeof(struct pme_dev)); + if (!pm_dev) { + fprintf(log_handle, "Error: failed to allocate pme_dev\n"); + return NULL; + } + memset(pm_dev, 0, sizeof(*pm_dev)); + pm_dev->dev = p; + pm_dev->config_cached = pm_dev->config_bufsize = 64; + pm_dev->config = malloc(64); + if (!pm_dev->config) { + fprintf(log_handle, "Error: failed to allocate pme_dev config\n"); + return NULL; + } + pm_dev->present = malloc(64); + if (!pm_dev->present) { + fprintf(log_handle, "Error: failed to allocate pme_dev present\n"); + return NULL; + } + memset(pm_dev->present, 1, 64); + if (!pci_read_block(p, 0, pm_dev->config, 64)) { + fprintf(log_handle, "lspci: Unable to read the standard configuration space header of pm_dev %04x:%02x:%02x.%d\n", + p->domain, p->bus, p->dev, p->func); + return NULL; + } + if ((pm_dev->config[PCI_HEADER_TYPE] & 0x7f) == PCI_HEADER_TYPE_CARDBUS) { + /* For cardbus bridges, we need to fetch 64 bytes more to get the + * full standard header... */ + if (pme_config_fetch(pm_dev, 64, 64)) + pm_dev->config_cached += 64; + } + pci_setup_cache(p, pm_dev->config, pm_dev->config_cached); + /* We don't need this again */ + //pci_fill_info(p, PCI_FILL_IDENT | PCI_FILL_CLASS); + return pm_dev; +} +/* End parts from lspci.{c|h} */ + + +static bool pci_device_is_boot_vga(struct pci_dev *info) { + size_t len = 0; + _cleanup_free_ char *boot_vga_str = NULL; + _cleanup_fclose_ FILE *file = NULL; + char sysfs_path[1024]; + + snprintf(sysfs_path, sizeof(sysfs_path), + "/sys/bus/pci/devices/%04x:%02x:%02x.%d/boot_vga", + info->domain, info->bus, info->dev, info->func); + + /* Check the driver version */ + file = fopen(sysfs_path, "r"); + if (file == NULL) { + fprintf(log_handle, "can't open %s\n", sysfs_path); + return false; + } + if (getline(&boot_vga_str, &len, file) == -1) { + fprintf(log_handle, "can't get line from %s\n", sysfs_path); + return false; + } + return (strcmp(boot_vga_str, "1\n") == 0); +} + /* Trim string in place */ static void trim(char *str) { @@ -947,7 +1133,7 @@ static bool is_link(char *file) { /* See if the device is bound to a driver */ -static bool is_device_bound_to_driver(struct pci_device *info) { +static bool is_device_bound_to_driver(struct pci_dev *info) { char sysfs_path[1024]; snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/pci/devices/%04x:%02x:%02x.%d/driver", @@ -958,7 +1144,7 @@ static bool is_device_bound_to_driver(struct pci_device *info) { /* See if the device is a pci passthrough */ -static bool is_device_pci_passthrough(struct pci_device *info) { +static bool is_device_pci_passthrough(struct pci_dev *info) { enum { BUFFER_SIZE = 1024 }; char buf[BUFFER_SIZE], sysfs_path[BUFFER_SIZE], *drv, *name; ssize_t length; @@ -1159,7 +1345,7 @@ static bool requires_offloading(struct device **devices, int i; bool status = false; for(i = 0; i < cards_n; i++) { - if (devices[i]->vendor_id == INTEL) { + if (devices[i]->boot_vga) { status = (devices[i]->has_connected_outputs == 1); break; } @@ -1226,15 +1412,25 @@ static bool create_prime_settings(const char *prime_settings) { prime_settings); return false; } - /* Set prime to "on" */ - fprintf(file, "on\n"); + /* Make on-demand the default if + * runtimepm is supported. + */ + if (nvidia_runtimepm_supported) { + /* Set prime to "on-demand" */ + fprintf(file, "on-demand\n"); + } + else { + /* Set prime to "on" */ + fprintf(file, "on\n"); + } + fflush(file); return true; } -static bool get_nvidia_driver_version(int *major, int *minor) { +static bool get_nvidia_driver_version(int *major, int *minor, int *extra) { int status; size_t len = 0; @@ -1252,15 +1448,41 @@ static bool get_nvidia_driver_version(int *major, int *minor) { return false; } - status = sscanf(driver_version, "%d.%d\n", major, minor); + status = sscanf(driver_version, "%d.%d.%d\n", major, minor, extra); /* Make sure that we match "desired_matches" */ - if (status == EOF || status != 2) { + if (status == EOF || status < 2 ) { fprintf(log_handle, "Warning: couldn't get the driver version from %s\n", nvidia_driver_version_path); return false; } + if (status == 2) + *extra = -1; + + return true; +} + + +static bool get_kernel_version(int *major, int *minor, int *extra) { + struct utsname buffer; + int status; + _cleanup_free_ char *version = NULL; + + errno = 0; + if (uname(&buffer) != 0) { + return false; + } + + status = sscanf(buffer.release, "%d.%d.%d\n", major, minor, extra); + + /* Make sure that we match "desired_matches" */ + if (status == EOF || status != 3 ) { + fprintf(log_handle, "Warning: couldn't get the kernel version from %s\n", + buffer.release); + return false; + } + return true; } @@ -1441,6 +1663,65 @@ static int remove_offload_serverlayout(void) { return remove_xorg_d_custom_file("11-nvidia-offload.conf"); } + +static bool create_runtime_file(const char *name) { + _cleanup_fclose_ FILE *file = NULL; + char path[PATH_MAX]; + + snprintf(path, sizeof(path), + "%s/%s", gpu_detection_path, name); + + fprintf(log_handle, "Trying to create new file: %s\n", + path); + + file = fopen(path, "w"); + if (file == NULL) { + fprintf(log_handle, "I couldn't open %s for writing.\n", + path); + return false; + } + fprintf(file, "yes\n"); + fflush(file); + + return true; +} + + +static bool create_nvidia_runtime_config(void) { + _cleanup_fclose_ FILE *file = NULL; + char path[] = "/lib/modprobe.d/nvidia-runtimepm.conf"; + + fprintf(log_handle, "Trying to create new file: %s\n", + path); + + file = fopen(path, "w"); + if (file == NULL) { + fprintf(log_handle, "I couldn't open %s for writing.\n", + path); + return false; + } + fprintf(file, "options nvidia \"NVreg_DynamicPowerManagement=0x02\"\n"); + fflush(file); + + return true; +} + + +static int remove_nvidia_runtime_config(void) { + struct stat st; + char path[] = "/lib/modprobe.d/nvidia-runtimepm.conf"; + + if (stat(path, &st) == 0) { + fprintf(log_handle, "Trying to remove file: %s\n", + path); + if (unlink(path) == 0) { + return 0; + } + } + return -errno; +} + + static bool manage_power_management(const struct device *device, bool enabled) { _cleanup_fclose_ FILE *file = NULL; char pci_device_path[PATH_MAX]; @@ -1468,10 +1749,14 @@ static bool manage_power_management(const struct device *device, bool enabled) { static void enable_power_management(const struct device *device) { manage_power_management(device, true); + if (nvidia_runtimepm_supported && ! nvidia_runtimepm_enabled) { + create_nvidia_runtime_config(); + } } static void disable_power_management(const struct device *device) { manage_power_management(device, false); + remove_nvidia_runtime_config(); } static bool unload_nvidia(void) { @@ -1730,6 +2015,261 @@ unload_again: } +static bool json_find_feature_in_array(char* feature, json_value* value) +{ + int length, x; + if (value == NULL) { + return false; + } + + length = value->u.array.length; + fprintf(log_handle, "Looking for availability of \"%s\" feature\n", feature); + for (x = 0; x < length; x++) { + /* fprintf(log_handle, "feature string: %s\n", value->u.array.values[x]->u.string.ptr); */ + if (strcmp(value->u.array.values[x]->u.string.ptr, feature) == 0) { + fprintf(log_handle, "\"%s\" feature found\n", feature); + return true; + } + } + fprintf(log_handle, "\"%s\" feature not found\n", feature); + return false; +} + + +static bool json_process_object_for_device_id(int id, json_value* value) +{ + int length, x; + if (value == NULL) { + return false; + } + length = value->u.object.length; + for (x = 0; x < length; x++) { + if (strcmp(value->u.object.values[x].name, "devid") == 0) { + if (strtol(value->u.object.values[x].value->u.string.ptr, NULL, 16) == id) { + fprintf(log_handle, "Device ID %s found in json file\n", value->u.object.values[x].value->u.string.ptr); + return true; + } + } + } + + return false; +} + + +static bool json_find_feature_in_object(char* feature, json_value* value) +{ + int length, x; + if (value == NULL) { + return false; + } + length = value->u.object.length; + for (x = 0; x < length; x++) { + if (strcmp(value->u.object.values[x].name, "name") == 0) { + fprintf(log_handle, "Device name: %s\n", value->u.object.values[x].value->u.string.ptr); + } + if (strcmp(value->u.object.values[x].name, "features") == 0) { + /* fprintf(log_handle, "features: %s\n", value->u.object.values[x].value->u.string.ptr);*/ + return (json_find_feature_in_array(feature, value->u.object.values[x].value)); + } + } + return false; +} + + +static json_value* json_find_device_id_in_array(int id, json_value* value) +{ + int length, x; + if (value == NULL) { + return NULL; + } + length = value->u.array.length; + for (x = 0; x < length; x++) { + if (json_process_object_for_device_id(id, value->u.array.values[x])) { + /*fprintf(log_handle, "Device ID found in object %d\n", x);*/ + return value->u.array.values[x]; + break; + } + } + fprintf(log_handle, "Device ID \"0x%x\" not found in json file\n", id); + + return NULL; +} + + +static json_value* json_find_device_id(int id, json_value* value) +{ + fprintf(log_handle, "Looking for device ID \"0x%x\" in json file\n", id); + int length, x; + json_value* chips = NULL; + if (value == NULL) { + return NULL; + } + + + length = value->u.object.length; + for (x = 0; x < length; x++) { + /*fprintf(log_handle, "object[%d].name = %s\n", x, value->u.object.values[x].name);*/ + if (strcmp(value->u.object.values[x].name, "chips") == 0) { + chips = value->u.object.values[x].value; + /*fprintf(log_handle, "Chips found\n");*/ + } + + } + + if (chips) { + /*fprintf(log_handle, "processing chips\n");*/ + return (json_find_device_id_in_array(id, chips)); + } + else { + fprintf(log_handle, "Error: json chips array not found. Aborting\n"); + } + return NULL; +} + + +static bool find_supported_gpus_json(char **json_file){ + int major, minor, extra; + _cleanup_fclose_ FILE *file = NULL; + + if (get_nvidia_driver_version(&major, &minor, &extra)) { + if (major >= 450) { + if (extra > -1) { + asprintf(json_file, "/usr/share/doc/nvidia-driver-%d-server/supported-gpus.json", + major); + } + else { + asprintf(json_file, "/usr/share/doc/nvidia-driver-%d/supported-gpus.json", + major); + } + fprintf(log_handle, "Found json file: %s\n", *json_file); + } + } + else { + fprintf(log_handle, "Warning: cannot check the NVIDIA driver major version\n"); + return false; + } + + return true; +} + + +/* Look up the device ID in the json file for RTD3 support */ +static bool is_nv_runtimepm_supported(int nv_device_id) { + _cleanup_free_ char *json_file = NULL; + _cleanup_fclose_ FILE *file = NULL; + struct stat filestatus; + int file_size; + char* file_contents; + json_char* json; + json_value* value; + json_value* device_value; + int major, minor, extra; + + bool supported = false; + + if(find_supported_gpus_json(&json_file)) { + if ( stat(json_file, &filestatus) != 0) { + fprintf(log_handle, "File %s not found\n", json_file); + return false; + } + + file_size = filestatus.st_size; + file_contents = (char*)malloc(filestatus.st_size); + if (file_contents == NULL) { + fprintf(log_handle, "Memory error: unable to allocate %d bytes\n", file_size); + return false; + } + + file = fopen(json_file, "rt"); + if (file == NULL) { + fprintf(log_handle, "Unable to open %s\n", json_file); + free(file_contents); + return false; + } + if (fread(file_contents, file_size, 1, file) != 1 ) { + fprintf(log_handle, "Unable t read content of %s\n", json_file); + free(file_contents); + return false; + } + + json = (json_char*)file_contents; + value = json_parse(json,file_size); + + if (value == NULL) { + fprintf(log_handle, "Unable to parse data\n"); + free(file_contents); + return false; + } + + device_value = json_find_device_id(nv_device_id, value); + supported = json_find_feature_in_object("runtimepm", device_value); + + device_value = NULL; + json_value_free(value); + free(file_contents); + } + + if (! get_kernel_version(&major, &minor, &extra)) { + fprintf(log_handle, "Failed to check kernel version. Disabling runtimepm.\n"); + return false; + } + + if (major > 4 || ((major == 4) && (minor >= 18))) { + fprintf(log_handle, "Linux %d.%d detected.\n", major, minor); + } + else { + fprintf(log_handle, "Linux %d.%d detected. Linux 4.18 or newer is required for runtimepm\n", + major, minor); + supported = false; + } + + return supported; +} + + +/* Check the procfs entry for the NVIDIA GPU to check the RTD3 status */ +static bool is_nv_runtimepm_enabled(struct pci_dev *device) { + size_t read; + char proc_gpu_path[PATH_MAX]; + _cleanup_fclose_ FILE *file = NULL; + _cleanup_free_ char *line = NULL; + char pattern[] = "Runtime D3 status:"; + size_t len = 0; + size_t pattern_len = strlen(pattern); + + + snprintf(proc_gpu_path, sizeof(proc_gpu_path), + "/proc/driver/nvidia/gpus/%04x:%02x:%02x.%x/power", + (unsigned int)device->domain, + (unsigned int)device->bus, + (unsigned int)device->dev, + (unsigned int)device->func); + + fprintf(log_handle, "Checking power status in %s\n", proc_gpu_path); + file = fopen(proc_gpu_path, "r"); + if (!file) { + fprintf(log_handle, "Error while opening %s\n", proc_gpu_path); + return false; + } + else { + while ((read = getline(&line, &len, file)) != -1) { + if (istrstr(line, pattern) != NULL) { + fprintf(log_handle, "%s", line); + if (strncmp(line, pattern, pattern_len) == 0) { + if (istrstr(line, "enabled") != NULL) { + return true; + } + else { + return false; + } + } + } + } + } + return false; +} + + int main(int argc, char *argv[]) { int opt, i; @@ -1767,10 +2307,12 @@ int main(int argc, char *argv[]) { /* The number of cards from last boot*/ int last_cards_n = 0; - /* Variables for pciaccess */ - int pci_init = -1; - struct pci_device_iterator *iter = NULL; - struct pci_device *info = NULL; + /* Variables for pci capabilities */ + bool d3hot = false; + bool d3cold = false; + struct pci_access *pacc = NULL; + struct pme_dev *pm_dev = NULL; + struct pci_dev *dev = NULL; /* Store the devices here */ struct device *current_devices[MAX_CARDS_N]; @@ -2122,76 +2664,119 @@ int main(int argc, char *argv[]) { offloading = fake_offloading; } else { - /* Get the current system data */ - pci_init = pci_system_init(); - if (pci_init != 0) - goto end; + /* Get the pci_access structure */ + pacc = pci_alloc(); - iter = pci_slot_match_iterator_create(&match); - if (!iter) + if (!pacc) { + fprintf(log_handle, "Error: failed to acces the PCI library\n"); goto end; + } + /* Initialize the PCI library */ + pci_init(pacc); + pci_scan_bus(pacc); + + /* Iterate over all devices */ + for (dev=pacc->devices; dev; dev=dev->next) { + /* Fill in header info we need */ + pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); + if (PCIINFOCLASSES(dev->device_class)) { + pm_dev = scan_device(dev); + if (pm_dev) { + fprintf(log_handle, "Vendor/Device Id: %x:%x\n", dev->vendor_id, dev->device_id); + fprintf(log_handle, "BusID \"PCI:%d@%d:%d:%d\"\n", + (int)dev->bus, (int)dev->domain, (int)dev->dev, (int)dev->func); + fprintf(log_handle, "Is boot vga? %s\n", (pci_device_is_boot_vga(dev) ? "yes" : "no")); + + if (!is_device_bound_to_driver(dev)) { + fprintf(log_handle, "The device is not bound to any driver.\n"); + } - while ((info = pci_device_next(iter)) != NULL) { - if (PCIINFOCLASSES(info->device_class)) { - fprintf(log_handle, "Vendor/Device Id: %x:%x\n", info->vendor_id, info->device_id); - fprintf(log_handle, "BusID \"PCI:%d@%d:%d:%d\"\n", - (int)info->bus, (int)info->domain, (int)info->dev, (int)info->func); - fprintf(log_handle, "Is boot vga? %s\n", (pci_device_is_boot_vga(info) ? "yes" : "no")); - - if (!is_device_bound_to_driver(info)) { - fprintf(log_handle, "The device is not bound to any driver.\n"); - } + if (is_device_pci_passthrough(dev)) { + fprintf(log_handle, "The device is a pci passthrough. Skipping...\n"); + continue; + } - if (is_device_pci_passthrough(info)) { - fprintf(log_handle, "The device is a pci passthrough. Skipping...\n"); - continue; - } + /* We don't support more than MAX_CARDS_N */ + if (cards_n < MAX_CARDS_N) { + current_devices[cards_n] = malloc(sizeof(struct device)); + if (!current_devices[cards_n]) { + if (pm_dev->config) + free(pm_dev->config); + if (pm_dev->present) + free(pm_dev->present); + free(pm_dev); + goto end; + } + current_devices[cards_n]->boot_vga = pci_device_is_boot_vga(dev); + current_devices[cards_n]->vendor_id = dev->vendor_id; + current_devices[cards_n]->device_id = dev->device_id; + current_devices[cards_n]->domain = dev->domain; + current_devices[cards_n]->bus = dev->bus; + current_devices[cards_n]->dev = dev->dev; + current_devices[cards_n]->func = dev->func; + + if (dev->vendor_id == NVIDIA) { + has_nvidia = true; + + if (!current_devices[cards_n]->boot_vga) { + nvidia_runtimepm_supported = is_nv_runtimepm_supported(dev->device_id); + + if (!nvidia_runtimepm_supported) { + /* This is a fairly expensive call, so check the database first */ + get_d3_substates(pm_dev, &d3cold, &d3hot); + nvidia_runtimepm_supported = d3hot; + } + + fprintf(log_handle, "Is nvidia runtime pm supported for \"0x%x\"? %s\n", dev->device_id, + nvidia_runtimepm_supported ? "yes" : "no"); + + if (nvidia_runtimepm_supported) + create_runtime_file("nvidia_runtimepm_supported"); + + nvidia_runtimepm_enabled = is_nv_runtimepm_enabled(dev); + fprintf(log_handle, "Is nvidia runtime pm enabled for \"0x%x\"? %s\n", dev->device_id, + nvidia_runtimepm_enabled ? "yes" : "no"); + + if (nvidia_runtimepm_enabled) + create_runtime_file("nvidia_runtimepm_enabled"); + } + } + else if (dev->vendor_id == INTEL) { + has_intel = true; + } + else if (dev->vendor_id == AMD) { + has_amd = true; + } - /* char *driver = NULL; */ - if (info->vendor_id == NVIDIA) { - has_nvidia = true; - } - else if (info->vendor_id == INTEL) { - has_intel = true; - } - else if (info->vendor_id == AMD) { - has_amd = true; - } + if (pm_dev->config) + free(pm_dev->config); + if (pm_dev->present) + free(pm_dev->present); + free(pm_dev); + } + else { + fprintf(log_handle, "Warning: too many devices %d. " + "Max supported %d. Ignoring the rest.\n", + cards_n, MAX_CARDS_N); + free(pm_dev->config); + free(pm_dev->present); + free(pm_dev); + break; + } - /* We don't support more than MAX_CARDS_N */ - if (cards_n < MAX_CARDS_N) { - current_devices[cards_n] = malloc(sizeof(struct device)); - if (!current_devices[cards_n]) - goto end; - current_devices[cards_n]->boot_vga = pci_device_is_boot_vga(info); - current_devices[cards_n]->vendor_id = info->vendor_id; - current_devices[cards_n]->device_id = info->device_id; - current_devices[cards_n]->domain = info->domain; - current_devices[cards_n]->bus = info->bus; - current_devices[cards_n]->dev = info->dev; - current_devices[cards_n]->func = info->func; - } - else { - fprintf(log_handle, "Warning: too many devices %d. " - "Max supported %d. Ignoring the rest.\n", - cards_n, MAX_CARDS_N); - break; + cards_n++; } - /* - else { - fprintf(stderr, "No hybrid graphics cards detected\n"); - break; - } - */ - cards_n++; } } - /* Add information about connected outputs */ - add_connected_outputs_info(current_devices, cards_n); - - /* See if it requires RandR offloading */ - offloading = requires_offloading(current_devices, cards_n); + /* Close everything */ + pci_cleanup(pacc); + pacc = NULL; } + /* Add information about connected outputs */ + add_connected_outputs_info(current_devices, cards_n); + + /* See if it requires RandR offloading */ + offloading = requires_offloading(current_devices, cards_n); fprintf(log_handle, "Does it require offloading? %s\n", (offloading ? "yes" : "no")); @@ -2244,7 +2829,7 @@ int main(int argc, char *argv[]) { &boot_vga_vendor_id, &boot_vga_device_id); - if (boot_vga_vendor_id == INTEL) { + if ((boot_vga_vendor_id == INTEL) || (boot_vga_vendor_id == AMD)) { if (offloading && nvidia_unloaded) { /* NVIDIA PRIME */ fprintf(log_handle, "PRIME detected\n"); @@ -2266,12 +2851,7 @@ int main(int argc, char *argv[]) { } goto end; } - else { - fprintf(log_handle, "Nothing to do\n"); - } - } - else if (boot_vga_vendor_id == AMD) { - if (has_changed && amdgpu_loaded && amdgpu_is_pro && amdgpu_pro_px_installed) { + else if (has_changed && amdgpu_loaded && amdgpu_is_pro && amdgpu_pro_px_installed) { /* If amdgpu-pro-px exists, we can assume it's a pxpress system. But now the * system has one card only, user probably disabled Switchable Graphics in * BIOS. So we need to use discrete config file here. @@ -2300,9 +2880,9 @@ int main(int argc, char *argv[]) { get_first_discrete(current_devices, cards_n, &discrete_device); - /* Intel + another GPU */ - if (boot_vga_vendor_id == INTEL) { - fprintf(log_handle, "Intel IGP detected\n"); + /* Intel or AMD + another GPU */ + if ((boot_vga_vendor_id == INTEL) || (boot_vga_vendor_id == AMD)) { + fprintf(log_handle, "%s IGP detected\n", (boot_vga_vendor_id == INTEL) ? "Intel" : "AMD"); /* AMDGPU-Pro Switchable */ if (has_changed && amdgpu_loaded && amdgpu_is_pro && amdgpu_pro_px_installed) { /* Similar to switchable enabled -> disabled case, but this time @@ -2313,9 +2893,9 @@ int main(int argc, char *argv[]) { run_amdgpu_pro_px(MODE_POWERSAVING); } /* NVIDIA Optimus */ - else if (offloading && (intel_loaded && !nouveau_loaded && + else if (offloading && ((intel_loaded || amdgpu_loaded) && !nouveau_loaded && (nvidia_loaded || nvidia_kmod_available))) { - fprintf(log_handle, "Intel hybrid system\n"); + fprintf(log_handle, "NVIDIA hybrid system\n"); /* Try to enable prime */ if (enable_prime(prime_settings, @@ -2347,11 +2927,8 @@ int main(int argc, char *argv[]) { end: - if (pci_init == 0) - pci_system_cleanup(); - - if (iter) - free(iter); + if (pacc) + pci_cleanup(pacc); if (log_file) free(log_file); @@ -2380,6 +2957,9 @@ end: if (dmi_product_version_path) free(dmi_product_version_path); + if (amdgpu_pro_px_file) + free(amdgpu_pro_px_file); + if (nvidia_driver_version_path) free(nvidia_driver_version_path); diff --git a/share/hybrid/json-parser/json.c b/share/hybrid/json-parser/json.c new file mode 100644 index 0000000..ffa9c60 --- /dev/null +++ b/share/hybrid/json-parser/json.c @@ -0,0 +1,1038 @@ +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. + * https://github.com/udp/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "json.h" + +#ifdef _MSC_VER + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #include +#endif + +const struct _json_value json_value_none; + +#include +#include +#include +#include + +typedef unsigned int json_uchar; + +/* There has to be a better way to do this */ +static const json_int_t JSON_INT_MAX = sizeof(json_int_t) == 1 + ? INT8_MAX + : (sizeof(json_int_t) == 2 + ? INT16_MAX + : (sizeof(json_int_t) == 4 + ? INT32_MAX + : INT64_MAX)); + +static unsigned char hex_value (json_char c) +{ + if (isdigit(c)) + return c - '0'; + + switch (c) { + case 'a': case 'A': return 0x0A; + case 'b': case 'B': return 0x0B; + case 'c': case 'C': return 0x0C; + case 'd': case 'D': return 0x0D; + case 'e': case 'E': return 0x0E; + case 'f': case 'F': return 0x0F; + default: return 0xFF; + } +} + +static int would_overflow (json_int_t value, json_char b) +{ + return ((JSON_INT_MAX - (b - '0')) / 10 ) < value; +} + +typedef struct +{ + unsigned long used_memory; + + unsigned int uint_max; + unsigned long ulong_max; + + json_settings settings; + int first_pass; + + const json_char * ptr; + unsigned int cur_line, cur_col; + +} json_state; + +static void * default_alloc (size_t size, int zero, void * user_data) +{ + return zero ? calloc (1, size) : malloc (size); +} + +static void default_free (void * ptr, void * user_data) +{ + free (ptr); +} + +static void * json_alloc (json_state * state, unsigned long size, int zero) +{ + if ((state->ulong_max - state->used_memory) < size) + return 0; + + if (state->settings.max_memory + && (state->used_memory += size) > state->settings.max_memory) + { + return 0; + } + + return state->settings.mem_alloc (size, zero, state->settings.user_data); +} + +static int new_value (json_state * state, + json_value ** top, json_value ** root, json_value ** alloc, + json_type type) +{ + json_value * value; + int values_size; + + if (!state->first_pass) + { + value = *top = *alloc; + *alloc = (*alloc)->_reserved.next_alloc; + + if (!*root) + *root = value; + + switch (value->type) + { + case json_array: + + if (value->u.array.length == 0) + break; + + if (! (value->u.array.values = (json_value **) json_alloc + (state, value->u.array.length * sizeof (json_value *), 0)) ) + { + return 0; + } + + value->u.array.length = 0; + break; + + case json_object: + + if (value->u.object.length == 0) + break; + + values_size = sizeof (*value->u.object.values) * value->u.object.length; + + if (! (value->u.object.values = (json_object_entry *) json_alloc + (state, values_size + ((unsigned long) value->u.object.values), 0)) ) + { + return 0; + } + + value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size; + + value->u.object.length = 0; + break; + + case json_string: + + if (! (value->u.string.ptr = (json_char *) json_alloc + (state, (value->u.string.length + 1) * sizeof (json_char), 0)) ) + { + return 0; + } + + value->u.string.length = 0; + break; + + default: + break; + }; + + return 1; + } + + if (! (value = (json_value *) json_alloc + (state, sizeof (json_value) + state->settings.value_extra, 1))) + { + return 0; + } + + if (!*root) + *root = value; + + value->type = type; + value->parent = *top; + + #ifdef JSON_TRACK_SOURCE + value->line = state->cur_line; + value->col = state->cur_col; + #endif + + if (*alloc) + (*alloc)->_reserved.next_alloc = value; + + *alloc = *top = value; + + return 1; +} + +#define whitespace \ + case '\n': ++ state.cur_line; state.cur_col = 0; \ + case ' ': case '\t': case '\r' + +#define string_add(b) \ + do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0); + +#define line_and_col \ + state.cur_line, state.cur_col + +static const long + flag_next = 1 << 0, + flag_reproc = 1 << 1, + flag_need_comma = 1 << 2, + flag_seek_value = 1 << 3, + flag_escaped = 1 << 4, + flag_string = 1 << 5, + flag_need_colon = 1 << 6, + flag_done = 1 << 7, + flag_num_negative = 1 << 8, + flag_num_zero = 1 << 9, + flag_num_e = 1 << 10, + flag_num_e_got_sign = 1 << 11, + flag_num_e_negative = 1 << 12, + flag_line_comment = 1 << 13, + flag_block_comment = 1 << 14, + flag_num_got_decimal = 1 << 15; + +json_value * json_parse_ex (json_settings * settings, + const json_char * json, + size_t length, + char * error_buf) +{ + json_char error [json_error_max]; + const json_char * end; + json_value * top, * root, * alloc = 0; + json_state state = { 0 }; + long flags = 0; + double num_digits = 0, num_e = 0; + double num_fraction = 0; + + /* Skip UTF-8 BOM + */ + if (length >= 3 && ((unsigned char) json [0]) == 0xEF + && ((unsigned char) json [1]) == 0xBB + && ((unsigned char) json [2]) == 0xBF) + { + json += 3; + length -= 3; + } + + error[0] = '\0'; + end = (json + length); + + memcpy (&state.settings, settings, sizeof (json_settings)); + + if (!state.settings.mem_alloc) + state.settings.mem_alloc = default_alloc; + + if (!state.settings.mem_free) + state.settings.mem_free = default_free; + + memset (&state.uint_max, 0xFF, sizeof (state.uint_max)); + memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max)); + + state.uint_max -= 8; /* limit of how much can be added before next check */ + state.ulong_max -= 8; + + for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass) + { + json_uchar uchar; + unsigned char uc_b1, uc_b2, uc_b3, uc_b4; + json_char * string = 0; + unsigned int string_length = 0; + + top = root = 0; + flags = flag_seek_value; + + state.cur_line = 1; + + for (state.ptr = json ;; ++ state.ptr) + { + json_char b = (state.ptr == end ? 0 : *state.ptr); + + if (flags & flag_string) + { + if (!b) + { sprintf (error, "Unexpected EOF in string (at %d:%d)", line_and_col); + goto e_failed; + } + + if (string_length > state.uint_max) + goto e_overflow; + + if (flags & flag_escaped) + { + flags &= ~ flag_escaped; + + switch (b) + { + case 'b': string_add ('\b'); break; + case 'f': string_add ('\f'); break; + case 'n': string_add ('\n'); break; + case 'r': string_add ('\r'); break; + case 't': string_add ('\t'); break; + case 'u': + + if (end - state.ptr <= 4 || + (uc_b1 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b2 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b3 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b4 = hex_value (*++ state.ptr)) == 0xFF) + { + sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col); + goto e_failed; + } + + uc_b1 = (uc_b1 << 4) | uc_b2; + uc_b2 = (uc_b3 << 4) | uc_b4; + uchar = (uc_b1 << 8) | uc_b2; + + if ((uchar & 0xF800) == 0xD800) { + json_uchar uchar2; + + if (end - state.ptr <= 6 || (*++ state.ptr) != '\\' || (*++ state.ptr) != 'u' || + (uc_b1 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b2 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b3 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b4 = hex_value (*++ state.ptr)) == 0xFF) + { + sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col); + goto e_failed; + } + + uc_b1 = (uc_b1 << 4) | uc_b2; + uc_b2 = (uc_b3 << 4) | uc_b4; + uchar2 = (uc_b1 << 8) | uc_b2; + + uchar = 0x010000 | ((uchar & 0x3FF) << 10) | (uchar2 & 0x3FF); + } + + if (sizeof (json_char) >= sizeof (json_uchar) || (uchar <= 0x7F)) + { + string_add ((json_char) uchar); + break; + } + + if (uchar <= 0x7FF) + { + if (state.first_pass) + string_length += 2; + else + { string [string_length ++] = 0xC0 | (uchar >> 6); + string [string_length ++] = 0x80 | (uchar & 0x3F); + } + + break; + } + + if (uchar <= 0xFFFF) { + if (state.first_pass) + string_length += 3; + else + { string [string_length ++] = 0xE0 | (uchar >> 12); + string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F); + string [string_length ++] = 0x80 | (uchar & 0x3F); + } + + break; + } + + if (state.first_pass) + string_length += 4; + else + { string [string_length ++] = 0xF0 | (uchar >> 18); + string [string_length ++] = 0x80 | ((uchar >> 12) & 0x3F); + string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F); + string [string_length ++] = 0x80 | (uchar & 0x3F); + } + + break; + + default: + string_add (b); + }; + + continue; + } + + if (b == '\\') + { + flags |= flag_escaped; + continue; + } + + if (b == '"') + { + if (!state.first_pass) + string [string_length] = 0; + + flags &= ~ flag_string; + string = 0; + + switch (top->type) + { + case json_string: + + top->u.string.length = string_length; + flags |= flag_next; + + break; + + case json_object: + + if (state.first_pass) + (*(json_char **) &top->u.object.values) += string_length + 1; + else + { + top->u.object.values [top->u.object.length].name + = (json_char *) top->_reserved.object_mem; + + top->u.object.values [top->u.object.length].name_length + = string_length; + + (*(json_char **) &top->_reserved.object_mem) += string_length + 1; + } + + flags |= flag_seek_value | flag_need_colon; + continue; + + default: + break; + }; + } + else + { + string_add (b); + continue; + } + } + + if (state.settings.settings & json_enable_comments) + { + if (flags & (flag_line_comment | flag_block_comment)) + { + if (flags & flag_line_comment) + { + if (b == '\r' || b == '\n' || !b) + { + flags &= ~ flag_line_comment; + -- state.ptr; /* so null can be reproc'd */ + } + + continue; + } + + if (flags & flag_block_comment) + { + if (!b) + { sprintf (error, "%d:%d: Unexpected EOF in block comment", line_and_col); + goto e_failed; + } + + if (b == '*' && state.ptr < (end - 1) && state.ptr [1] == '/') + { + flags &= ~ flag_block_comment; + ++ state.ptr; /* skip closing sequence */ + } + + continue; + } + } + else if (b == '/') + { + if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object) + { sprintf (error, "%d:%d: Comment not allowed here", line_and_col); + goto e_failed; + } + + if (++ state.ptr == end) + { sprintf (error, "%d:%d: EOF unexpected", line_and_col); + goto e_failed; + } + + switch (b = *state.ptr) + { + case '/': + flags |= flag_line_comment; + continue; + + case '*': + flags |= flag_block_comment; + continue; + + default: + sprintf (error, "%d:%d: Unexpected `%c` in comment opening sequence", line_and_col, b); + goto e_failed; + }; + } + } + + if (flags & flag_done) + { + if (!b) + break; + + switch (b) + { + whitespace: + continue; + + default: + + sprintf (error, "%d:%d: Trailing garbage: `%c`", + state.cur_line, state.cur_col, b); + + goto e_failed; + }; + } + + if (flags & flag_seek_value) + { + switch (b) + { + whitespace: + continue; + + case ']': + + if (top && top->type == json_array) + flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next; + else + { sprintf (error, "%d:%d: Unexpected ]", line_and_col); + goto e_failed; + } + + break; + + default: + + if (flags & flag_need_comma) + { + if (b == ',') + { flags &= ~ flag_need_comma; + continue; + } + else + { + sprintf (error, "%d:%d: Expected , before %c", + state.cur_line, state.cur_col, b); + + goto e_failed; + } + } + + if (flags & flag_need_colon) + { + if (b == ':') + { flags &= ~ flag_need_colon; + continue; + } + else + { + sprintf (error, "%d:%d: Expected : before %c", + state.cur_line, state.cur_col, b); + + goto e_failed; + } + } + + flags &= ~ flag_seek_value; + + switch (b) + { + case '{': + + if (!new_value (&state, &top, &root, &alloc, json_object)) + goto e_alloc_failure; + + continue; + + case '[': + + if (!new_value (&state, &top, &root, &alloc, json_array)) + goto e_alloc_failure; + + flags |= flag_seek_value; + continue; + + case '"': + + if (!new_value (&state, &top, &root, &alloc, json_string)) + goto e_alloc_failure; + + flags |= flag_string; + + string = top->u.string.ptr; + string_length = 0; + + continue; + + case 't': + + if ((end - state.ptr) < 3 || *(++ state.ptr) != 'r' || + *(++ state.ptr) != 'u' || *(++ state.ptr) != 'e') + { + goto e_unknown_value; + } + + if (!new_value (&state, &top, &root, &alloc, json_boolean)) + goto e_alloc_failure; + + top->u.boolean = 1; + + flags |= flag_next; + break; + + case 'f': + + if ((end - state.ptr) < 4 || *(++ state.ptr) != 'a' || + *(++ state.ptr) != 'l' || *(++ state.ptr) != 's' || + *(++ state.ptr) != 'e') + { + goto e_unknown_value; + } + + if (!new_value (&state, &top, &root, &alloc, json_boolean)) + goto e_alloc_failure; + + flags |= flag_next; + break; + + case 'n': + + if ((end - state.ptr) < 3 || *(++ state.ptr) != 'u' || + *(++ state.ptr) != 'l' || *(++ state.ptr) != 'l') + { + goto e_unknown_value; + } + + if (!new_value (&state, &top, &root, &alloc, json_null)) + goto e_alloc_failure; + + flags |= flag_next; + break; + + default: + + if (isdigit (b) || b == '-') + { + if (!new_value (&state, &top, &root, &alloc, json_integer)) + goto e_alloc_failure; + + if (!state.first_pass) + { + while (isdigit (b) || b == '+' || b == '-' + || b == 'e' || b == 'E' || b == '.') + { + if ( (++ state.ptr) == end) + { + b = 0; + break; + } + + b = *state.ptr; + } + + flags |= flag_next | flag_reproc; + break; + } + + flags &= ~ (flag_num_negative | flag_num_e | + flag_num_e_got_sign | flag_num_e_negative | + flag_num_zero); + + num_digits = 0; + num_fraction = 0; + num_e = 0; + + if (b != '-') + { + flags |= flag_reproc; + break; + } + + flags |= flag_num_negative; + continue; + } + else + { sprintf (error, "%d:%d: Unexpected %c when seeking value", line_and_col, b); + goto e_failed; + } + }; + }; + } + else + { + switch (top->type) + { + case json_object: + + switch (b) + { + whitespace: + continue; + + case '"': + + if (flags & flag_need_comma) + { sprintf (error, "%d:%d: Expected , before \"", line_and_col); + goto e_failed; + } + + flags |= flag_string; + + string = (json_char *) top->_reserved.object_mem; + string_length = 0; + + break; + + case '}': + + flags = (flags & ~ flag_need_comma) | flag_next; + break; + + case ',': + + if (flags & flag_need_comma) + { + flags &= ~ flag_need_comma; + break; + } + + default: + sprintf (error, "%d:%d: Unexpected `%c` in object", line_and_col, b); + goto e_failed; + }; + + break; + + case json_integer: + case json_double: + + if (isdigit (b)) + { + ++ num_digits; + + if (top->type == json_integer || flags & flag_num_e) + { + if (! (flags & flag_num_e)) + { + if (flags & flag_num_zero) + { sprintf (error, "%d:%d: Unexpected `0` before `%c`", line_and_col, b); + goto e_failed; + } + + if (num_digits == 1 && b == '0') + flags |= flag_num_zero; + } + else + { + flags |= flag_num_e_got_sign; + num_e = (num_e * 10) + (b - '0'); + continue; + } + + if (would_overflow(top->u.integer, b)) + { -- num_digits; + -- state.ptr; + top->type = json_double; + top->u.dbl = (double)top->u.integer; + continue; + } + + top->u.integer = (top->u.integer * 10) + (b - '0'); + continue; + } + + if (flags & flag_num_got_decimal) + num_fraction = (num_fraction * 10) + (b - '0'); + else + top->u.dbl = (top->u.dbl * 10) + (b - '0'); + + continue; + } + + if (b == '+' || b == '-') + { + if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign)) + { + flags |= flag_num_e_got_sign; + + if (b == '-') + flags |= flag_num_e_negative; + + continue; + } + } + else if (b == '.' && top->type == json_integer) + { + if (!num_digits) + { sprintf (error, "%d:%d: Expected digit before `.`", line_and_col); + goto e_failed; + } + + top->type = json_double; + top->u.dbl = (double) top->u.integer; + + flags |= flag_num_got_decimal; + num_digits = 0; + continue; + } + + if (! (flags & flag_num_e)) + { + if (top->type == json_double) + { + if (!num_digits) + { sprintf (error, "%d:%d: Expected digit after `.`", line_and_col); + goto e_failed; + } + + top->u.dbl += num_fraction / pow (10.0, num_digits); + } + + if (b == 'e' || b == 'E') + { + flags |= flag_num_e; + + if (top->type == json_integer) + { + top->type = json_double; + top->u.dbl = (double) top->u.integer; + } + + num_digits = 0; + flags &= ~ flag_num_zero; + + continue; + } + } + else + { + if (!num_digits) + { sprintf (error, "%d:%d: Expected digit after `e`", line_and_col); + goto e_failed; + } + + top->u.dbl *= pow (10.0, (flags & flag_num_e_negative ? - num_e : num_e)); + } + + if (flags & flag_num_negative) + { + if (top->type == json_integer) + top->u.integer = - top->u.integer; + else + top->u.dbl = - top->u.dbl; + } + + flags |= flag_next | flag_reproc; + break; + + default: + break; + }; + } + + if (flags & flag_reproc) + { + flags &= ~ flag_reproc; + -- state.ptr; + } + + if (flags & flag_next) + { + flags = (flags & ~ flag_next) | flag_need_comma; + + if (!top->parent) + { + /* root value done */ + + flags |= flag_done; + continue; + } + + if (top->parent->type == json_array) + flags |= flag_seek_value; + + if (!state.first_pass) + { + json_value * parent = top->parent; + + switch (parent->type) + { + case json_object: + + parent->u.object.values + [parent->u.object.length].value = top; + + break; + + case json_array: + + parent->u.array.values + [parent->u.array.length] = top; + + break; + + default: + break; + }; + } + + if ( (++ top->parent->u.array.length) > state.uint_max) + goto e_overflow; + + top = top->parent; + + continue; + } + } + + alloc = root; + } + + return root; + +e_unknown_value: + + sprintf (error, "%d:%d: Unknown value", line_and_col); + goto e_failed; + +e_alloc_failure: + + strcpy (error, "Memory allocation failure"); + goto e_failed; + +e_overflow: + + sprintf (error, "%d:%d: Too long (caught overflow)", line_and_col); + goto e_failed; + +e_failed: + + if (error_buf) + { + if (*error) + strcpy (error_buf, error); + else + strcpy (error_buf, "Unknown error"); + } + + if (state.first_pass) + alloc = root; + + while (alloc) + { + top = alloc->_reserved.next_alloc; + state.settings.mem_free (alloc, state.settings.user_data); + alloc = top; + } + + if (!state.first_pass) + json_value_free_ex (&state.settings, root); + + return 0; +} + +json_value * json_parse (const json_char * json, size_t length) +{ + json_settings settings = { 0 }; + return json_parse_ex (&settings, json, length, 0); +} + +void json_value_free_ex (json_settings * settings, json_value * value) +{ + json_value * cur_value; + + if (!value) + return; + + value->parent = 0; + + while (value) + { + switch (value->type) + { + case json_array: + + if (!value->u.array.length) + { + settings->mem_free (value->u.array.values, settings->user_data); + break; + } + + value = value->u.array.values [-- value->u.array.length]; + continue; + + case json_object: + + if (!value->u.object.length) + { + settings->mem_free (value->u.object.values, settings->user_data); + break; + } + + value = value->u.object.values [-- value->u.object.length].value; + continue; + + case json_string: + + settings->mem_free (value->u.string.ptr, settings->user_data); + break; + + default: + break; + }; + + cur_value = value; + value = value->parent; + settings->mem_free (cur_value, settings->user_data); + } +} + +void json_value_free (json_value * value) +{ + json_settings settings = { 0 }; + settings.mem_free = default_free; + json_value_free_ex (&settings, value); +} diff --git a/share/hybrid/json-parser/json.h b/share/hybrid/json-parser/json.h new file mode 100644 index 0000000..713b603 --- /dev/null +++ b/share/hybrid/json-parser/json.h @@ -0,0 +1,281 @@ + +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. + * https://github.com/udp/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _JSON_H +#define _JSON_H + +#ifndef json_char + #define json_char char +#endif + +#ifndef json_int_t + #ifndef _MSC_VER + #include + #define json_int_t int64_t + #else + #define json_int_t __int64 + #endif +#endif + +#include + +#ifdef __cplusplus + + #include + + extern "C" + { + +#endif + +typedef struct +{ + unsigned long max_memory; + int settings; + + /* Custom allocator support (leave null to use malloc/free) + */ + + void * (* mem_alloc) (size_t, int zero, void * user_data); + void (* mem_free) (void *, void * user_data); + + void * user_data; /* will be passed to mem_alloc and mem_free */ + + size_t value_extra; /* how much extra space to allocate for values? */ + +} json_settings; + +#define json_enable_comments 0x01 + +typedef enum +{ + json_none, + json_object, + json_array, + json_integer, + json_double, + json_string, + json_boolean, + json_null + +} json_type; + +extern const struct _json_value json_value_none; + +typedef struct _json_object_entry +{ + json_char * name; + unsigned int name_length; + + struct _json_value * value; + +} json_object_entry; + +typedef struct _json_value +{ + struct _json_value * parent; + + json_type type; + + union + { + int boolean; + json_int_t integer; + double dbl; + + struct + { + unsigned int length; + json_char * ptr; /* null terminated */ + + } string; + + struct + { + unsigned int length; + + json_object_entry * values; + + #if defined(__cplusplus) && __cplusplus >= 201103L + decltype(values) begin () const + { return values; + } + decltype(values) end () const + { return values + length; + } + #endif + + } object; + + struct + { + unsigned int length; + struct _json_value ** values; + + #if defined(__cplusplus) && __cplusplus >= 201103L + decltype(values) begin () const + { return values; + } + decltype(values) end () const + { return values + length; + } + #endif + + } array; + + } u; + + union + { + struct _json_value * next_alloc; + void * object_mem; + + } _reserved; + + #ifdef JSON_TRACK_SOURCE + + /* Location of the value in the source JSON + */ + unsigned int line, col; + + #endif + + + /* Some C++ operator sugar */ + + #ifdef __cplusplus + + public: + + inline _json_value () + { memset (this, 0, sizeof (_json_value)); + } + + inline const struct _json_value &operator [] (int index) const + { + if (type != json_array || index < 0 + || ((unsigned int) index) >= u.array.length) + { + return json_value_none; + } + + return *u.array.values [index]; + } + + inline const struct _json_value &operator [] (const char * index) const + { + if (type != json_object) + return json_value_none; + + for (unsigned int i = 0; i < u.object.length; ++ i) + if (!strcmp (u.object.values [i].name, index)) + return *u.object.values [i].value; + + return json_value_none; + } + + inline operator const char * () const + { + switch (type) + { + case json_string: + return u.string.ptr; + + default: + return ""; + }; + } + + inline operator json_int_t () const + { + switch (type) + { + case json_integer: + return u.integer; + + case json_double: + return (json_int_t) u.dbl; + + default: + return 0; + }; + } + + inline operator bool () const + { + if (type != json_boolean) + return false; + + return u.boolean != 0; + } + + inline operator double () const + { + switch (type) + { + case json_integer: + return (double) u.integer; + + case json_double: + return u.dbl; + + default: + return 0; + }; + } + + #endif + +} json_value; + +json_value * json_parse (const json_char * json, + size_t length); + +#define json_error_max 128 +json_value * json_parse_ex (json_settings * settings, + const json_char * json, + size_t length, + char * error); + +void json_value_free (json_value *); + + +/* Not usually necessary, unless you used a custom mem_alloc and now want to + * use a custom mem_free. + */ +void json_value_free_ex (json_settings * settings, + json_value *); + + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif diff --git a/ubuntu-drivers b/ubuntu-drivers index 3472aa7..a97fff3 100755 --- a/ubuntu-drivers +++ b/ubuntu-drivers @@ -48,7 +48,12 @@ def command_list(args): packages = UbuntuDrivers.detect.system_driver_packages( sys_path=sys_path, freeonly=args.free_only, include_oem=args.install_oem_meta) - cache = apt.Cache() + try: + cache = apt.Cache() + except Exception as ex: + print(ex) + return 1 + for package in packages: try: linux_modules = UbuntuDrivers.detect.get_linux_modules_metapackage(cache, package) @@ -86,7 +91,11 @@ def command_list_oem(args): def list_gpgpu(args): '''Show all GPGPU driver packages which apply to the current system.''' found = False - cache = apt.Cache() + try: + cache = apt.Cache() + except Exception as ex: + print(ex) + return 1 packages = UbuntuDrivers.detect.system_gpgpu_driver_packages(cache, sys_path) for package in packages: candidate = packages[package]['metapackage'] @@ -126,8 +135,11 @@ def command_devices(args): def command_install(args): '''Install drivers that are appropriate for your hardware.''' - - cache = apt.Cache() + try: + cache = apt.Cache() + except Exception as ex: + print(ex) + return 1 packages = UbuntuDrivers.detect.system_driver_packages( cache, sys_path, freeonly=args.free_only, @@ -142,6 +154,12 @@ def command_install(args): for p in packages: if not cache[p].installed: to_install.append(p) + # See if runtimepm is supported + if packages[p].get('runtimepm'): + # Create a file for nvidia-prime + pm_fd = open('/run/nvidia_runtimepm_supported', 'w') + pm_fd.write('\n') + pm_fd.close() # Add the matching linux modules package when available try: modules_package = UbuntuDrivers.detect.get_linux_modules_metapackage(cache, p) @@ -183,6 +201,16 @@ def command_install(args): if update_ret != 0: return update_ret + # If we are dealing with NVIDIA PRIME, and runtimepm + # is supported, enable it + if (os.path.isfile('/run/nvidia_runtimepm_supported') and + os.path.isfile('/usr/bin/prime-select')): + print('Trying to select the on-demand PRIME profile') + try: + subprocess.call(['prime-select', 'on-demand']) + except: + pass + # All updates completed successfully, now let's upgrade the packages if oem_meta_to_install: ret = subprocess.call(['apt', @@ -211,7 +239,11 @@ def install_gpgpu(args): # No args, just --gpgpu not_found_exit_status = 0 - cache = apt.Cache() + try: + cache = apt.Cache() + except Exception as ex: + print(ex) + return 1 packages = UbuntuDrivers.detect.system_gpgpu_driver_packages(cache, sys_path) packages = UbuntuDrivers.detect.gpgpu_install_filter(packages, args.driver_string) @@ -256,7 +288,13 @@ def command_debug(args): print('=== log messages from detection ===') aliases = UbuntuDrivers.detect.system_modaliases() - cache = apt.Cache() + + try: + cache = apt.Cache() + except Exception as ex: + print(ex) + return 1 + packages = UbuntuDrivers.detect.system_driver_packages( cache, sys_path, freeonly=args.free_only, include_oem=args.install_oem_meta) auto_packages = UbuntuDrivers.detect.auto_install_filter(packages) @@ -281,6 +319,8 @@ def command_debug(args): else: auto = '' + support = info.get('support') + info_str = '' if info['from_distro']: info_str += ' [distro]' @@ -298,6 +338,8 @@ def command_debug(args): info_str += ' vendor: ' + info['vendor'] if 'model' in info: info_str += ' model: ' + info['model'] + if support: + info_str += ' support level: ' + info['support'] print('%s: installed: %s available: %s%s%s ' % (package, inst, cand, auto, info_str)) @@ -378,7 +420,11 @@ def autoinstall(config, **kwargs): @pass_config def list(config, **kwargs): '''Show all driver packages which apply to the current system.''' - cache = apt.Cache() + try: + cache = apt.Cache() + except Exception as ex: + print(ex) + return 1 if kwargs.get('gpgpu'): packages = UbuntuDrivers.detect.system_gpgpu_driver_packages(cache, sys_path)