Read ports

This commit is contained in:
Adrian Woźniak 2022-06-29 18:11:24 +02:00
parent f94ffbf276
commit b86302534f
No known key found for this signature in database
GPG Key ID: 0012845A89C7352B
9 changed files with 450 additions and 6 deletions

114
Cargo.lock generated
View File

@ -199,6 +199,19 @@ dependencies = [
"toml", "toml",
] ]
[[package]]
name = "amdportsd"
version = "0.1.0"
dependencies = [
"amdgpu",
"futures",
"log",
"pretty_env_logger",
"ron 0.7.1",
"serde",
"tokio",
]
[[package]] [[package]]
name = "amdvold" name = "amdvold"
version = "1.0.10" version = "1.0.10"
@ -903,6 +916,95 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "futures"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-executor"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
[[package]]
name = "futures-macro"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
[[package]]
name = "futures-task"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
[[package]]
name = "futures-util"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.7" version = "0.2.7"
@ -1650,6 +1752,12 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.25" version = "0.3.25"
@ -1923,6 +2031,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "slab"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
[[package]] [[package]]
name = "slotmap" name = "slotmap"
version = "1.0.6" version = "1.0.6"

View File

@ -9,4 +9,5 @@ members = [
"amdmond-lib", "amdmond-lib",
"amdguid", "amdguid",
"amdgui-helper", "amdgui-helper",
"amdportsd",
] ]

View File

@ -140,7 +140,7 @@ fn handle_event(app: &mut App) -> io::Result<Status> {
} }
}, },
}, },
KeyCode::Up => match app.selected_point.clone() { KeyCode::Up => match app.selected_point {
Some(index) => { Some(index) => {
change_value(app, index, 1.0, y, y_mut); change_value(app, index, 1.0, y, y_mut);
} }
@ -155,12 +155,12 @@ fn handle_event(app: &mut App) -> io::Result<Status> {
} }
}, },
KeyCode::Left => { KeyCode::Left => {
if let Some(index) = app.selected_point.clone() { if let Some(index) = app.selected_point {
change_value(app, index, -1.0, x, x_mut); change_value(app, index, -1.0, x, x_mut);
} }
} }
KeyCode::Right => { KeyCode::Right => {
if let Some(index) = app.selected_point.clone() { if let Some(index) = app.selected_point {
change_value(app, index, 1.0, x, x_mut); change_value(app, index, 1.0, x, x_mut);
} }
} }
@ -271,13 +271,13 @@ where
let prev = index let prev = index
.checked_sub(1) .checked_sub(1)
.and_then(|i| app.config.speed_matrix().get(i)) .and_then(|i| app.config.speed_matrix().get(i))
.map(|v| read(v)) .map(read)
.unwrap_or(0.0); .unwrap_or(0.0);
let next = app let next = app
.config .config
.speed_matrix() .speed_matrix()
.get(index + 1) .get(index + 1)
.map(|v| read(v)) .map(read)
.unwrap_or(100.0); .unwrap_or(100.0);
let current = app let current = app
.config .config

View File

