Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for RGB command #84

Merged
merged 6 commits into from
Mar 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion framework_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ cross_freebsd = ["unix", "freebsd_pio"]
# Windows does not have the cros_ec driver nor raw port I/O access to userspace
windows = ["std", "smbios", "dep:windows", "win_driver", "raw_pio", "hidapi", "rusb", "dep:wmi"]
smbios = ["dep:smbios-lib"]
std = ["dep:clap", "dep:clap-verbosity-flag", "dep:env_logger", "smbios-lib?/std"]
std = ["dep:clap", "dep:clap-num", "dep:clap-verbosity-flag", "dep:env_logger", "smbios-lib?/std"]
rusb = ["dep:rusb"]
hidapi = ["dep:hidapi"]
uefi = [
Expand Down Expand Up @@ -51,6 +51,7 @@ regex = { version = "1.11.1", default-features = false }
redox_hwio = { git = "https://github.com/FrameworkComputer/rust-hwio", branch = "freebsd", default-features = false }
libc = { version = "0.2.155", optional = true }
clap = { version = "4.5", features = ["derive"], optional = true }
clap-num = { version = "1.2.0", optional = true }
clap-verbosity-flag = { version = "2.2.1", optional = true }
nix = { version = "0.29.0", features = ["ioctl", "user"], optional = true }
num = { version = "0.4", default-features = false }
Expand Down
18 changes: 10 additions & 8 deletions framework_lib/src/chromium_ec/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,20 @@ pub enum EcCommands {
PwmSetDuty = 0x0025,
PwmGetDuty = 0x0026,
SetTabletMode = 0x0031,
GpioGet = 0x93,
I2cPassthrough = 0x9e,
ConsoleSnapshot = 0x97,
ConsoleRead = 0x98,
GpioGet = 0x0093,
I2cPassthrough = 0x009e,
ConsoleSnapshot = 0x0097,
ConsoleRead = 0x0098,
/// List the features supported by the firmware
GetFeatures = 0x0D,
GetFeatures = 0x000D,
/// Force reboot, causes host reboot as well
Reboot = 0xD1,
Reboot = 0x00D1,
/// Control EC boot
RebootEc = 0xD2,
RebootEc = 0x00D2,
/// Get information about PD controller power
UsbPdPowerInfo = 0x103,
UsbPdPowerInfo = 0x0103,
RgbKbdSetColor = 0x013A,
RgbKbd = 0x013B,

// Framework specific commands
/// Configure the behavior of the flash notify
Expand Down
31 changes: 31 additions & 0 deletions framework_lib/src/chromium_ec/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,37 @@ impl EcRequest<EcResponseUsbPdPowerInfo> for EcRequestUsbPdPowerInfo {
}
}

// TODO: Actually 128, but if we go above ~80 EC returns REQUEST_TRUNCATED
// At least when I use the portio driver
pub const EC_RGBKBD_MAX_KEY_COUNT: usize = 64;

#[repr(C, packed)]
#[derive(Default, Clone, Copy, Debug)]
pub struct RgbS {
pub r: u8,
pub g: u8,
pub b: u8,
}

#[repr(C, packed)]
pub struct EcRequestRgbKbdSetColor {
/// Specifies the starting key ID whose color is being changed
pub start_key: u8,
/// Specifies # of elements in color
pub length: u8,
/// RGB color data array of length up to MAX_KEY_COUNT
pub color: [RgbS; EC_RGBKBD_MAX_KEY_COUNT],
}

#[repr(C, packed)]
pub struct EcResponseRgbKbdSetColor {}

impl EcRequest<EcResponseRgbKbdSetColor> for EcRequestRgbKbdSetColor {
fn command_id() -> EcCommands {
EcCommands::RgbKbdSetColor
}
}

// --- Framework Specific commands ---

#[repr(C, packed)]
Expand Down
17 changes: 17 additions & 0 deletions framework_lib/src/chromium_ec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,23 @@ impl CrosEc {
let res = request.send_command(self)?;
Ok(res.val == 1)
}

pub fn rgbkbd_set_color(&self, start_key: u8, colors: Vec<RgbS>) -> EcResult<()> {
for (chunk, colors) in colors.chunks(EC_RGBKBD_MAX_KEY_COUNT).enumerate() {
let mut request = EcRequestRgbKbdSetColor {
start_key: start_key + ((chunk * EC_RGBKBD_MAX_KEY_COUNT) as u8),
length: colors.len() as u8,
color: [(); EC_RGBKBD_MAX_KEY_COUNT].map(|()| Default::default()),
};

for (i, color) in colors.iter().enumerate() {
request.color[i] = *color;
}

let _res = request.send_command(self)?;
}
Ok(())
}
}

#[cfg_attr(not(feature = "uefi"), derive(clap::ValueEnum))]
Expand Down
9 changes: 9 additions & 0 deletions framework_lib/src/commandline/clap_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//! This way we can use it in the regular OS commandline tool on Linux and Windows,
//! as well as on the UEFI shell tool.
use clap::Parser;
use clap_num::maybe_hex;

