Read ports
This commit is contained in:
parent
f94ffbf276
commit
b86302534f
114
Cargo.lock
generated
114
Cargo.lock
generated
@ -199,6 +199,19 @@ dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "amdportsd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"amdgpu",
|
||||
"futures",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"ron 0.7.1",
|
||||
"serde",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "amdvold"
|
||||
version = "1.0.10"
|
||||
@ -903,6 +916,95 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "getrandom"
|
||||
version = "0.2.7"
|
||||
@ -1650,6 +1752,12 @@ version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.25"
|
||||
@ -1923,6 +2031,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
|
||||
|
||||
[[package]]
|
||||
name = "slotmap"
|
||||
version = "1.0.6"
|
||||
|
@ -9,4 +9,5 @@ members = [
|
||||
"amdmond-lib",
|
||||
"amdguid",
|
||||
"amdgui-helper",
|
||||
"amdportsd",
|
||||
]
|
||||
|
@ -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) => {
|
||||
change_value(app, index, 1.0, y, y_mut);
|
||||
}
|
||||
@ -155,12 +155,12 @@ fn handle_event(app: &mut App) -> io::Result<Status> {
|
||||
}
|
||||
},
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -271,13 +271,13 @@ where
|
||||
let prev = index
|
||||
.checked_sub(1)
|
||||
.and_then(|i| app.config.speed_matrix().get(i))
|
||||
.map(|v| read(v))
|
||||
.map(read)
|
||||
.unwrap_or(0.0);
|
||||
let next = app
|
||||
.config
|
||||
.speed_matrix()
|
||||
.get(index + 1)
|
||||
.map(|v| read(v))
|
||||
.map(read)
|
||||
.unwrap_or(100.0);
|
||||
let current = app
|
||||
.config
|
||||
|
@ -1,6 +1,7 @@
|
||||
#[cfg(feature = "gui-helper")]
|
||||
use crate::helper_cmd::GuiHelperError;
|
||||
use crate::lock_file::LockFileError;
|
||||
use crate::ports::PortsError;
|
||||
use pidlock::PidlockError;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
|
||||
@ -48,6 +49,8 @@ pub enum AmdGpuError {
|
||||
#[error("{0:?}")]
|
||||
GuiHelper(#[from] GuiHelperError),
|
||||
#[error("{0:?}")]
|
||||
Ports(#[from] PortsError),
|
||||
#[error("{0:?}")]
|
||||
LockFile(#[from] LockFileError),
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ mod error;
|
||||
pub mod helper_cmd;
|
||||
pub mod hw_mon;
|
||||
pub mod lock_file;
|
||||
pub mod ports;
|
||||
mod temp_input;
|
||||
pub mod utils;
|
||||
|
||||
|
124
amdgpu/src/ports.rs
Normal file
124
amdgpu/src/ports.rs
Normal 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)
|
||||
}
|
@ -47,7 +47,7 @@ fn main() -> Result<()> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
15
amdportsd/Cargo.toml
Normal file
15
amdportsd/Cargo.toml
Normal 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
186
amdportsd/src/main.rs
Normal 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);
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user