@ -1,6 +1,7 @@
#[cfg(feature = "gui-helper")] #[cfg(feature = "gui-helper")]
use crate::helper_cmd::GuiHelperError; use crate::helper_cmd::GuiHelperError;
use crate::lock_file::LockFileError; use crate::lock_file::LockFileError;
use crate::ports::PortsError;
use pidlock::PidlockError; use pidlock::PidlockError;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
@ -48,6 +49,8 @@ pub enum AmdGpuError {
#[error("{0:?}")] #[error("{0:?}")]
GuiHelper(#[from] GuiHelperError), GuiHelper(#[from] GuiHelperError),
#[error("{0:?}")] #[error("{0:?}")]
Ports(#[from] PortsError),
#[error("{0:?}")]
LockFile(#[from] LockFileError), LockFile(#[from] LockFileError),
} }

View File

@ -10,6 +10,7 @@ mod error;
pub mod helper_cmd; pub mod helper_cmd;
pub mod hw_mon; pub mod hw_mon;
pub mod lock_file; pub mod lock_file;
pub mod ports;
mod temp_input; mod temp_input;
pub mod utils; pub mod utils;

124
amdgpu/src/ports.rs Normal file
View File

@ -0,0 +1,124 @@
//! AMD GUI helper communication toolkit
use std::io::{Read, Write};
use std::ops::Deref;
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
#[derive(Debug, thiserror::Error)]
pub enum PortsError {
#[error("AMD GPU ports socket file not found. Is service running?")]
NoSockFile,
#[error("Failed to connect to /tmp/amdgpu-ports.sock. {0}")]
UnableToConnect(#[from] std::io::Error),
#[error("Failed to ports command. {0}")]
Serialize(#[from] ron::Error),
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Output {
#[serde(rename = "c")]
pub card: String,
#[serde(rename = "t")]
pub port_type: String,
#[serde(rename = "m")]
pub port_name: Option<String>,
#[serde(rename = "n")]
pub port_number: u8,
#[serde(rename = "s")]
pub status: Status,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Status {
#[serde(rename = "c")]
Connected,
#[serde(rename = "d")]
Disconnected,
}
impl Default for Status {
fn default() -> Self {
Self::Disconnected
}
}
impl Output {
fn to_path(&self) -> PathBuf {
PathBuf::new().join("/sys/class/drm").join(format!(
"card{}-{}{}-{}",
self.card,
self.port_type,
self.port_name
.as_deref()
.map(|s| format!("-{s}"))
.unwrap_or_default(),
self.port_number
))
}
fn status_path(&self) -> PathBuf {
self.to_path().join("status")
}
pub fn read_status(&self) -> Option<Status> {
Some(
match std::fs::read_to_string(self.status_path()).ok()?.trim() {
"connected" => Status::Connected,
"disconnected" => Status::Disconnected,
_ => return None,
},
)
}
}
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct Pid(pub i32);
impl Deref for Pid {
type Target = i32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum Command {
Ports,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum Response {
Ports(Vec<Output>),
NoOp,
}
pub fn sock_file() -> PathBuf {
std::path::Path::new("/tmp").join("amdgpu-ports.sock")
}
pub fn send_command(cmd: Command) -> crate::Result<Response> {
let sock_path = sock_file();
if !sock_path.exists() {
return Err(PortsError::NoSockFile.into());
}
let mut stream = UnixStream::connect(&sock_path).map_err(PortsError::UnableToConnect)?;
let s = ron::to_string(&cmd).map_err(PortsError::Serialize)?;
if stream.write_all(format!("{}\n", s).as_bytes()).is_ok() {
log::info!("Command send");
}
let res: Response = {
let mut s = String::with_capacity(100);
let _ = stream.read_to_string(&mut s);
ron::from_str(&s).map_err(PortsError::Serialize)?
};
Ok(res)
}

View File

@ -47,7 +47,7 @@ fn main() -> Result<()> {
path: sock_path.clone(), path: sock_path.clone(),
})? })?
}; };
if let Err(e) = std::fs::set_permissions(&sock_path, Permissions::from_mode(0x777)) { if let Err(e) = std::fs::set_permissions(&sock_path, Permissions::from_mode(0o777)) {
log::error!("Failed to change gui helper socket file mode. {:?}", e); log::error!("Failed to change gui helper socket file mode. {:?}", e);
} }

15
amdportsd/Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "amdportsd"
version = "0.1.0"
edition = "2021"
[dependencies]
amdgpu = { path = "../amdgpu", features = ["gui-helper"] }
tokio = { version = "1.19.2", features = ["full"] }
futures = { version = "0.3", features = [] }
ron = { version = "0.7.1" }
serde = { version = "1.0.137", features = ["derive"] }
log = { version = "0.4" }
pretty_env_logger = { version = "0.4" }

186
amdportsd/src/main.rs Normal file
View File

@ -0,0 +1,186 @@
use amdgpu::ports::{sock_file, Command, Response};
use amdgpu::{ports::*, IoFailure};
use std::fs::{DirEntry, Permissions};
use std::io::{Read, Write};
use std::net::Shutdown;
use std::os::unix::fs::PermissionsExt;
use std::os::unix::net::{UnixListener, UnixStream};
use std::sync::{Arc, Mutex};
use std::time::Duration;
fn parse_output(entry: DirEntry) -> Option<Output> {
let ty = entry.file_type().ok()?;
if ty.is_dir() {
return None;
}
let file_name = entry.file_name();
let path = file_name.to_str()?;
let mut it = path
.split('-')
.map(String::from)
.collect::<Vec<_>>()
.into_iter();
let mut output = Output {
card: it.next()?.strip_prefix("card")?.to_string(),
port_type: it.next()?,
..Default::default()
};
let mut it = it.rev();
output.port_number = it.next()?.parse().ok()?;
let mut it = it.rev().peekable();
if it.peek().is_some() {
output.port_name = Some(it.collect::<Vec<_>>().join("-"));
}
output.status = output.read_status()?;
Some(output)
}
async fn read_outputs(state: Arc<Mutex<Vec<Output>>>) {
loop {
let outputs = std::fs::read_dir("/sys/class/drm")
.unwrap()
.filter_map(|r| r.ok())
.filter(|e| {
e.path()
.to_str()
.map(|s| s.contains("card"))
.unwrap_or_default()
})
.filter_map(parse_output)
.collect::<Vec<_>>();
if let Ok(mut lock) = state.lock() {
*lock = outputs;
}
tokio::time::sleep(Duration::from_millis(1_000 / 3)).await;
}
}
pub struct Service(UnixStream);
impl Service {
/// Serialize and send command
pub fn write_response(&mut self, res: Response) {
match ron::to_string(&res) {
Ok(buffer) => match self.0.write_all(buffer.as_bytes()) {
Ok(_) => {
log::info!("Response successfully written")
}
Err(e) => log::warn!("Failed to write response. {:?}", e),
},
Err(e) => {
log::warn!("Failed to serialize response {:?}. {:?}", res, e)
}
}
}
/// Read from `.sock` file new line separated commands
pub fn read_command(&mut self) -> Option<String> {
let mut command = String::with_capacity(100);
log::info!("Reading stream...");
read_line(&mut self.0, &mut command);
if command.is_empty() {
return None;
}
Some(command)
}
/// Close connection with no operation response
pub fn kill(mut self) {
self.write_response(Response::NoOp);
self.close();
}
pub fn close(self) {
let _ = self.0.shutdown(Shutdown::Both);
}
}
async fn service(state: Arc<Mutex<Vec<Output>>>) {
let sock_path = sock_file();
let listener = {
let _ = std::fs::remove_file(&sock_path);
UnixListener::bind(&sock_path)
.map_err(|io| IoFailure {
io,
path: sock_path.clone(),
})
.expect("Creating pid file for ports failed")
};
if let Err(e) = std::fs::set_permissions(&sock_path, Permissions::from_mode(0o777)) {
log::error!("Failed to change gui helper socket file mode. {:?}", e);
}
while let Ok((stream, _addr)) = listener.accept() {
handle_connection(stream, state.clone());
}
}
fn read_line(stream: &mut UnixStream, command: &mut String) {
let mut buffer = [0];
while stream.read_exact(&mut buffer).is_ok() {
if buffer[0] == b'\n' {
break;
}
match std::str::from_utf8(&buffer) {
Ok(s) => {
command.push_str(s);
}
Err(e) => {
log::error!("Failed to read from client. {:?}", e);
let _ = stream.shutdown(Shutdown::Both);
continue;
}
}
}
}
fn handle_connection(stream: UnixStream, state: Arc<Mutex<Vec<Output>>>) {
let mut service = Service(stream);
let command = match service.read_command() {
Some(s) => s,
_ => return service.kill(),
};
log::info!("Incoming {:?}", command);
let cmd = match ron::from_str::<Command>(command.trim()) {
Ok(cmd) => cmd,
Err(e) => {
log::warn!("Invalid message {:?}. {:?}", command, e);
return service.kill();
}
};
handle_command(service, cmd, state);
}
fn handle_command(mut service: Service, cmd: Command, state: Arc<Mutex<Vec<Output>>>) {
match cmd {
Command::Ports => {
if let Ok(outputs) = state.lock() {
service.write_response(Response::Ports(outputs.iter().map(Clone::clone).collect()));
}
}
}
}
fn main() {
let executor = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let state = Arc::new(Mutex::new(Vec::new()));
executor.block_on(async {
let sync = read_outputs(state.clone());
let handle = service(state);
tokio::join!(sync, handle);
});
}