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

How to shutdown the led green on my tower HP Unité centrale Gamer TG01-1769NF #17

Open
jactib opened this issue Nov 28, 2021 · 10 comments

Comments

@jactib
Copy link

jactib commented Nov 28, 2021

I have install your module but on my PC I can shutdown the light under windows with OMEN Command Center but on linux how to proceed ? linux mint 20.2 5.4.0-90 generic
thanks
jactib

@wl2776
Copy link
Contributor

wl2776 commented Dec 1, 2021

I have install your module but on my PC I can shutdown the light under windows with OMEN Command Center but on linux how to proceed ? linux mint 20.2 5.4.0-90 generic thanks jactib

Try to write #000000 to rgb zones.

@jactib
Copy link
Author

jactib commented Dec 1, 2021

I tried but I can't write # 0 to rgb zones.

@dedius
Copy link

dedius commented Dec 2, 2021

I've used this format 0xffffff

@jactib
Copy link
Author

jactib commented Dec 2, 2021

I tried your format : sudo bash -c 'echo 0xffffff > /sys/devices/platform/hp-wmi/rgb_zones/zone00'
but the green light is always on .

@pelrun
Copy link
Owner

pelrun commented Dec 15, 2021

Sorry, I've had zero free time over the past few weeks; I'll try to look at this but it'll be after Christmas.

@jactib
Copy link
Author

jactib commented Dec 15, 2021

ok no problem, happy Christmas !

@Blatzar
Copy link

Blatzar commented Dec 29, 2021

+ 1

I haven't found any way to turn the led off in Linux and doing it in Windows will only last til you completely turn off the computer. I can assist in any way possible short of programming the module. 🙏

@Blatzar
Copy link

Blatzar commented Jan 30, 2024

Never mind I went ahead and learned Kernel development. This turns off my HP chassis light:

#include <linux/module.h> /* Needed by all modules */
#include <linux/printk.h> /* Needed for pr_info() */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/rfkill.h>
#include <linux/string.h>

#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"

struct bios_args {
    u32 signature;
    u32 command;
    u32 commandtype;
    u32 datasize;
    u8 data[128];
};

enum hp_wmi_command {
    HPWMI_READ = 0x01,
    HPWMI_WRITE = 0x02,
    HPWMI_ODM = 0x03,
    HPWMI_COLOR = 131081,
};

enum hp_light {
    ON = 0,
    BREATHING = 1,
    BLINKING = 3,
    OFF = 255,
};

enum hp_animation_duration {
    SHORT = 2,
    MEDIUM = 5,
    LONG = 10,
};

/* map output size to the corresponding WMI method id */
static inline int encode_outsize_for_pvsz(int outsize)
{
  if (outsize > 4096)
    return -EINVAL;
  if (outsize > 1024)
    return 5;
  if (outsize > 128)
    return 4;
  if (outsize > 4)
    return 3;
  if (outsize > 0)
    return 2;
  return 1;
}

enum hp_return_value {
  HPWMI_RET_WRONG_SIGNATURE	= 0x02,
  HPWMI_RET_UNKNOWN_COMMAND	= 0x03,
  HPWMI_RET_UNKNOWN_CMDTYPE	= 0x04,
  HPWMI_RET_INPUT_SIZE_NULL	= 0x05,
  HPWMI_RET_INPUT_DATA_NULL = 0x06,
  HPWMI_RET_INPUT_DATA_INVALID = 0x07,
  HPWMI_RET_RETURN_SIZE_NULL	= 0x08,
  HPWMI_RET_RETURN_SIZE_INVALID = 0x09,
};

struct bios_return {
  u32 sigpass;
  u32 return_code;
};

