From 3d04d6f1cc8bb17941b3d0cdf7f1ce2cb9087e0d Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sat, 19 Oct 2024 01:48:13 +0200 Subject: [PATCH 01/12] Detect FS67xx (Flashtor) devices by their PCIe Packet Switches the AS67xx devices shouldn't have those packet switches from `lspci` on a FS6712X: 02:00.0 PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01) 03:00.0 PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01) (... and several more ...) `lspci -v -nn` then revealed that their PCI vendor ID and product ID are 1b21:2806 (vendor 0xb21 product 0x2806) --- asustor.c | 59 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/asustor.c b/asustor.c index 4d454cc..7b8a2f8 100644 --- a/asustor.c +++ b/asustor.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -246,44 +247,51 @@ static struct gpiod_lookup_table asustor_600_gpio_keys_lookup = { // ASUSTOR Platform. struct asustor_driver_data { + const char *name; struct gpiod_lookup_table *leds; struct gpiod_lookup_table *keys; }; static struct asustor_driver_data asustor_fs6700_driver_data = { + .name = "FS67xx", .leds = &asustor_fs6700_gpio_leds_lookup, .keys = &asustor_fs6700_gpio_keys_lookup, }; static struct asustor_driver_data asustor_6700_driver_data = { + .name = "AS67xx", + .leds = &asustor_6700_gpio_leds_lookup, + .keys = &asustor_6100_gpio_keys_lookup, +}; + +static struct asustor_driver_data asustor_6600_driver_data = { + // NOTE: This is (currently?) the same as for AS6700 + // because it seems to use the same GPIO numbers, + // but listed extra for the different name + .name = "AS66xx", .leds = &asustor_6700_gpio_leds_lookup, .keys = &asustor_6100_gpio_keys_lookup, }; static struct asustor_driver_data asustor_6100_driver_data = { + .name = "AS61xx", .leds = &asustor_6100_gpio_leds_lookup, .keys = &asustor_6100_gpio_keys_lookup, }; static struct asustor_driver_data asustor_600_driver_data = { + .name = "AS6xx", .leds = &asustor_600_gpio_leds_lookup, .keys = &asustor_600_gpio_keys_lookup, }; static const struct dmi_system_id asustor_systems[] = { - { - // Note: This uses the BIOS release date to help match the FS67xx, - // because otherwise it matches the AS670xT, AS540xT and others - .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"), - DMI_EXACT_MATCH(DMI_BIOS_DATE, "09/15/2023"), - }, - .driver_data = &asustor_fs6700_driver_data, - }, { // Note: This not only matches (and works with) AS670xT (Lockerstore Gen2), - // but also AS540xT (Nimbustor Gen2) + // but also AS540xT (Nimbustor Gen2) and, unfortunately, also + // Flashstor (FS6706T and FS6712X) which can't be detected with DMI but is + // different enough from the AS67xx devices to need different treatment. + // asustor_init() has additional code to detect FS67xx based on available PCIe devices .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"), @@ -297,7 +305,7 @@ static const struct dmi_system_id asustor_systems[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "GeminiLake"), }, - .driver_data = &asustor_6700_driver_data, + .driver_data = &asustor_6600_driver_data, }, { .matches = { @@ -370,14 +378,27 @@ static int __init asustor_init(void) return -ENODEV; } - if (strlen(system->matches[2].substr)) - pr_info("Found %s/%s/%s\n", system->matches[0].substr, - system->matches[1].substr, system->matches[2].substr); - else - pr_info("Found %s/%s\n", system->matches[0].substr, - system->matches[1].substr); - driver_data = system->driver_data; + + // figure out if this is really an AS67xx or instead a FS67xx ("Flashstor"), + // which has different LEDs and only supports m.2 SSDs (no SATA drives) + if (driver_data == &asustor_6700_driver_data) { + // this matches the "ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch" (rev 01) + // PCI bridge (vendor ID 0x1b21, device ID 0x2806 aka 1b21:2806), which AFAIK is only used + // in Flashtor devices (though unfortunately we only had FS6712X and AS5402T to check) + // see also https://github.com/mafredri/asustor-platform-driver/pull/21#issuecomment-2420883171 + // and following. + if (pci_get_device(0x1b21, 0x2806, NULL) != NULL) { + // TODO: if necessary, we could count those devices; the current assumption + // is that the bigger *A*S67xx (or AS54xx) devices don't have this at all + + driver_data = &asustor_fs6700_driver_data; + } + } + + pr_info("Found %s (%s/%s)\n", driver_data->name, system->matches[0].substr, + system->matches[1].substr); + gpiod_add_lookup_table(driver_data->leds); gpiod_add_lookup_table(driver_data->keys); From fb3f9810f8af77060a7905e1051229c9bb07e908 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sat, 19 Oct 2024 02:48:07 +0200 Subject: [PATCH 02/12] asustor.c: Implement force_device argument to override device detection can be used like `modprobe asustor force_device=AS66xx` or by adding /etc/modprobe.d/asustor.conf with a line like options asustor force_device=FS67xx (Also ran clang-format) --- asustor.c | 94 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 25 deletions(-) diff --git a/asustor.c b/asustor.c index 7b8a2f8..192527a 100644 --- a/asustor.c +++ b/asustor.c @@ -252,6 +252,8 @@ struct asustor_driver_data { struct gpiod_lookup_table *keys; }; +// NOTE: if you add another device here, update VALID_OVERRIDE_NAMES accordingly! + static struct asustor_driver_data asustor_fs6700_driver_data = { .name = "FS67xx", .leds = &asustor_fs6700_gpio_leds_lookup, @@ -285,6 +287,8 @@ static struct asustor_driver_data asustor_600_driver_data = { .keys = &asustor_600_gpio_keys_lookup, }; +#define VALID_OVERRIDE_NAMES "AS6xx, AS61xx, AS66xx, AS67xx, FS67xx" + static const struct dmi_system_id asustor_systems[] = { { // Note: This not only matches (and works with) AS670xT (Lockerstore Gen2), @@ -365,6 +369,13 @@ static struct gpio_chip *find_chip_by_name(const char *name) } #endif +static char *force_device = ""; +module_param(force_device, charp, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC( + force_device, + "Don't try to detect ASUSTOR device, use the given one instead. " + "Valid values: " VALID_OVERRIDE_NAMES); + // TODO(mafredri): Allow force model for testing. static int __init asustor_init(void) { @@ -372,32 +383,64 @@ static int __init asustor_init(void) const struct gpiod_lookup *keys_table; int ret, i; - system = dmi_first_match(asustor_systems); - if (!system) { - pr_info("No supported ASUSTOR mainboard found"); - return -ENODEV; - } - - driver_data = system->driver_data; - - // figure out if this is really an AS67xx or instead a FS67xx ("Flashstor"), - // which has different LEDs and only supports m.2 SSDs (no SATA drives) - if (driver_data == &asustor_6700_driver_data) { - // this matches the "ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch" (rev 01) - // PCI bridge (vendor ID 0x1b21, device ID 0x2806 aka 1b21:2806), which AFAIK is only used - // in Flashtor devices (though unfortunately we only had FS6712X and AS5402T to check) - // see also https://github.com/mafredri/asustor-platform-driver/pull/21#issuecomment-2420883171 - // and following. - if (pci_get_device(0x1b21, 0x2806, NULL) != NULL) { - // TODO: if necessary, we could count those devices; the current assumption - // is that the bigger *A*S67xx (or AS54xx) devices don't have this at all - + driver_data = NULL; + // allow overriding detection with force_device kernel parameter + if (force_device && *force_device) { + // special case: FS67xx isn't in the asustor_systems table, as it can't + // be detected through DMI + if (strcmp(force_device, "FS67xx") == 0) { driver_data = &asustor_fs6700_driver_data; + } else { + for (i = 0; i < ARRAY_SIZE(asustor_systems); i++) { + struct asustor_driver_data *dd = + asustor_systems[i].driver_data; + if (dd && dd->name && + strcmp(force_device, dd->name) == 0) { + driver_data = dd; + break; + } + } + if (driver_data == NULL) { + pr_err("force_device parameter set to invalid value \"%s\"!\n", + force_device); + pr_info(" valid force_device values are: %s\n", + VALID_OVERRIDE_NAMES); + return -EINVAL; + } + } + pr_info("force_device parameter is set to \"%s\", treating your machine as " + "that device instead of trying to detect it!\n", + force_device); + } else { // try to detect the ASUSTOR device + system = dmi_first_match(asustor_systems); + if (!system) { + pr_info("No supported ASUSTOR mainboard found"); + return -ENODEV; } - } - pr_info("Found %s (%s/%s)\n", driver_data->name, system->matches[0].substr, - system->matches[1].substr); + driver_data = system->driver_data; + + // figure out if this is really an AS67xx or instead a FS67xx ("Flashstor"), + // which has different LEDs and only supports m.2 SSDs (no SATA drives) + if (driver_data == &asustor_6700_driver_data) { + // this matches the "ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 + // Packet Switch" (rev 01) PCI bridge (vendor ID 0x1b21, device ID 0x2806 + // aka 1b21:2806), which AFAIK is only used in Flashtor devices + // (though unfortunately we only had FS6712X and AS5402T to check) + // see also https://github.com/mafredri/asustor-platform-driver/pull/21#issuecomment-2420883171 + // and following. + if (pci_get_device(0x1b21, 0x2806, NULL) != NULL) { + // TODO: if necessary, we could count those devices; the current + // assumption is that even the bigger *A*S67xx (or AS54xx) + // devices don't have this at all + + driver_data = &asustor_fs6700_driver_data; + } + } + + pr_info("Found %s or similar (%s/%s)\n", driver_data->name, + system->matches[0].substr, system->matches[1].substr); + } gpiod_add_lookup_table(driver_data->leds); gpiod_add_lookup_table(driver_data->keys); @@ -427,8 +470,9 @@ static int __init asustor_init(void) goto err; } - asustor_keys_pdev = asustor_create_pdev( - "gpio-keys-polled", &asustor_keys_pdata, sizeof(asustor_keys_pdata)); + asustor_keys_pdev = + asustor_create_pdev("gpio-keys-polled", &asustor_keys_pdata, + sizeof(asustor_keys_pdata)); if (IS_ERR(asustor_keys_pdev)) { ret = PTR_ERR(asustor_keys_pdev); platform_device_unregister(asustor_leds_pdev); From 0279957fe7df08ff13d6569945f6fdbd69f3c744 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sat, 19 Oct 2024 04:44:28 +0200 Subject: [PATCH 03/12] Update README for force_device module parameter --- README.md | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2fba458..968a9c5 100644 --- a/README.md +++ b/README.md @@ -38,12 +38,22 @@ On many systems, ASUSTOR uses a mix of IT87 and CPU GPIOs to control leds and bu - AS5402T, AS5404T - FS6706T (NOT TESTED!), FS6712X - .. possibly more, if they're similar enough. - The following DMI system-manufacturer / system-product-name combinations are currently supported - (see `sudo dmidecode -s system-manufacturer` and `sudo dmidecode -s system-product-name`): - - "ASUSTOR Inc." / "AS-6xxT" - - "Insyde" / "AS61xx" - - "Insyde" / "GeminiLake" - - "Intel Corporation" / "Jasper Lake Client Platform" + +The following DMI system-manufacturer / system-product-name combinations are currently supported +(see `sudo dmidecode -s system-manufacturer` and `sudo dmidecode -s system-product-name`): +* "ASUSTOR Inc." / "AS-6xxT" + - Identified by `asustor` kernel module as **"AS6xx"** +* "Insyde" / "AS61xx" + - Identified by `asustor` kernel module as **"AS61xx"** +* "Insyde" / "GeminiLake" + - These are the *Lockerstor* AS66xxT devices, like AS6604T + - *maybe also others like Nimbustor AS520xT?* + - Identified by `asustor` kernel module as **"AS66xx"** +* "Intel Corporation" / "Jasper Lake Client Platform" + - These are the *Lockerstor Gen2* AS67xxT (AS6702T etc), *Nimbustor Gen2* AS54xxT (AS5402T etc) + and *Flashstor* FS6706T/FS6712X devices. + - Identified by `asustor` kernel module as **"AS67xx"** for *Lockerstor Gen2* and *Nimbustor Gen2* + - **_or_** identified as **"FS67xx"** if it's a *Flashstor* device ## Features @@ -142,6 +152,28 @@ Note that `it87` conflicts with `asustor-it87`, you may wish to add `it87` to th ~~You may want to use [`patches/001-ignore-pwm-polarity-it87.patch`](patches/001-ignore-pwm-polarity-it87.patch) for the `it87` kernel module if it complains about PWM polarity. In this case, it's possible to use `fix_pwm_polarity=1`, however, it may reverse the polarity which is unwanted (i.e. high is low, low is high). It works fine when left as configured by the firmware.~~ +### Override detection of ASUSTOR device by `asustor` kernel module + +If the `asustor` kernel module doesn't detect your device correctly, you can force it to treat your +ASUSTOR device as one of the supported devices by setting the `force_device` module parameter. + +can be used like: +`$ sudo modprobe asustor force_device=AS66xx` +or by creating an `/etc/modprobe.d/asustor.conf` text file with the following content: + +``` +# override device detection of the asustor kernel module +options asustor force_device=FS67xx +``` + +Of course you should replace "FS67xx" with the device you want to try, see the [Compatiblity](#compatibility) +section above for how the `asustor` kernel module identifies devices, or +`$ sudo modinfo -p asustor` +which will print a short usage info including the currently supported device names for `force_device`. + +_**NOTE:** If you need to use the `force_device` parameter to make your device work, please open an issue +so the detection logic in the `asustor` kernel module can be fixed to properly support it._ + ### Misc - `blue:power` and `red:power` can be turned on simultaneously for a pink-ish tint From 0a77a0a8e3ea8e314243ccef6498c34c1eaf1121 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 20 Oct 2024 03:53:32 +0200 Subject: [PATCH 04/12] asustor.c: Generalize system detection with matching PCI devices Instead of using dmi_first_match(asustor_systems) to figure out what kind of ASUSTOR system we're on, I added custom pci_matches[] to asustor_driver_data and wrote custom logic to find the matching system based on both DMI data *and* PCI devices. It's a bit ugly that I had to copy the dmi_matches() code from drivers/firmware/dmi_scan.c, but as that function (or equivalent ones) aren't exported by dmi_scan.c (it's static), it was unavoidable. On the upside, this helped getting rid of the special-casing of FS67xx in asustor_init() and is potentially useful for matching other systems in the future. It should even be a good base for implementing matching of other kinds of devices (than PCI/PCIe), if that becomes necessary. NOTE: While the asustor_systems array isn't fed into dmi_first_match() anymore, it still needs to use the dmi_system_id type so it can be used with MODULE_DEVICE_TABLE(), which creates the alias entries that can be seen in `modinfo asustor`, like alias: dmi*:svn*Insyde*:pn*AS61xx*: and is also used to automatically load the asustor kernel module on boot if the DMI entries in those aliases match the current system --- README.md | 1 + asustor.c | 227 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 180 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 968a9c5..d3c1ae3 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,7 @@ sudo dmidecode -s bios-version sudo dmidecode -s bios-release-date sudo dmidecode -s bios-revision sudo gpioinfo +sudo lspci -nn -PP ``` NOTE: If `gpioinfo` does not return anything, you may need to figure out which (if any) gpio drivers to load. Also keep in mind that your distribution may not ship with all `gpio-` drivers, so you may need to compile them yourself. diff --git a/asustor.c b/asustor.c index 192527a..0dbce7d 100644 --- a/asustor.c +++ b/asustor.c @@ -245,23 +245,53 @@ static struct gpiod_lookup_table asustor_600_gpio_keys_lookup = { }; // clang-format on +struct pci_device_match { + // match PCI devices with the given vendorID and productID (to help identify ASUSTOR systems) + // you can get them from `lspci -nn`, for example in + // "00:08.0 System peripheral [0880]: Intel Corporation Device [8086:4e11]" + // 0x8086 is the vendorID and 0x4e11 is the deviceID + uint16_t vendorID; + uint16_t deviceID; + + int16_t min_count; // how often that device should exist at least + int16_t max_count; // how often that device should exist at most +}; + // ASUSTOR Platform. struct asustor_driver_data { - const char *name; + const char *name; // used for force_device and for some log messages + + struct pci_device_match pci_matches[4]; + struct gpiod_lookup_table *leds; struct gpiod_lookup_table *keys; }; +#define VALID_OVERRIDE_NAMES "AS6xx, AS61xx, AS66xx, AS67xx, FS67xx" + // NOTE: if you add another device here, update VALID_OVERRIDE_NAMES accordingly! static struct asustor_driver_data asustor_fs6700_driver_data = { .name = "FS67xx", + // FS67xx needs to match PCI devices because it has the same DMI data as *A*S67xx + .pci_matches = { + // PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01) + // FS6712X seems to have 15 of these, no idea about FS6706T (so I keep min_count at 1) + // currently the upper limit doesn't matter so I just use 10000 + { 0x1b21, 0x2806, 1, 10000 }, + }, .leds = &asustor_fs6700_gpio_leds_lookup, .keys = &asustor_fs6700_gpio_keys_lookup, }; static struct asustor_driver_data asustor_6700_driver_data = { .name = "AS67xx", + // AS67xx needs to match PCI devices because it has the same DMI data as *F*S67xx + .pci_matches = { + // PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01) + // *F*S67xx seems to use these, I think *A*S67xx doesn't, so expect 0 + { 0x1b21, 0x2806, 0, 0 }, + }, .leds = &asustor_6700_gpio_leds_lookup, .keys = &asustor_6100_gpio_keys_lookup, }; @@ -271,6 +301,8 @@ static struct asustor_driver_data asustor_6600_driver_data = { // because it seems to use the same GPIO numbers, // but listed extra for the different name .name = "AS66xx", + // This (and the remaining systems) don't need to match PCI devices to be detected, + // so they're not set here (and thus initialized to all-zero) .leds = &asustor_6700_gpio_leds_lookup, .keys = &asustor_6100_gpio_keys_lookup, }; @@ -287,15 +319,28 @@ static struct asustor_driver_data asustor_600_driver_data = { .keys = &asustor_600_gpio_keys_lookup, }; -#define VALID_OVERRIDE_NAMES "AS6xx, AS61xx, AS66xx, AS67xx, FS67xx" - +// NOTE: Don't use this table with dmi_first_match(), because it has two entries that +// match the same (AS67xx and FS67xx). find_matching_asustor_system() handles +// that by also matching PCI devices from asustor_driver_data::pci_matches. +// This table only exists in this form (instead of just using an array of +// asustor_driver_data) for MODULE_DEVICE_TABLE(). static const struct dmi_system_id asustor_systems[] = { + // NOTE: each entry in this table must have its own unique asustor_driver_data + // (having a unique .name) set as .driver_data + { + // Note: yes, this is the same DMI match as the next entry, because just by DMI, + // FS67xx and AS67xx can't be told apart. But our custom matching logic + // in find_matching_asustor_system() also takes driver_data->pci_matches[] + // into account, so that should be ok. + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"), + }, + .driver_data = &asustor_fs6700_driver_data, + }, { - // Note: This not only matches (and works with) AS670xT (Lockerstore Gen2), - // but also AS540xT (Nimbustor Gen2) and, unfortunately, also - // Flashstor (FS6706T and FS6712X) which can't be detected with DMI but is - // different enough from the AS67xx devices to need different treatment. - // asustor_init() has additional code to detect FS67xx based on available PCIe devices + // Note: This not only matches (and works with) AS670xT (Lockerstor Gen2), + // but also AS540xT (Nimbustor Gen2) .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"), @@ -369,6 +414,116 @@ static struct gpio_chip *find_chip_by_name(const char *name) } #endif +/** + * dmi_matches - check if dmi_system_id structure matches system DMI data + * @dmi: pointer to the dmi_system_id structure to check + */ +// copied from dmi_matches() in Linux 6.11.2 drivers/firmware/dmi_scan.c where it's private (static) +// with only one small change (dmi_val = dmi_get_system_info(s) instead of dmi_ident[s]) +static bool dmi_matches(const struct dmi_system_id *dmi) +{ + int i; + const char *dmi_val; + + for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) { + int s = dmi->matches[i].slot; + if (s == DMI_NONE) + break; + if (s == DMI_OEM_STRING) { + /* DMI_OEM_STRING must be exact match */ + const struct dmi_device *valid; + + valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, + dmi->matches[i].substr, NULL); + if (valid) + continue; + } else if ((dmi_val = dmi_get_system_info(s)) != NULL) { + if (dmi->matches[i].exact_match) { + if (!strcmp(dmi_val, dmi->matches[i].substr)) + continue; + } else { + if (strstr(dmi_val, dmi->matches[i].substr)) + continue; + } + } + + /* No match */ + return false; + } + return true; +} + +// how often does the PCI(e) device with given vendor/device IDs exist in this system? +static int count_pci_device_instances(unsigned int vendor, unsigned int device) +{ + // start with -1 as the do-while loop runs at least once even if nothing is found + int ret = -1; + struct pci_dev *pd = NULL; + do { + ++ret; + pd = pci_get_device(vendor, device, pd); + } while (pd != NULL); + return ret; +} + +// check if sys->pci_matches[] match with the PCI devices in the system +// NOTE: this only checks if the devices in sys->pci_matches[] exist in the system +// with the expected count (or do *not* exist if the counts are 0), +// PCI devices existing that aren't listed in sys->pci_matches[] is expected +// and does not make this function fail. +static bool pci_devices_match(const struct asustor_driver_data *sys) +{ + int i; + for (i = 0; i < ARRAY_SIZE(sys->pci_matches); ++i) { + int dev_cnt; + const struct pci_device_match *pdm; + pdm = &sys->pci_matches[i]; + if (pdm->vendorID == 0 && pdm->deviceID == 0) { + // no more entries, the previous ones matched + // or we would've returned false already + return true; + } + dev_cnt = count_pci_device_instances(pdm->vendorID, + pdm->deviceID); + if (dev_cnt < pdm->min_count || dev_cnt > pdm->max_count) { + return false; + } + } + return true; +} + +// find out which ASUSTOR system this is, based on asustor_systems[], including +// their linked asustor_driver_data's pci_matches +// returns NULL if this isn't a known system +static const struct dmi_system_id *find_matching_asustor_system(void) +{ + int as_idx; + + for (as_idx = 0; as_idx < ARRAY_SIZE(asustor_systems); as_idx++) { + struct asustor_driver_data *dd; + const struct dmi_system_id *sys; + sys = &asustor_systems[as_idx]; + dd = sys->driver_data; + if (dd == NULL) { + // no driverdata? must be at end of table + break; + } + + if (!dmi_matches(sys)) { + continue; + } + + if (!pci_devices_match(dd)) { + continue; + } + + // DMI and PCI devices matched => this is it + return sys; + } + + return NULL; +} + static char *force_device = ""; module_param(force_device, charp, S_IRUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC( @@ -386,33 +541,27 @@ static int __init asustor_init(void) driver_data = NULL; // allow overriding detection with force_device kernel parameter if (force_device && *force_device) { - // special case: FS67xx isn't in the asustor_systems table, as it can't - // be detected through DMI - if (strcmp(force_device, "FS67xx") == 0) { - driver_data = &asustor_fs6700_driver_data; - } else { - for (i = 0; i < ARRAY_SIZE(asustor_systems); i++) { - struct asustor_driver_data *dd = - asustor_systems[i].driver_data; - if (dd && dd->name && - strcmp(force_device, dd->name) == 0) { - driver_data = dd; - break; - } - } - if (driver_data == NULL) { - pr_err("force_device parameter set to invalid value \"%s\"!\n", - force_device); - pr_info(" valid force_device values are: %s\n", - VALID_OVERRIDE_NAMES); - return -EINVAL; + for (i = 0; i < ARRAY_SIZE(asustor_systems); i++) { + struct asustor_driver_data *dd = + asustor_systems[i].driver_data; + if (dd && dd->name && + strcmp(force_device, dd->name) == 0) { + driver_data = dd; + break; } } + if (driver_data == NULL) { + pr_err("force_device parameter set to invalid value \"%s\"!\n", + force_device); + pr_info(" valid force_device values are: %s\n", + VALID_OVERRIDE_NAMES); + return -EINVAL; + } pr_info("force_device parameter is set to \"%s\", treating your machine as " "that device instead of trying to detect it!\n", force_device); - } else { // try to detect the ASUSTOR device - system = dmi_first_match(asustor_systems); + } else { // try to detect the ASUSTOR system + system = find_matching_asustor_system(); if (!system) { pr_info("No supported ASUSTOR mainboard found"); return -ENODEV; @@ -420,24 +569,6 @@ static int __init asustor_init(void) driver_data = system->driver_data; - // figure out if this is really an AS67xx or instead a FS67xx ("Flashstor"), - // which has different LEDs and only supports m.2 SSDs (no SATA drives) - if (driver_data == &asustor_6700_driver_data) { - // this matches the "ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 - // Packet Switch" (rev 01) PCI bridge (vendor ID 0x1b21, device ID 0x2806 - // aka 1b21:2806), which AFAIK is only used in Flashtor devices - // (though unfortunately we only had FS6712X and AS5402T to check) - // see also https://github.com/mafredri/asustor-platform-driver/pull/21#issuecomment-2420883171 - // and following. - if (pci_get_device(0x1b21, 0x2806, NULL) != NULL) { - // TODO: if necessary, we could count those devices; the current - // assumption is that even the bigger *A*S67xx (or AS54xx) - // devices don't have this at all - - driver_data = &asustor_fs6700_driver_data; - } - } - pr_info("Found %s or similar (%s/%s)\n", driver_data->name, system->matches[0].substr, system->matches[1].substr); } From e4c6266a9e0ca5eb6c75d8bb113caea66bd956f3 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sat, 19 Oct 2024 01:41:52 +0200 Subject: [PATCH 05/12] Small improvements to LED table comments and README document that the disk-activity doesn't work for NVME drives and that LEDs sometimes appear to have different colors than their name might suggest, especially when two LEDs are visible as one --- README.md | 11 +++++++++++ asustor.c | 27 ++++++++------------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index d3c1ae3..9d62b85 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,15 @@ The following DMI system-manufacturer / system-product-name combinations are cur ## Features - LEDs (front panel, disk) + - Represented as subdirectories in `/sys/class/leds/` + - In the subdirectories, you can set `brightness` to 0 or 1 to switch the LED off or on, for example + `$ echo 1 | sudo tee blue:power/brightness` + similarly, the `trigger` can be configured, see [below](#set-triggers-for-leds) + - Sometimes the name of an LED doesn't exactly represent its color, for example, on the + *Flashstor FS6712X*, the `blue:lan` LED is actually purple when connected with 10GBit + (but blue when connected with 1GBit). Also, sometimes two LEDs physically appear as one, so + enabling both will create a third color (e.g. if both `nvme1:green` and `nvme1:red` are enabled, + it will look orange). - See [asustor.c](asustor.c). - Buttons - USB Copy Button @@ -144,6 +153,8 @@ echo r8169-0-200:00:link > /sys/class/leds/blue\:lan `cat /sys/class/leds/green\:usb/trigger` will list the available triggers, with the currently used one being marked with square brackes (e.g. `[none] kbd-scrolllock kbd-numlock kbd-capslock ...`). +Note that currently the disk-related triggers (like `disk-activity`) do **not** work with NVME drives. + ### `it87` and PWM polarity This project includes a patched version of the `it87` module that is part of mainline kernel (`asustor-it87`). It skips PWM sanity checks for the fan because ASUSTOR firmware correctly initializes fans in active low polarity and can be used straight with `fancontrol` or similar tools. diff --git a/asustor.c b/asustor.c index 0dbce7d..6860fd3 100644 --- a/asustor.c +++ b/asustor.c @@ -46,22 +46,25 @@ .default_state = LEDS_GPIO_DEFSTATE_OFF \ } +// clang-format off + // ASUSTOR Leds. // If ledtrig-blkdev ever lands, use that instead of disk-activity: // https://lore.kernel.org/linux-leds/20210819025053.222710-1-arequipeno@gmail.com/ +// Also, the "disk-activity" trigger does (currently?) *not* trigger for NVME devices. static struct gpio_led asustor_leds[] = { - { .name = "power:front_panel", - .default_state = LEDS_GPIO_DEFSTATE_ON }, // 0 + { .name = "power:front_panel", // 0 + .default_state = LEDS_GPIO_DEFSTATE_ON }, { .name = "power:lcd", .default_state = LEDS_GPIO_DEFSTATE_ON }, // 1 { .name = "blue:power", .default_state = LEDS_GPIO_DEFSTATE_ON }, // 2 { .name = "red:power", .default_state = LEDS_GPIO_DEFSTATE_OFF }, // 3 { .name = "green:status", .default_state = LEDS_GPIO_DEFSTATE_ON }, // 4 { - .name = "red:status", + .name = "red:status", // 5 .default_state = LEDS_GPIO_DEFSTATE_OFF, .panic_indicator = 1, .default_trigger = "panic", - }, // 5 + }, { .name = "blue:usb", .default_state = LEDS_GPIO_DEFSTATE_OFF }, // 6 { .name = "green:usb", .default_state = LEDS_GPIO_DEFSTATE_OFF }, // 7 { .name = "blue:lan", .default_state = LEDS_GPIO_DEFSTATE_ON }, // 8 @@ -86,7 +89,6 @@ static const struct gpio_led_platform_data asustor_leds_pdata = { .num_leds = ARRAY_SIZE(asustor_leds), }; -// clang-format off static struct gpiod_lookup_table asustor_fs6700_gpio_leds_lookup = { .dev_id = "leds-gpio", .table = { @@ -103,18 +105,7 @@ static struct gpiod_lookup_table asustor_fs6700_gpio_leds_lookup = { // 6 // 7 GPIO_LOOKUP_IDX(GPIO_IT87, 55, NULL, 8, GPIO_ACTIVE_HIGH), // blue:lan - // 9 - // 10 - // 11 - // 12 - // 13 - // 14 - // 15 - // 16 - // 17 - // 18 - // 19 - // 20 + // LEDs 9 - 20 don't exist in this system GPIO_LOOKUP_IDX(GPIO_IT87, 12, NULL, 21, GPIO_ACTIVE_LOW), // nvme1:green:disk GPIO_LOOKUP_IDX(GPIO_IT87, 13, NULL, 22, GPIO_ACTIVE_LOW), // nvme1:red:disk {} @@ -348,8 +339,6 @@ static const struct dmi_system_id asustor_systems[] = { .driver_data = &asustor_6700_driver_data, }, { - // Note: The same also seemed to work with AS6602T, - // though I can't test that anymore .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "GeminiLake"), From 9e162bac0f36503474a143c8552188837915adb2 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 27 Oct 2024 00:51:40 +0200 Subject: [PATCH 06/12] README: Add AS6706T to compatible devices, mark AS5404T as untested AS6706T has been confirmed as working by several users so it should be listed. AS5404T most probably is very similar to the (tested) AS6704T, like AS5402T is very similar to AS6702T, but so far hasn't been confirmed to work so I added `(NOT TESTED!)` to that entry --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9d62b85..334e7fe 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ On many systems, ASUSTOR uses a mix of IT87 and CPU GPIOs to control leds and bu - AS6104T (NOT TESTED!) - AS6204T - AS6602T, AS6604T (NOT TESTED!) -- AS6702T, AS6704T -- AS5402T, AS5404T +- AS6702T, AS6704T, AS6706T +- AS5402T, AS5404T (NOT TESTED!) - FS6706T (NOT TESTED!), FS6712X - .. possibly more, if they're similar enough. From 9c3dee5915d2f90e1fa518f0abc28a380e762cfb Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 24 Nov 2024 01:47:07 +0100 Subject: [PATCH 07/12] Most changes suggested in the code review including one to avoid leaking pci_dev structs in count_pci_device_instances() --- README.md | 24 ++++++++++++++---------- asustor.c | 37 ++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 334e7fe..0bfef4e 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,9 @@ The following DMI system-manufacturer / system-product-name combinations are cur - LEDs (front panel, disk) - Represented as subdirectories in `/sys/class/leds/` - - In the subdirectories, you can set `brightness` to 0 or 1 to switch the LED off or on, for example - `$ echo 1 | sudo tee blue:power/brightness` - similarly, the `trigger` can be configured, see [below](#set-triggers-for-leds) + - In the subdirectories, you can set `brightness` to 0 or 1 to switch the LED off or on + (for example `echo 1 | sudo tee blue:power/brightness`). Similarly, the `trigger` can + also be configured, see [below](#set-triggers-for-leds). - Sometimes the name of an LED doesn't exactly represent its color, for example, on the *Flashstor FS6712X*, the `blue:lan` LED is actually purple when connected with 10GBit (but blue when connected with 1GBit). Also, sometimes two LEDs physically appear as one, so @@ -154,6 +154,8 @@ echo r8169-0-200:00:link > /sys/class/leds/blue\:lan one being marked with square brackes (e.g. `[none] kbd-scrolllock kbd-numlock kbd-capslock ...`). Note that currently the disk-related triggers (like `disk-activity`) do **not** work with NVME drives. +That's a general limitation of the Linux kernel that is independent of this project. +If this feature is ever implemented in the kernel, it will automatically work with this driver. ### `it87` and PWM polarity @@ -168,19 +170,21 @@ Note that `it87` conflicts with `asustor-it87`, you may wish to add `it87` to th If the `asustor` kernel module doesn't detect your device correctly, you can force it to treat your ASUSTOR device as one of the supported devices by setting the `force_device` module parameter. -can be used like: -`$ sudo modprobe asustor force_device=AS66xx` -or by creating an `/etc/modprobe.d/asustor.conf` text file with the following content: +This can be done manually with `sudo modprobe asustor force_device=AS66xx`, or by creating +`/etc/modprobe.d/asustor.conf` with the following content: ``` # override device detection of the asustor kernel module options asustor force_device=FS67xx ``` -Of course you should replace "FS67xx" with the device you want to try, see the [Compatiblity](#compatibility) -section above for how the `asustor` kernel module identifies devices, or -`$ sudo modinfo -p asustor` -which will print a short usage info including the currently supported device names for `force_device`. +Please replace "FS67xx" with the device you want to try. +See the [Compatiblity](#compatibility)-section above for how the `asustor` kernel module identifies devices. +Alternatively, can use the following command to print module parameters, including the currently supported device names for `force_device`: + +```console +$ sudo modinfo -p asustor +``` _**NOTE:** If you need to use the `force_device` parameter to make your device work, please open an issue so the detection logic in the `asustor` kernel module can be fixed to properly support it._ diff --git a/asustor.c b/asustor.c index 6860fd3..7580dd3 100644 --- a/asustor.c +++ b/asustor.c @@ -248,6 +248,10 @@ struct pci_device_match { int16_t max_count; // how often that device should exist at most }; +enum { + DEVICE_COUNT_MAX = 0x7fff // INT16_MAX - used for "no upper limit" +}; + // ASUSTOR Platform. struct asustor_driver_data { const char *name; // used for force_device and for some log messages @@ -268,8 +272,8 @@ static struct asustor_driver_data asustor_fs6700_driver_data = { .pci_matches = { // PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01) // FS6712X seems to have 15 of these, no idea about FS6706T (so I keep min_count at 1) - // currently the upper limit doesn't matter so I just use 10000 - { 0x1b21, 0x2806, 1, 10000 }, + // currently the upper limit doesn't matter so I just use DEVICE_COUNT_MAX + { 0x1b21, 0x2806, 1, DEVICE_COUNT_MAX }, }, .leds = &asustor_fs6700_gpio_leds_lookup, .keys = &asustor_fs6700_gpio_keys_lookup, @@ -280,7 +284,8 @@ static struct asustor_driver_data asustor_6700_driver_data = { // AS67xx needs to match PCI devices because it has the same DMI data as *F*S67xx .pci_matches = { // PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01) - // *F*S67xx seems to use these, I think *A*S67xx doesn't, so expect 0 + // *F*S67xx seems to use these, *A*S67xx doesn't, so expect 0 + // (BTW, AS6706T has 5x "ASM2812 6-Port PCIe x4 Gen3 Packet Switch" [1b21:2812], I hope *F*S6706T doesn't) { 0x1b21, 0x2806, 0, 0 }, }, .leds = &asustor_6700_gpio_leds_lookup, @@ -319,7 +324,7 @@ static const struct dmi_system_id asustor_systems[] = { // NOTE: each entry in this table must have its own unique asustor_driver_data // (having a unique .name) set as .driver_data { - // Note: yes, this is the same DMI match as the next entry, because just by DMI, + // NOTE: yes, this is the same DMI match as the next entry, because just by DMI, // FS67xx and AS67xx can't be told apart. But our custom matching logic // in find_matching_asustor_system() also takes driver_data->pci_matches[] // into account, so that should be ok. @@ -330,7 +335,7 @@ static const struct dmi_system_id asustor_systems[] = { .driver_data = &asustor_fs6700_driver_data, }, { - // Note: This not only matches (and works with) AS670xT (Lockerstor Gen2), + // NOTE: This not only matches (and works with) AS670xT (Lockerstor Gen2), // but also AS540xT (Nimbustor Gen2) .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), @@ -442,22 +447,28 @@ static bool dmi_matches(const struct dmi_system_id *dmi) return true; } -// how often does the PCI(e) device with given vendor/device IDs exist in this system? +// How many PCI(e) devices with given vendor/device IDs exist in this system? static int count_pci_device_instances(unsigned int vendor, unsigned int device) { - // start with -1 as the do-while loop runs at least once even if nothing is found - int ret = -1; - struct pci_dev *pd = NULL; - do { + int ret = 0; + struct pci_dev *pd, *last_pd = NULL; + + while ((pd = pci_get_device(vendor, device, last_pd)) != NULL) { + if (last_pd) { + pci_dev_put(last_pd); + } + last_pd = pd; ++ret; - pd = pci_get_device(vendor, device, pd); - } while (pd != NULL); + } + if (last_pd) { + pci_dev_put(last_pd); + } return ret; } // check if sys->pci_matches[] match with the PCI devices in the system // NOTE: this only checks if the devices in sys->pci_matches[] exist in the system -// with the expected count (or do *not* exist if the counts are 0), +// with the expected count (or the device does *not* exist if the counts are 0), // PCI devices existing that aren't listed in sys->pci_matches[] is expected // and does not make this function fail. static bool pci_devices_match(const struct asustor_driver_data *sys) From 7342195772beb812ef90ad689fafe17b4eeaad2a Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 24 Nov 2024 21:37:16 +0100 Subject: [PATCH 08/12] Split asustor.c into asustor_main.c and asustor_gpl2.c asustor_main.c is licensed under GPLv2 or later, while asustor_gpl2.c is licensed under GPLv2 only. dmi_matches() is moved from asustor(_main).c to asustor_gpl2.c, because it's copied from drivers/firmware/dmi_scan.c which is released under GPLv2 only. asustor.c had to be renamed to asustor_main.c, because apparently the kernel build system only supports a source file with the same name as the module if that module is built only from that file. I also bumped the DRIVER_VERSION (used for DKMS) from v0.1 to v0.2 to hopefully avoid conflicts due to all these changes --- Makefile | 7 ++++-- README.md | 2 +- asustor_gpl2.c | 48 +++++++++++++++++++++++++++++++++++++ asustor.c => asustor_main.c | 48 ++++++------------------------------- 4 files changed, 61 insertions(+), 44 deletions(-) create mode 100644 asustor_gpl2.c rename asustor.c => asustor_main.c (94%) diff --git a/Makefile b/Makefile index f206859..8068984 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ KERNEL_MODULES := /lib/modules/$(TARGET) KERNEL_BUILD := $(KERNEL_MODULES)/build SYSTEM_MAP := /boot/System.map-$(TARGET) DRIVER := asustor asustor_it87 asustor_gpio_it87 -DRIVER_VERSION := v0.1 +DRIVER_VERSION := v0.2 #DRIVER_VERSION ?= $(shell git describe --long) # DKMS @@ -17,6 +17,8 @@ asustor_gpio_it87_DEST_DIR = $(KERNEL_MODULES)/kernel/drivers/gpio obj-m := $(patsubst %,%.o,$(DRIVER)) obj-ko := $(patsubst %,%.ko,$(DRIVER)) +# asustor.o is built from two source files for license reasons +asustor-y := asustor_main.o asustor_gpl2.o all: modules @@ -38,8 +40,9 @@ dkms: @mkdir -p $(DKMS_ROOT_PATH_ASUSTOR) @echo "obj-m := asustor.o" >>$(DKMS_ROOT_PATH_ASUSTOR)/Makefile @echo "obj-ko := asustor.ko" >>$(DKMS_ROOT_PATH_ASUSTOR)/Makefile + @echo "asustor-y := asustor_main.o asustor_gpl2.o" >>$(DKMS_ROOT_PATH_ASUSTOR)/Makefile @cp dkms.conf $(DKMS_ROOT_PATH_ASUSTOR) - @cp asustor.c $(DKMS_ROOT_PATH_ASUSTOR) + @cp asustor_main.c asustor_gpl2.c $(DKMS_ROOT_PATH_ASUSTOR) @sed -i -e '/^PACKAGE_VERSION=/ s/=.*/=\"$(DRIVER_VERSION)\"/' $(DKMS_ROOT_PATH_ASUSTOR)/dkms.conf @mkdir -p $(DKMS_ROOT_PATH_ASUSTOR_IT87) diff --git a/README.md b/README.md index 0bfef4e..dc23ad8 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ The following DMI system-manufacturer / system-product-name combinations are cur (but blue when connected with 1GBit). Also, sometimes two LEDs physically appear as one, so enabling both will create a third color (e.g. if both `nvme1:green` and `nvme1:red` are enabled, it will look orange). - - See [asustor.c](asustor.c). + - See [asustor_main.c](asustor_main.c). - Buttons - USB Copy Button - Power Button (AS6) diff --git a/asustor_gpl2.c b/asustor_gpl2.c new file mode 100644 index 0000000..bce740a --- /dev/null +++ b/asustor_gpl2.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * asustor_gpl2.c - Functions used by asustor_main.c (for asustor.ko) that were copied from the + * Linux kernel and are licensed under GPLv2 ONLY, instead of GPLv2 or later. + */ + +#include + +bool asustor_dmi_matches(const struct dmi_system_id *dmi); + +/** + * dmi_matches - check if dmi_system_id structure matches system DMI data + * @dmi: pointer to the dmi_system_id structure to check + */ +// copied from dmi_matches() in Linux 6.11.2 drivers/firmware/dmi_scan.c where it's private (static) +// with only one small change (dmi_val = dmi_get_system_info(s) instead of dmi_ident[s]) +bool asustor_dmi_matches(const struct dmi_system_id *dmi) +{ + int i; + const char *dmi_val; + + for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) { + int s = dmi->matches[i].slot; + if (s == DMI_NONE) + break; + if (s == DMI_OEM_STRING) { + /* DMI_OEM_STRING must be exact match */ + const struct dmi_device *valid; + + valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, + dmi->matches[i].substr, NULL); + if (valid) + continue; + } else if ((dmi_val = dmi_get_system_info(s)) != NULL) { + if (dmi->matches[i].exact_match) { + if (!strcmp(dmi_val, dmi->matches[i].substr)) + continue; + } else { + if (strstr(dmi_val, dmi->matches[i].substr)) + continue; + } + } + + /* No match */ + return false; + } + return true; +} diff --git a/asustor.c b/asustor_main.c similarity index 94% rename from asustor.c rename to asustor_main.c index 7580dd3..91f2188 100644 --- a/asustor.c +++ b/asustor_main.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * asustor.c - Platform driver for ASUSTOR NAS hardware + * asustor_main.c - main part of asustor.ko, a platform driver for ASUSTOR NAS hardware + * (the other part is in asustor_gpl2.c which is GPL-2.0-only) * * Copyright (C) 2021 Mathias Fredriksson */ @@ -408,45 +409,6 @@ static struct gpio_chip *find_chip_by_name(const char *name) } #endif -/** - * dmi_matches - check if dmi_system_id structure matches system DMI data - * @dmi: pointer to the dmi_system_id structure to check - */ -// copied from dmi_matches() in Linux 6.11.2 drivers/firmware/dmi_scan.c where it's private (static) -// with only one small change (dmi_val = dmi_get_system_info(s) instead of dmi_ident[s]) -static bool dmi_matches(const struct dmi_system_id *dmi) -{ - int i; - const char *dmi_val; - - for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) { - int s = dmi->matches[i].slot; - if (s == DMI_NONE) - break; - if (s == DMI_OEM_STRING) { - /* DMI_OEM_STRING must be exact match */ - const struct dmi_device *valid; - - valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, - dmi->matches[i].substr, NULL); - if (valid) - continue; - } else if ((dmi_val = dmi_get_system_info(s)) != NULL) { - if (dmi->matches[i].exact_match) { - if (!strcmp(dmi_val, dmi->matches[i].substr)) - continue; - } else { - if (strstr(dmi_val, dmi->matches[i].substr)) - continue; - } - } - - /* No match */ - return false; - } - return true; -} - // How many PCI(e) devices with given vendor/device IDs exist in this system? static int count_pci_device_instances(unsigned int vendor, unsigned int device) { @@ -492,6 +454,10 @@ static bool pci_devices_match(const struct asustor_driver_data *sys) return true; } +// returns true if dmi->matches match on the current system +// implemented in asustor_gpl2.c +extern bool asustor_dmi_matches(const struct dmi_system_id *dmi); + // find out which ASUSTOR system this is, based on asustor_systems[], including // their linked asustor_driver_data's pci_matches // returns NULL if this isn't a known system @@ -509,7 +475,7 @@ static const struct dmi_system_id *find_matching_asustor_system(void) break; } - if (!dmi_matches(sys)) { + if (!asustor_dmi_matches(sys)) { continue; } From 4d305a5a2d4bba5deafe1df8f46b1c05872a9028 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Mon, 25 Nov 2024 02:03:53 +0100 Subject: [PATCH 09/12] README: Add link the nvme disk activity trigger kernel patch .. and update the link to the block device LED trigger to the (AFAIK) latest (last?) attempt by Ian Pilcher (v13) Though TBH it seems like neither is ever gonna happen, because the block subsystem maintainers don't even want to provide an API that allows accessing block device data based on the device name (like sda), and generally seem to be unfriendly, so maybe the best solution would be to implement this in the userland (periodically check disks read/write stats and control blinking based on that)... --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dc23ad8..a4a2a3e 100644 --- a/README.md +++ b/README.md @@ -222,7 +222,10 @@ NOTE: If `gpioinfo` does not return anything, you may need to figure out which ( - Support variable amount of disk LEDs - ~~Create a new led trigger driver so that we can blink disk LEDs individually, the existing `disk-activity` trigger always blinks all LEDs on activity from any disk~~ - - Pray that [[RFC PATCH v3 00/18] Add block device LED trigger](https://lore.kernel.org/linux-leds/20210819025053.222710-1-arequipeno@gmail.com/) by Ian Pilcher lands in the linux kernel + - Pray that [[PATCH v13 0/2] Introduce block device LED trigger](https://lore.kernel.org/lkml/20221227225226.546489-1-arequipeno@gmail.com/T/#mc8758efa18e1b7ed51a50c298d881a2e91280b1f) + by Ian Pilcher lands in the linux kernel + - Pray that [[PATCH] nvme-pci: trigger disk activity LED](https://lore.kernel.org/lkml/4100a868-c5bd-91dd-0c45-a92fb1344b12@kernel.dk/T/) + by Enzo Matsumiya (or an alternative implementation that lets NVME disk activity trigger LEDs) lands in the linux kernel ## DKMS From 157c69c1ff3e78208b7cac7050b71caf9cb27a14 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Tue, 26 Nov 2024 17:29:58 +0100 Subject: [PATCH 10/12] asustor_main.c: Avoid leaking struct gpio_device turns out that the gpio_device* returned by gpio_device_find_by_label() must be "freed" (or the refcounter decremented) by calling gpio_device_put(dev). While fixing that, I refactored the code for getting the GPIO chip's base number a bit so we don't have to use the (deprecated) gpio_device_get_chip() anymore. --- asustor_main.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/asustor_main.c b/asustor_main.c index 91f2188..932bff0 100644 --- a/asustor_main.c +++ b/asustor_main.c @@ -391,21 +391,30 @@ static struct platform_device *__init asustor_create_pdev(const char *name, static int gpiochip_match_name(struct gpio_chip *chip, void *data) { const char *name = data; - return !strcmp(chip->label, name); } -static struct gpio_chip *find_chip_by_name(const char *name) +// -1 means "not found" +static int get_gpio_base_for_chipname(const char *name) { - return gpiochip_find((void *)name, gpiochip_match_name); + struct gpio_chip *gc = gpiochip_find((void *)name, gpiochip_match_name); + return (gc != NULL) ? gc->base : -1; } #else // DG: kernel 6.7 removed gpiochip_find() and introduced gpio_device_find() // and friends instead -static struct gpio_chip *find_chip_by_name(const char *name) + +// -1 means "not found" +static int get_gpio_base_for_chipname(const char *name) { + int ret = -1; struct gpio_device *dev = gpio_device_find_by_label(name); - return (dev != NULL) ? gpio_device_get_chip(dev) : NULL; + if (dev != NULL) { + ret = gpio_device_get_base(dev); + // make sure to decrement the reference count of dev + gpio_device_put(dev); + } + return ret; } #endif @@ -549,12 +558,12 @@ static int __init asustor_init(void) for (; keys_table->key != NULL; keys_table++) { if (i == keys_table->idx) { // add the GPIO chip's base, so we get the absolute (global) gpio number - struct gpio_chip *chip = - find_chip_by_name(keys_table->key); - if (chip == NULL) + const char *cn = keys_table->key; + int gpio_base = get_gpio_base_for_chipname(cn); + if (gpio_base == -1) continue; asustor_gpio_keys_table[i].gpio = - chip->base + keys_table->chip_hwnum; + gpio_base + keys_table->chip_hwnum; } } } From 9c9cc669d8c6b22f12c203c97b05f5b294851118 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sat, 14 Dec 2024 14:11:23 +0100 Subject: [PATCH 11/12] Support FS6706T, detect every AS67xx and FS67xx device separately Looks like the Flashtor for 6 SSDs doesn't have any PCIe device that the *A*S67xx devices don't have. So now we painstakingly detect every AS67xx and FS67xx separately. On the upside, this allows exposing only the SATA LEDs that actually exist on each specific AS67xx device. This also means that the overrides (force_device kernel module argument) for those devices have changed and are now "AS6702", "AS6704", "AS6706" instead of "AS67xx" and "FS6706", "FS6712" instead of "FS67xx" --- README.md | 14 ++-- asustor_main.c | 190 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 173 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index a4a2a3e..a03e0f9 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ On many systems, ASUSTOR uses a mix of IT87 and CPU GPIOs to control leds and bu ### Optional -- `it87` (AS6, AS61, AS62, AS66XX, AS67XX, AS54XX) +- `it87` (AS6, AS61, AS62, AS66XX, AS67XX, AS54XX, FS67XX) - This project includes a patched version of `it87` called `asustor-it87` which skips fan pwm sanity checks and supports more variants of IT86XX and the IT87XX chips than the kernels `it87` driver. Support for timer-based blinking of up to two LEDs (only works on some models) has also been added. @@ -52,8 +52,12 @@ The following DMI system-manufacturer / system-product-name combinations are cur * "Intel Corporation" / "Jasper Lake Client Platform" - These are the *Lockerstor Gen2* AS67xxT (AS6702T etc), *Nimbustor Gen2* AS54xxT (AS5402T etc) and *Flashstor* FS6706T/FS6712X devices. - - Identified by `asustor` kernel module as **"AS67xx"** for *Lockerstor Gen2* and *Nimbustor Gen2* - - **_or_** identified as **"FS67xx"** if it's a *Flashstor* device + - Identified by `asustor` kernel module as: + * **"AS6702"** for *Lockerstor Gen2* and *Nimbustor Gen2* with *two* SATA drives (AS6702T, AS5402T) + * **"AS6704"** for *Lockerstor Gen2* and *Nimbustor Gen2* with *four* SATA drives (AS6704T, AS5404T) + * **"AS6706"** for *Lockerstor Gen2* with *six* SATA drives (AS6706T) + * **"FS6706"** for *Flashtor* with *six* slots for m.2 NVME SSDs (FS6706T) + * **"FS6712"** for *Flashtor* with *twelve* slots for m.2 NVME SSDs (FS6712X) ## Features @@ -175,10 +179,10 @@ This can be done manually with `sudo modprobe asustor force_device=AS66xx`, or b ``` # override device detection of the asustor kernel module -options asustor force_device=FS67xx +options asustor force_device=FS6712 ``` -Please replace "FS67xx" with the device you want to try. +Please replace "FS6712" with the device you want to try. See the [Compatiblity](#compatibility)-section above for how the `asustor` kernel module identifies devices. Alternatively, can use the following command to print module parameters, including the currently supported device names for `force_device`: diff --git a/asustor_main.c b/asustor_main.c index 932bff0..e9d98b9 100644 --- a/asustor_main.c +++ b/asustor_main.c @@ -113,7 +113,51 @@ static struct gpiod_lookup_table asustor_fs6700_gpio_leds_lookup = { }, }; -static struct gpiod_lookup_table asustor_6700_gpio_leds_lookup = { +static struct gpiod_lookup_table asustor_as6702_gpio_leds_lookup = { + .dev_id = "leds-gpio", + .table = { + // 0: AS6702T and AS5402T don't have a front panel to illuminate + // 1: they don't have a LCD either + GPIO_LOOKUP_IDX(GPIO_IT87, 56, NULL, 2, GPIO_ACTIVE_LOW), // blue:power + GPIO_LOOKUP_IDX(GPIO_IT87, 8, NULL, 3, GPIO_ACTIVE_LOW), // red:power + GPIO_LOOKUP_IDX(GPIO_IT87, 31, NULL, 4, GPIO_ACTIVE_LOW), // green:status + GPIO_LOOKUP_IDX(GPIO_IT87, 49, NULL, 5, GPIO_ACTIVE_LOW), // red:status + // 6 + GPIO_LOOKUP_IDX(GPIO_IT87, 21, NULL, 7, GPIO_ACTIVE_LOW), // green:usb + GPIO_LOOKUP_IDX(GPIO_IT87, 55, NULL, 8, GPIO_ACTIVE_HIGH), // blue:lan + GPIO_LOOKUP_IDX(GPIO_IT87, 12, NULL, 9, GPIO_ACTIVE_HIGH), // sata1:green:disk + GPIO_LOOKUP_IDX(GPIO_IT87, 13, NULL, 10, GPIO_ACTIVE_LOW), // sata1:red:disk + GPIO_LOOKUP_IDX(GPIO_IT87, 46, NULL, 11, GPIO_ACTIVE_HIGH), // sata2:green:disk + GPIO_LOOKUP_IDX(GPIO_IT87, 47, NULL, 12, GPIO_ACTIVE_LOW), // sata2:red:disk + {} + }, +}; + +static struct gpiod_lookup_table asustor_as6704_gpio_leds_lookup = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX(GPIO_IT87, 29, NULL, 0, GPIO_ACTIVE_HIGH), // power:front_panel + GPIO_LOOKUP_IDX(GPIO_IT87, 59, NULL, 1, GPIO_ACTIVE_HIGH), // power:lcd + GPIO_LOOKUP_IDX(GPIO_IT87, 56, NULL, 2, GPIO_ACTIVE_LOW), // blue:power + GPIO_LOOKUP_IDX(GPIO_IT87, 8, NULL, 3, GPIO_ACTIVE_LOW), // red:power + GPIO_LOOKUP_IDX(GPIO_IT87, 31, NULL, 4, GPIO_ACTIVE_LOW), // green:status + GPIO_LOOKUP_IDX(GPIO_IT87, 49, NULL, 5, GPIO_ACTIVE_LOW), // red:status + // 6 + GPIO_LOOKUP_IDX(GPIO_IT87, 21, NULL, 7, GPIO_ACTIVE_LOW), // green:usb + GPIO_LOOKUP_IDX(GPIO_IT87, 55, NULL, 8, GPIO_ACTIVE_HIGH), // blue:lan + GPIO_LOOKUP_IDX(GPIO_IT87, 12, NULL, 9, GPIO_ACTIVE_HIGH), // sata1:green:disk + GPIO_LOOKUP_IDX(GPIO_IT87, 13, NULL, 10, GPIO_ACTIVE_LOW), // sata1:red:disk + GPIO_LOOKUP_IDX(GPIO_IT87, 46, NULL, 11, GPIO_ACTIVE_HIGH), // sata2:green:disk + GPIO_LOOKUP_IDX(GPIO_IT87, 47, NULL, 12, GPIO_ACTIVE_LOW), // sata2:red:disk + GPIO_LOOKUP_IDX(GPIO_IT87, 51, NULL, 13, GPIO_ACTIVE_HIGH), // sata3:green:disk + GPIO_LOOKUP_IDX(GPIO_IT87, 52, NULL, 14, GPIO_ACTIVE_LOW), // sata3:red:disk + GPIO_LOOKUP_IDX(GPIO_IT87, 63, NULL, 15, GPIO_ACTIVE_HIGH), // sata4:green:disk + GPIO_LOOKUP_IDX(GPIO_IT87, 48, NULL, 16, GPIO_ACTIVE_LOW), // sata4:red:disk + {} + }, +}; + +static struct gpiod_lookup_table asustor_as6706_gpio_leds_lookup = { .dev_id = "leds-gpio", .table = { GPIO_LOOKUP_IDX(GPIO_IT87, 29, NULL, 0, GPIO_ACTIVE_HIGH), // power:front_panel @@ -263,36 +307,100 @@ struct asustor_driver_data { struct gpiod_lookup_table *keys; }; -#define VALID_OVERRIDE_NAMES "AS6xx, AS61xx, AS66xx, AS67xx, FS67xx" +#define VALID_OVERRIDE_NAMES \ + "AS6xx, AS61xx, AS66xx, AS6702, AS6704, AS6706, FS6706, FS6712" // NOTE: if you add another device here, update VALID_OVERRIDE_NAMES accordingly! -static struct asustor_driver_data asustor_fs6700_driver_data = { - .name = "FS67xx", - // FS67xx needs to match PCI devices because it has the same DMI data as *A*S67xx +/* + * Unfortunately, AS67xx and FS67xx can't be told apart by DMI, they all identify as + * "Intel Corporation" - "Jasper Lake Client Platform", so we need to match PCI devices. + * + * How to tell AS67xx and FS6xx apart: + * + * only AS6702T/AS5402T has [8086:4dd3] Intel Corporation Jasper Lake SATA AHCI Controller + * (only [AF]S67xx: [8086:4dc8] Intel Corporation Jasper Lake HD Audio + * - but for now I think AS670xT vs AS540xT doesn't matter. Not sure if AS5404T has this; AS5402T doesn't) + * + * only AS6704T has [1b21:1164] ASMedia Technology Inc. ASM1164 Serial ATA AHCI Controller + * - TODO: does AS5404T also use this? until disproven, I assume it does + * only AS6706T has [1b21:1166] ASMedia Technology Inc. ASM1166 Serial ATA Controller + * + * only FS6712X has [1b21:2806] ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch + * (it doesn't have any SATA controller) + * FS6706T does not have any SATA controller and no ASMedia PCIe packet switch either + */ + +static struct asustor_driver_data asustor_as6702_driver_data = { + .name = "AS6702", + .pci_matches = { + // SATA controller [0106]: Intel Corporation Jasper Lake SATA AHCI Controller [8086:4dd3] (rev 01) + // Both AS6702T and AS5402T use this SATA controller (the other devices don't) + { 0x8086, 0x4dd3, 1, 1 } + }, + .leds = &asustor_as6702_gpio_leds_lookup, + .keys = &asustor_6100_gpio_keys_lookup, +}; + +static struct asustor_driver_data asustor_as6704_driver_data = { + .name = "AS6704", + .pci_matches = { + // SATA controller: ASMedia Technology Inc. ASM1164 Serial ATA AHCI Controller [1b21:1164] (rev 02) + // This SATA controller is used by AS6704T, and hopefully by AS5404T as well, but + // not by any of the other AS67xx or FS67xx devices + { 0x1b21, 0x1164, 1, 1 } + }, + .leds = &asustor_as6704_gpio_leds_lookup, + .keys = &asustor_6100_gpio_keys_lookup, +}; + +static struct asustor_driver_data asustor_as6706_driver_data = { + .name = "AS6706", + .pci_matches = { + // SATA controller [0106]: ASMedia Technology Inc. ASM1166 Serial ATA Controller [1b21:1166] (rev 02) + // only used by AS6706T; there (currently?) is no AS5406T + { 0x1b21, 0x1166, 1, 1 } + // (BTW, AS6706T also has 5x "ASM2812 6-Port PCIe x4 Gen3 Packet Switch" [1b21:2812], + // which thankfully is NOT the same one that FS6712 uses. Also it allows replacing the + // m.2 NVME slots with a 10Gbit NIC, could be that then the packet switch goes away, IDK) + }, + .leds = &asustor_as6706_gpio_leds_lookup, + .keys = &asustor_6100_gpio_keys_lookup, +}; + +static struct asustor_driver_data asustor_fs6712_driver_data = { + .name = "FS6712", .pci_matches = { // PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01) - // FS6712X seems to have 15 of these, no idea about FS6706T (so I keep min_count at 1) - // currently the upper limit doesn't matter so I just use DEVICE_COUNT_MAX + // apparently only FS6712X uses this - 15 of those turn up in lspci, at least if + // all m.2 NVME slots have a SSD installed. I guess it's safest to match that at least + // one of these exist; upper limit doesn't matter, so just use DEVICE_COUNT_MAX { 0x1b21, 0x2806, 1, DEVICE_COUNT_MAX }, }, .leds = &asustor_fs6700_gpio_leds_lookup, .keys = &asustor_fs6700_gpio_keys_lookup, }; -static struct asustor_driver_data asustor_6700_driver_data = { - .name = "AS67xx", - // AS67xx needs to match PCI devices because it has the same DMI data as *F*S67xx +static struct asustor_driver_data asustor_fs6706_driver_data = { + .name = "FS6706", .pci_matches = { - // PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01) - // *F*S67xx seems to use these, *A*S67xx doesn't, so expect 0 - // (BTW, AS6706T has 5x "ASM2812 6-Port PCIe x4 Gen3 Packet Switch" [1b21:2812], I hope *F*S6706T doesn't) + // FS6706T doesn't have that ASMedia PCI bridge / PCIe Packet switch { 0x1b21, 0x2806, 0, 0 }, + // .. it doesn't have any of the SATA controllers either + { 0x8086, 0x4dd3, 0, 0 }, // .. not the Intel one used by AS6702T/AS5402T + { 0x1b21, 0x1164, 0, 0 }, // .. neither the ASMedia one used by AS6704T + { 0x1b21, 0x1166, 0, 0 }, // .. nor the ASMedia one used by AS6706T }, - .leds = &asustor_6700_gpio_leds_lookup, - .keys = &asustor_6100_gpio_keys_lookup, + .leds = &asustor_fs6700_gpio_leds_lookup, + .keys = &asustor_fs6700_gpio_keys_lookup, }; +/* + * It currently looks like the older systems are easier to tell apart, at least if one doesn't insist + * on detecting the 2 vs 4 vs 6 drives versions (I only did this for AS67xx because I had to do the + * advanced detection anyway) + */ + static struct asustor_driver_data asustor_6600_driver_data = { // NOTE: This is (currently?) the same as for AS6700 // because it seems to use the same GPIO numbers, @@ -300,7 +408,10 @@ static struct asustor_driver_data asustor_6600_driver_data = { .name = "AS66xx", // This (and the remaining systems) don't need to match PCI devices to be detected, // so they're not set here (and thus initialized to all-zero) - .leds = &asustor_6700_gpio_leds_lookup, + + // the LED GPIOs are the same as in AS67xx, so use the one from AS6704 which should work for + // both AS6602T and AS6604T (an AS66xx with more than 4 drives doesn't seem to exist) + .leds = &asustor_as6704_gpio_leds_lookup, .keys = &asustor_6100_gpio_keys_lookup, }; @@ -316,34 +427,61 @@ static struct asustor_driver_data asustor_600_driver_data = { .keys = &asustor_600_gpio_keys_lookup, }; -// NOTE: Don't use this table with dmi_first_match(), because it has two entries that -// match the same (AS67xx and FS67xx). find_matching_asustor_system() handles +// NOTE: Don't use this table with dmi_first_match(), because it has several entries that +// match the same (for AS67xx and FS67xx). find_matching_asustor_system() handles // that by also matching PCI devices from asustor_driver_data::pci_matches. // This table only exists in this form (instead of just using an array of // asustor_driver_data) for MODULE_DEVICE_TABLE(). static const struct dmi_system_id asustor_systems[] = { // NOTE: each entry in this table must have its own unique asustor_driver_data // (having a unique .name) set as .driver_data + + // The following devices all use the same DMI matches and are actually told apart by + // our custom matching logic in find_matching_asustor_system() also takes + // driver_data->pci_matches[] into account. + // See also the bigger comment above about AS67xx vs FS67xx + { + // NOTE: This not only matches (and works with) AS6702T (Lockerstor Gen2), + // but also AS5402T (Nimbustor Gen2) + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"), + }, + .driver_data = &asustor_as6702_driver_data, + }, + { + // NOTE: This not only matches (and works with) AS6704T (Lockerstor Gen2), + // but (hopefully!) also AS5404T (Nimbustor Gen2) + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"), + }, + .driver_data = &asustor_as6704_driver_data, + }, { - // NOTE: yes, this is the same DMI match as the next entry, because just by DMI, - // FS67xx and AS67xx can't be told apart. But our custom matching logic - // in find_matching_asustor_system() also takes driver_data->pci_matches[] - // into account, so that should be ok. .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"), }, - .driver_data = &asustor_fs6700_driver_data, + .driver_data = &asustor_as6706_driver_data, }, + // *F*S67xx: { - // NOTE: This not only matches (and works with) AS670xT (Lockerstor Gen2), - // but also AS540xT (Nimbustor Gen2) .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"), }, - .driver_data = &asustor_6700_driver_data, + .driver_data = &asustor_fs6706_driver_data, }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"), + }, + .driver_data = &asustor_fs6712_driver_data, + }, + + // older devices can be matched only by DMI { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), From 86a3478452913a73789c83cdf845fc646df7e291 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sat, 14 Dec 2024 22:51:32 +0100 Subject: [PATCH 12/12] Support controlling red LED strips on side of FS67xx devices It's those red strips on the left and right side of the power button. They're controlled by three GPIOs that are exposed as the new LEDs red:side_inner, red:side_mid and red:side_outer One of them was previously controlled by power:front_panel, but I changed it so it's more consistent with the other two LEDs (LED pairs?) of the strips Thanks to @brunomatic for figuring out the GPIOs of these LEDs! --- README.md | 2 +- asustor_main.c | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a03e0f9..e46c2f1 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ On many systems, ASUSTOR uses a mix of IT87 and CPU GPIOs to control leds and bu - AS6602T, AS6604T (NOT TESTED!) - AS6702T, AS6704T, AS6706T - AS5402T, AS5404T (NOT TESTED!) -- FS6706T (NOT TESTED!), FS6712X +- FS6706T, FS6712X - .. possibly more, if they're similar enough. The following DMI system-manufacturer / system-product-name combinations are currently supported diff --git a/asustor_main.c b/asustor_main.c index e9d98b9..0d82990 100644 --- a/asustor_main.c +++ b/asustor_main.c @@ -83,6 +83,9 @@ static struct gpio_led asustor_leds[] = { DISK_ERR_LED("sata6"), // 20 NVME_ACT_LED("nvme1"), // 21 NVME_ERR_LED("nvme1"), // 22 + { .name = "red:side_inner", .default_state = LEDS_GPIO_DEFSTATE_ON }, // 23 + { .name = "red:side_mid", .default_state = LEDS_GPIO_DEFSTATE_ON }, // 24 + { .name = "red:side_outer", .default_state = LEDS_GPIO_DEFSTATE_ON }, // 25 }; static const struct gpio_led_platform_data asustor_leds_pdata = { @@ -93,10 +96,8 @@ static const struct gpio_led_platform_data asustor_leds_pdata = { static struct gpiod_lookup_table asustor_fs6700_gpio_leds_lookup = { .dev_id = "leds-gpio", .table = { - // Red LEDs to the left and right of the - // power button on the side of the unit - GPIO_LOOKUP_IDX(GPIO_IT87, 52, NULL, 0, GPIO_ACTIVE_LOW), // power:front_panel - // 1 + // 0 - no front panel on this device + // 1 - no LCD either // blue:power also controls the red LED // inside the power button on the side GPIO_LOOKUP_IDX(GPIO_IT87, 56, NULL, 2, GPIO_ACTIVE_LOW), // blue:power @@ -109,6 +110,10 @@ static struct gpiod_lookup_table asustor_fs6700_gpio_leds_lookup = { // LEDs 9 - 20 don't exist in this system GPIO_LOOKUP_IDX(GPIO_IT87, 12, NULL, 21, GPIO_ACTIVE_LOW), // nvme1:green:disk GPIO_LOOKUP_IDX(GPIO_IT87, 13, NULL, 22, GPIO_ACTIVE_LOW), // nvme1:red:disk + // red LED strip next to the power button on the side + GPIO_LOOKUP_IDX(GPIO_IT87, 46, NULL, 23, GPIO_ACTIVE_LOW), // red:side_inner + GPIO_LOOKUP_IDX(GPIO_IT87, 47, NULL, 24, GPIO_ACTIVE_LOW), // red:side_mid + GPIO_LOOKUP_IDX(GPIO_IT87, 52, NULL, 25, GPIO_ACTIVE_LOW), // red:side_outer {} }, };