This package aggregates and abstracts Ubuntu specific logic and knowledge about third-party driver packages, and provides APIs for installers and driver configuration GUIs. It also contains some NVidia specific support code to find the most appropriate driver version (as we usually ship several), as well as setting up the alternatives symlinks that the proprietary NVidia and FGLRX packages use.
After cloning the repository, configure git to use the project's git hooks:
git config core.hooksPath git-hooksThis enables pre-commit hooks that run static analysis (pycodestyle, pyflakes) and mypy type checking.
The simplest frontend is the ubuntu-drivers command line tool. You can use
it to show the available driver packages which apply to the current system
(ubuntu-drivers list), or to install all drivers which are appropriate for
automatic installation (sudo ubuntu-drivers autoinstall), which is mostly
useful for integration into installers.
Please see ubuntu-drivers --help for details.
The UbuntuDrivers.detect Python module provides some functions to detect the
system's hardware, matching driver packages, and packages which are eligible
for automatic installation.
The three main functions are:
-
Which driver packages apply to this system?
packages = UbuntuDrivers.detect.system_driver_packages() -
Which devices need drivers, and which packages do they need?
driver_info = UbuntuDrivers.detect.system_device_drivers() -
Which driver package(s) applies to this piece of hardware?
import apt apt_cache = apt.Cache apt_packages = UbuntuDrivers.detect.packages_for_modalias(apt_cache, modalias)
These functions only use python-apt. They do not need any other dependencies, root privileges, D-BUS calls, etc.
This project also provides a system-bus D-Bus service that exposes driver
information. The service registers as com.ubuntu.Drivers on the system bus
with the object path /com/ubuntu/Drivers and exposes a single method:
drivers: Returns a list of devices and their available drivers. The first driver entry in each list is the recommended one.
The returned structure is a list of dictionaries like:
[
{
"sys_path": "/sys/devices/...",
"modalias": "pci:...",
"vendor": "NVIDIA Corporation",
"model": "GP107M [GeForce GTX 1050 Mobile]",
"drivers": [
{
"name": "nvidia-driver-570",
"source": "distro",
"free": False,
"builtin": False,
"recommended": True,
"support": "PB",
"open_preferred": False,
"packages": ["nvidia-driver-570", "linux-modules-nvidia-570-generic"],
"gpgpu_packages": ["nvidia-headless-no-dkms-570", "linux-modules-nvidia-570-generic"],
},
...
],
},
...
]source is either "distro" or "third-party", and support carries the
package's apt Support field ("PB", "NFB", "LTSB" or "Legacy"), empty
when the package does not declare one. open_preferred is True when Ubuntu's
driver-selection logic prefers the "open" kernel module variant over the
closed-source variant for this driver.
packages is what ubuntu-drivers install <name> would install for this
driver: the complete install set, not filtered by installed state.
gpgpu_packages is the equivalent for ubuntu-drivers install --gpgpu <name>. Either list may be empty when the driver would require DKMS.
The D-Bus service implementation lives in
UbuntuDrivers/service/drivers_service.py. It is activated on demand by
dbus-daemon and exits after a short period of inactivity, so results are
never more than one idle period stale.
Hardware detection uses three complementary mechanisms:
- Modaliases: match device identifiers from sysfs against package
Modaliasespatterns - MIDR: match ARM CPU identification fields against package
Midrfields - Plugins: run custom checks for hardware that needs additional detection logic (see below)
The principal method of mapping hardware to driver packages is to use modalias patterns. Hardware devices export a "modalias" sysfs attribute, for example
$ cat /sys/devices/pci0000:00/0000:00:1b.0/modalias
pci:v00008086d00003B56sv000017AAsd0000215Ebc04sc03i00Kernel modules declare which hardware they can handle with modalias patterns (globs), e. g.:
$ modinfo snd_hda_intel
[...]
alias: pci:v00008086d*sv*sd*bc04sc03i00*Driver packages which are not installed by default (e. g. backports of drivers
from newer Linux packages, or the proprietary NVidia driver package
nvidia-current) have a Modaliases: package header which includes all
modalias patterns from all kernel modules that they ship. It is recommended to
add these headers to the package with dh_modaliases(1).
ubuntu-drivers-common uses these package headers to map a particular piece of
hardware (identified by a modalias) to the driver packages which cover that
hardware.
MIDR detection reads /sys/devices/system/cpu/cpu*/regs/identification/midr_el1
and considers every distinct CPU type. Packages can declare constraints such as:
Midr: implementer:0x41,part_number:0xd05
(Note: Within your source package, declare this field as XB-Midr. The XB- will be stripped in the deb.)
Supported fields: implementer, variant, architecture, part_number,
and revision. All specified fields must match at least one CPU's MIDR; omitted fields
are unconstrained. Missing or invalid MIDR data is ignored.
For NVIDIA GPUs, the modalias-based detection can be overridden by a custom configuration file, which pins a specific driver series to a given PCI device ID and declares extra device features. This is mainly used by OEM enablement to ship a validated driver for a particular platform.
The file is looked up in this order, and the first one found wins:
/etc/custom_supported_gpus.json/usr/share/oem-*-meta/custom_supported_gpus.json(first match in sorted order)
{
"chips": [
{
"devid": "0x25BA",
"name": "TEST 25BA",
"branch": "580",
"features": [
"runtimepm"
]
}
]
}devid: the PCI device ID of the GPU, as an uppercase hexadecimal string with a0xprefix (this is thed0000....part of the modalias).name: a human-readable name, only used for logging.branch: the pinned driver series. Anything after the first.is ignored, so both"580"and"580.1234"select the580series.features: a list of feature flags.runtimepmmarks the device as supporting runtime power management for the pinned series, which makesubuntu-driversenable runtime PM for the NVIDIA driver during installation.
branch is mapped to the exact package name nvidia-driver-<branch>, which
must exist in the apt package pool; otherwise the entry is ignored and normal
detection applies. Because the match is on the exact package name, a branch
of "580" pins nvidia-driver-580 and never the nvidia-driver-580-open
variant, even when the open variant would otherwise be preferred. To pin the
open variant, set "branch": "580-open".
The pinned driver is used as follows:
ubuntu-drivers listandubuntu-drivers devicesstill show every applicable driver, but the pinned one is flagged asrecommended.ubuntu-drivers list --recommendedandubuntu-drivers installwithout an explicit driver argument collapse the NVIDIA alternatives to the pinned driver only.ubuntu-drivers install <driver>with an explicit driver argument still honors the user's choice.
Note that an explicit install request is handled differently from a
custom_supported_gpus.json pin with respect to the open kernel-module
variants. Requesting a driver series by its base name, e.g.
ubuntu-drivers install nvidia-driver-580
can still resolve to the open variant nvidia-driver-580-open when that GPU
prefers the open kernel modules (the "Prefer-Variant: Open" packaging
preference). This is intentional: a user who asks for the "580 series" should
get the variant that is recommended for their hardware. To force the open
variant, request nvidia-driver-580-open explicitly. This differs from a
custom_supported_gpus.json pin, which matches the exact package name and
therefore never substitutes the -open variant (see above).
For some kinds of drivers the modalias detection approach does not work. For
example, the "sl-modem-daemon" driver requires some checks in
/proc/asound/cards and aplay -l to decide whether or not it applies to the
system. These special cases can be put into a detection plugin, by adding a
small piece of Python code to /usr/share/ubuntu-drivers-common/detect/NAME.py
(shipped in ./detect-plugins/ in the ubuntu-drivers-common source). They need
to export a method
def detect(apt_cache):
# do detection logic here
return ['driver_package', ...]which can do any kind of detection and then return the resulting set of packages that apply to the current system. Please note that this cannot rely on having root privileges.
For the autopkgtest of ubuntu-drivers, the following command can be used when developing test cases:
PYTHONPATH=. tests/run test_ubuntu_driversTesting in a clean environment is always recommended. Using a pbuilder chroot, an sbuild chroot, or a direct upload to a PPA, will reduce the chances of tests failing due to your specific system.