use crate::chromium_ec::CrosEcDriverType;
use crate::commandline::{
Expand Down Expand Up @@ -149,6 +150,13 @@ struct ClapCli {
#[arg(long)]
kblight: Option<Option<u8>>,

/// Set the color of <key> to <RGB>. Multiple colors for adjacent keys can be set at once.
/// <key> <RGB> [<RGB> ...]
/// Example: 0 0xFF000 0x00FF00 0x0000FF
#[clap(num_args = 2..)]
#[arg(long, value_parser=maybe_hex::<u64>)]
rgbkbd: Vec<u64>,

/// Set tablet mode override
#[clap(value_enum)]
#[arg(long)]
Expand Down Expand Up @@ -274,6 +282,7 @@ pub fn parse(args: &[String]) -> Cli {
fp_led_level: args.fp_led_level,
fp_brightness: args.fp_brightness,
kblight: args.kblight,
rgbkbd: args.rgbkbd,
tablet_mode: args.tablet_mode,
console: args.console,
reboot_ec: args.reboot_ec,
Expand Down
17 changes: 17 additions & 0 deletions framework_lib/src/commandline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use crate::chromium_ec;
use crate::chromium_ec::commands::DeckStateMode;
use crate::chromium_ec::commands::FpLedBrightnessLevel;
use crate::chromium_ec::commands::RebootEcCmd;
use crate::chromium_ec::commands::RgbS;
use crate::chromium_ec::commands::TabletModeOverride;
use crate::chromium_ec::EcResponseStatus;
use crate::chromium_ec::{print_err, EcFlashType};
Expand Down Expand Up @@ -166,6 +167,7 @@ pub struct Cli {
pub fp_led_level: Option<Option<FpBrightnessArg>>,
pub fp_brightness: Option<Option<u8>>,
pub kblight: Option<Option<u8>>,
pub rgbkbd: Vec<u64>,
pub tablet_mode: Option<TabletModeArg>,
pub console: Option<ConsoleArg>,
pub reboot_ec: Option<RebootEcArg>,
Expand Down Expand Up @@ -753,6 +755,21 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
} else if let Some(Some(kblight)) = args.kblight {
assert!(kblight <= 100);
ec.set_keyboard_backlight(kblight);
} else if !args.rgbkbd.is_empty() {
if args.rgbkbd.len() < 2 {
println!(
"Must provide at least 2 arguments. Provided only: {}",
args.rgbkbd.len()
);
} else {
let start_key = args.rgbkbd[0] as u8;
let colors = args.rgbkbd[1..].iter().map(|color| RgbS {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bonus points if you figure out how to do the *8 multipliers like i have on the ec console.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left as an exercise for the reader

r: ((color & 0x00FF0000) >> 16) as u8,
g: ((color & 0x0000FF00) >> 8) as u8,
b: (color & 0x000000FF) as u8,
});
ec.rgbkbd_set_color(start_key, colors.collect()).unwrap();
}
} else if let Some(None) = args.kblight {
print!("Keyboard backlight: ");
if let Some(percentage) = print_err(ec.get_keyboard_backlight()) {
Expand Down
13 changes: 13 additions & 0 deletions framework_lib/src/commandline/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ pub fn parse(args: &[String]) -> Cli {
fp_led_level: None,
fp_brightness: None,
kblight: None,
rgbkbd: vec![],
tablet_mode: None,
console: None,
reboot_ec: None,
Expand Down Expand Up @@ -216,6 +217,18 @@ pub fn parse(args: &[String]) -> Cli {
Some(None)
};
found_an_option = true;
} else if arg == "--rgbkbd" {
cli.rgbkbd = if args.len() > i + 2 {
let mut colors = Vec::<u64>::new();
for color_i in i + 1..args.len() {
// TODO: Fail parsing instead of unwrap()
colors.push(args[color_i].parse::<u64>().unwrap());
}
colors
} else {
println!("--rgbkbd requires at least 2 arguments, the start key and an RGB value");
vec![]
}
} else if arg == "--tablet-mode" {
cli.tablet_mode = if args.len() > i + 1 {
let tablet_mode_arg = &args[i + 1];
Expand Down
37 changes: 37 additions & 0 deletions rgbkbd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
# Example invocation in fish shell
# cargo build && sudo ./target/debug/framework_tool \
# --driver portio --has-mec false --pd-ports 1 1 --pd-addrs 64 64 \
# (./rgbkbd.py | string split ' ')

BRIGHTNESS = 1
RED = int(0xFF * BRIGHTNESS) << 16
GREEN = int(0xFF * BRIGHTNESS) << 8
BLUE = int(0xFF * BRIGHTNESS)
CYAN = GREEN + BLUE
YELLOW = RED + GREEN
PURPLE = BLUE + RED
WHITE = RED + GREEN + BLUE

grid_4x4 = [
[ YELLOW, RED, RED, RED, YELLOW ],
[ RED, WHITE, GREEN, WHITE, RED ],
[ RED, GREEN, BLUE, GREEN, RED ],
[ RED, WHITE, GREEN, WHITE, RED ],
[ YELLOW, RED, RED, RED, YELLOW ],
]

fan_8leds = [[
# WHITE, CYAN, BLUE, GREEN, PURPLE, RED, YELLOW, WHITE
RED, RED, RED, RED,
GREEN, GREEN, GREEN, GREEN
]]

# colors = grid_4x4
colors = fan_8leds

print('--rgbkbd 0', end='')
for row in colors:
for col in row:
print(' ', end='')
print(col, end='')
Loading