/*
 * hp_wmi_perform_query
 *
 * query:	The commandtype (enum hp_wmi_commandtype)
 * write:	The command (enum hp_wmi_command)
 * buffer:	Buffer used as input and/or output
 * insize:	Size of input buffer
 * outsize:	Size of output buffer
 *
 * returns zero on success
 *         an HP WMI query specific error code (which is positive)
 *         -EINVAL if the query was not successful at all
 *         -EINVAL if the output buffer size exceeds buffersize
 *
 * Note: The buffersize must at least be the maximum of the input and output
 *       size. E.g. Battery info query is defined to have 1 byte input
 *       and 128 byte output. The caller would do:
 *       buffer = kzalloc(128, GFP_KERNEL);
 *       ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128)
 */
static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
                                void *buffer, int insize, int outsize) {
    int mid;
    struct bios_return *bios_return;
    int actual_outsize;
    union acpi_object *obj;
    struct bios_args args = {
            .signature = 0x55434553,
            .command = command,
            .commandtype = query,
            .datasize = insize,
            .data = {0},
    };
    struct acpi_buffer input = {sizeof(struct bios_args), &args};
    struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
    int ret = 0;

    mid = encode_outsize_for_pvsz(outsize);
    if (WARN_ON(mid < 0))
        return mid;

    if (WARN_ON(insize > sizeof(args.data)))
        return -EINVAL;
    memcpy(&args.data[0], buffer, insize);

    wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output);

    obj = output.pointer;

    if (!obj)
        return -EINVAL;

    if (obj->type != ACPI_TYPE_BUFFER) {
        ret = -EINVAL;
        goto out_free;
    }

    bios_return = (struct bios_return *) obj->buffer.pointer;
    ret = bios_return->return_code;

    if (ret) {
        if (ret != HPWMI_RET_UNKNOWN_CMDTYPE)
            pr_warn("query 0x%x returned error 0x%x\n", query, ret);
        goto out_free;
    }

    /* Ignore output data of zero size */
    if (!outsize)
        goto out_free;

    actual_outsize = min(outsize, (int) (obj->buffer.length - sizeof(*bios_return)));
    memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize);
    memset(buffer + actual_outsize, 0, outsize - actual_outsize);

    out_free:
    kfree(obj);
    return ret;
}


static int __init start_module(void) {
    u8 state[3] = { 0, 0, 0 };
    int ret;
    int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);

    pr_info("HP-Color module loaded.\n");
    if (!bios_capable) {
        pr_err("BIOS not capable!\n");
        return 1;
    }

    state[1] = OFF;  // Change animation
    state[2] = LONG; // Change duration of animation

    ret = hp_wmi_perform_query(7, HPWMI_COLOR, &state, sizeof(state), sizeof(state));
    //pr_info("Query return: %d\n", ret);

    return 0;
}

static void __exit exit_module(void) {
    pr_info("HP-Color module unloaded.\n");
}


module_init(start_module);
module_exit(exit_module);

MODULE_LICENSE("GPL");

@VFROMONT
Copy link

VFROMONT commented Dec 22, 2024

Hello @Blatzar ,
I have an HP Pavilion Gaming with a green led too. Can you explain to me steps by steps how I can use your code to turn off my led ? I have linux mint.
Thank you !

@Blatzar
Copy link

Blatzar commented Dec 24, 2024

Hello @VFROMONT

I think you should be able to compile it like any other kernel module, it's surprisingly easy. I think you can follow the guide at https://www.cyberciti.biz/tips/compiling-linux-kernel-module.html and have it work. I am not able to check myself due to running NixOs which takes a completely different approach to loading kernel modules.

Basically:

  • Copy the code to hp-color.c
  • have a file named Makefile in the same directory with the content:
obj-m = hp-color.o
KVERSION = $(shell uname -r)
all:
        make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
        make -C /lib/modules/$(KVERSION)/build M=$(PWD)
  • Run make to compile the module
  • Run sudo insmod hp-color.ko to load the module. This should immediately turn off the light (if you want something else than turning off then you will need to tinker with the code)

There are plenty of tutorials online for kernel module development in case this is confusing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants