// Copyright 2018-2021 System76// // SPDX-License-Identifier: GPL-3.0-only pub mod mux; pub mod sideband; use sideband::{Sideband, SidebandError, PCR_BASE_ADDRESS}; use std::{ fs, io::{self, Read, Seek}, }; #[derive(Debug, thiserror::Error)] pub enum HotPlugDetectError { #[error("failed to read DMI product version: {}", _0)] ProductVersion(io::Error), #[error("error constructing sideband: {}", _0)] Sideband(SidebandError), #[error("{} variant "{}" does not support hotplug detection", model, variant)] VariantUnsupported { model: &"static str, variant: String }, #[error("model "{}" does not support hotplug detection", _0)] ModelUnsupported(String), #[error("failed to read {}"s subsystem device: {}", model, why)] SubsystemDevice { model: &"static str, why: io::Error }, #[error("failed to open /dev/mem: {}", _0)] DevMemAccess(io::Error), } impl From for HotPlugDetectError { fn from(err: SidebandError) -> Self { HotPlugDetectError::Sideband(err) } } pub trait Detect { unsafe fn detect(&mut self) -> [bool; 4]; } const AMD_FCH_GPIO_CONTROL_BASE: u32 = 0xFED8_1500; struct Amd { mem: fs::File, gpios: Vec , } impl Amd { unsafe fn new(gpios: Vec ) -> Result { let mem = fs::OpenOptions::new() .read(true) .write(true) .open("/dev/mem") .map_err(HotPlugDetectError::DevMemAccess)?; Ok(Self { mem, gpios }) } } impl Detect for Amd { unsafe fn detect(&mut self) -> [bool; 4] { let mut hpd = [false; 4]; for (i, offset) in self.gpios.iter().enumerate() { let control_offset = AMD_FCH_GPIO_CONTROL_BASE + offset * 4; if self.mem.seek(io::SeekFrom::Start(u64::from(control_offset))).is_err() { return hpd; } let mut control = [0; 4]; if self.mem.read(&mut control).is_err() { return hpd; } let value = u32::from_ne_bytes(control); hpd[i] = value & (1 << 16) == (1 << 16); } hpd } } pub struct Intel { sideband: Sideband, port: u8, pins: [u8; 4], } impl Detect for Intel { unsafe fn detect(&mut self) -> [bool; 4] { let mut hpd = [false; 4]; for (i, &pin) in self.pins.iter().enumerate() { if pin > 0 { let data = self.sideband.gpio(self.port, pin); hpd[i] = data & 2 == 2; } } hpd } } enum Integrated { Amd(Amd), Intel(Intel), } pub struct HotPlugDetect { integrated: Integrated, } impl HotPlugDetect { /// # Errors /// /// - If `/sys/class/dmi/id/product_version` cannot be read /// - If `Sideband::new` fails #[allow(clippy::too_many_lines)] pub unsafe fn new(nvidia_device: Option ) -> Result { let model = fs::read_to_string("/sys/class/dmi/id/product_version") .map_err(HotPlugDetectError::ProductVersion)?; match model.trim() { "addw1" | "addw2" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x6A, pins: [ 0x28, // USB-C on rear 0x2a, // HDMI 0x2c, // Mini DisplayPort 0x2e, // USB-C on right ], }), }), "gaze14" => { let variant = fs::read_to_string("/sys/bus/pci/devices/0000:00:00.0/subsystem_device") .map_err(|why| HotPlugDetectError::SubsystemDevice { model: "gaze14", why, })?; match variant.trim() { // NVIDIA GTX 1660 Ti "0x8550" | "0x8551" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x6A, pins: [ 0x2a, // HDMI 0x00, // Mini DisplayPort (0x2c) is connected to Intel graphics 0x2e, // USB-C 0x00, // Not Connected ], }), }), // NVIDIA GTX 1650 "0x8560" | "0x8561" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x6A, pins: [ 0x00, // HDMI (0x2a) is connected to Intel graphics 0x2e, // Mini DisplayPort 0x00, // Not Connected 0x00, // Not Connected ], }), }), other => Err(HotPlugDetectError::VariantUnsupported { model: "gaze14", variant: other.into(), }), } } "gaze15" => { let variant = nvidia_device.unwrap_or_else(|| "unknown".to_string()); match variant.trim() { // NVIDIA GTX 1660 Ti "0x2191" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x6A, pins: [ 0x2a, // HDMI 0x00, // Mini DisplayPort (0x2c) is connected to Intel graphics 0x2e, // USB-C 0x00, // Not Connected ], }), }), // NVIDIA GTX 1650, 1650 Ti "0x1f99" | "0x1f95" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x6A, pins: [ 0x00, // HDMI (0x2a) is connected to Intel graphics 0x2e, // Mini DisplayPort 0x00, // Not Connected 0x00, // Not Connected ], }), }), other => Err(HotPlugDetectError::VariantUnsupported { model: "gaze15", variant: other.into(), }), } } "gaze16-3050" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x6A, pins: [ 0x00, // HDMI (0x52) is connected to Intel graphics 0x58, // Mini DisplayPort 0x00, // Not Connected 0x00, // Not Connected ], }), }), "gaze16-3060" | "gaze16-3060-b" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x69, pins: [ 0x02, // Mini DisplayPort 0x04, // USB-C 0x00, // Not Connected 0x00, // Not Connected ], }), }), "gaze17-3060-b" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x6E, pins: [ 0x72, // Mini DisplayPort 0x78, // HDMI 0x00, // Not Connected 0x00, // Not Connected ], }), }), "kudu6" => { let gpios = vec![ 0x02, // USB-C 0x03, // HDMI 0x15, // Mini DisplayPort ]; Ok(Self { integrated: Integrated::Amd(Amd::new(gpios)?) }) } "oryp4" | "oryp4-b" | "oryp5" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x6A, pins: [ 0x28, // USB-C 0x2a, // HDMI 0x2c, // Mini DisplayPort 0x00, // Not Connected ], }), }), "oryp6" | "oryp7" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x6A, pins: [ 0x2a, // HDMI 0x2c, // Mini DisplayPort 0x2e, // USB-C 0x00, // Not Connected ], }), }), "oryp8" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x69, pins: [ 0x02, // Mini DisplayPort 0x04, // HDMI 0x06, // USB-C 0x00, // Not Connected ], }), }), "oryp9" | "oryp10" => Ok(Self { integrated: Integrated::Intel(Intel { sideband: Sideband::new(PCR_BASE_ADDRESS)?, port: 0x6E, pins: [ 0x72, // Mini DisplayPort 0x78, // HDMI 0x7C, // USB-C 0x00, // Not Connected ], }), }), other => Err(HotPlugDetectError::ModelUnsupported(other.into())), } } } impl Detect for HotPlugDetect { unsafe fn detect(&mut self) -> [bool; 4] { match &mut self.integrated { Integrated::Amd(amd) => amd.detect(), Integrated::Intel(intel) => intel.detect(), } } }
Related articles
system76-power snd
// Copyright 2018-2021 System76 <[email protected]> // // SPDX-License-Identifier: GPL-3.0-only use crate::kernel_parameters::{DeviceList, KernelParameter, PowerSave, PowerSaveController}; use std::path::Path; pub struct SoundDevice { device:
system76-power pci
// Copyright 2018-2021 System76 <[email protected]> // // SPDX-License-Identifier: GPL-3.0-only use std::{fs::write, io, path::PathBuf}; pub struct PciBus { path: PathBuf, } impl PciBus { pub fn new() -> io::Result<PciBus> { let path =
system76-power CI
on: push: branches: [master] pull_request: name: Continuous integration jobs: fmt: name: Rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: install toolchain run: rustup toolchain instal
system76-power gpio
use log::LevelFilter; use std::process; use system76_power::{ hotplug::sideband::{Sideband, SidebandError, PCR_BASE_ADDRESS}, logging, }; struct GpioGroup<"a> { name: &"a str, count: u8, } struct GpioCommunity<"a> { id: u8,
system76-power TESTING
# Testing This document provides a guideline for testing and verifying the expected behaviors of the project. When a patch is ready for testing, the checklists may be copied and marked as they are proven to be working. ## Checklists Tasks for a tester
system76-power args
// Copyright 2018-2022 System76 <[email protected]> // // SPDX-License-Identifier: GPL-3.0-only use clap::{builder::PossibleValuesParser, Parser}; #[derive(Parser)] #[clap( about = "Query or set the graphics mode", long_about = "Query or set th
system76-power disks
// Copyright 2018-2021 System76 <[email protected]> // // SPDX-License-Identifier: GPL-3.0-only use crate::errors::DiskPowerError; use std::{ fs::{read_to_string, write}, path::{Path, PathBuf}, process::{Command, Stdio}, }; const AUTOSUSPEN
system76-power hid_backlight
// Copyright 2018-2021 System76 <[email protected]> // // SPDX-License-Identifier: GPL-3.0-only use hidapi::{HidApi, HidDevice, HidResult}; use inotify::{Inotify, WatchMask}; use std::{fs, path::Path}; fn keyboard(device: &HidDevice, brightness: u8, co
system76-power autoswitch
use log::LevelFilter; use std::{process, thread, time}; use system76_power::{ hotplug::sideband::{Sideband, SidebandError, PCR_BASE_ADDRESS}, logging, }; fn inner() -> Result<(), SidebandError> { let sideband = unsafe { Sideband::new(PCR_BAS
system76-power graphics
use log::LevelFilter; use std::{io, process}; use system76_power::{graphics::Graphics, logging}; fn inner() -> io::Result<()> { Graphics::new()?; Ok(()) } fn main() { if let Err(why) = logging::setup(LevelFilter::Debug) { eprintln!