Skip to content

Commit ac1deb9

Browse files
committed
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
1 parent 09d4a19 commit ac1deb9

File tree

2 files changed

+183
-51
lines changed

2 files changed

+183
-51
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ sudo dmidecode -s bios-version
198198
sudo dmidecode -s bios-release-date
199199
sudo dmidecode -s bios-revision
200200
sudo gpioinfo
201+
sudo lspci -nn -PP
201202
```
202203

203204
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.

asustor.c

Lines changed: 182 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ static struct gpio_led asustor_leds[] = {
5757
{ .name = "red:power", .default_state = LEDS_GPIO_DEFSTATE_OFF }, // 3
5858
{ .name = "green:status", .default_state = LEDS_GPIO_DEFSTATE_ON }, // 4
5959
{
60-
.name = "red:status",
60+
.name = "red:status", // 5
6161
.default_state = LEDS_GPIO_DEFSTATE_OFF,
6262
.panic_indicator = 1,
6363
.default_trigger = "panic",
64-
}, // 5
64+
},
6565
{ .name = "blue:usb", .default_state = LEDS_GPIO_DEFSTATE_OFF }, // 6
6666
{ .name = "green:usb", .default_state = LEDS_GPIO_DEFSTATE_OFF }, // 7
6767
{ .name = "blue:lan", .default_state = LEDS_GPIO_DEFSTATE_ON }, // 8
@@ -103,7 +103,7 @@ static struct gpiod_lookup_table asustor_fs6700_gpio_leds_lookup = {
103103
// 6
104104
// 7
105105
GPIO_LOOKUP_IDX(GPIO_IT87, 55, NULL, 8, GPIO_ACTIVE_HIGH), // blue:lan
106-
// 9 - 20 don't exist
106+
// LEDs 9 - 20 don't exist in this system
107107
GPIO_LOOKUP_IDX(GPIO_IT87, 12, NULL, 21, GPIO_ACTIVE_LOW), // nvme1:green:disk
108108
GPIO_LOOKUP_IDX(GPIO_IT87, 13, NULL, 22, GPIO_ACTIVE_LOW), // nvme1:red:disk
109109
{}
@@ -234,23 +234,53 @@ static struct gpiod_lookup_table asustor_600_gpio_keys_lookup = {
234234
};
235235
// clang-format on
236236

237+
struct pci_device_match {
238+
// match PCI devices with the given vendorID and productID (to help identify ASUSTOR systems)
239+
// you can get them from `lspci -nn`, for example in
240+
// "00:08.0 System peripheral [0880]: Intel Corporation Device [8086:4e11]"
241+
// 0x8086 is the vendorID and 0x4e11 is the deviceID
242+
uint16_t vendorID;
243+
uint16_t deviceID;
244+
245+
int16_t min_count; // how often that device should exist at least
246+
int16_t max_count; // how often that device should exist at most
247+
};
248+
237249
// ASUSTOR Platform.
238250
struct asustor_driver_data {
239-
const char *name;
251+
const char *name; // used for force_device and for some log messages
252+
253+
struct pci_device_match pci_matches[4];
254+
240255
struct gpiod_lookup_table *leds;
241256
struct gpiod_lookup_table *keys;
242257
};
243258

259+
#define VALID_OVERRIDE_NAMES "AS6xx, AS61xx, AS66xx, AS67xx, FS67xx"
260+
244261
// NOTE: if you add another device here, update VALID_OVERRIDE_NAMES accordingly!
245262

246263
static struct asustor_driver_data asustor_fs6700_driver_data = {
247264
.name = "FS67xx",
265+
// FS67xx needs to match PCI devices because it has the same DMI data as *A*S67xx
266+
.pci_matches = {
267+
// PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01)
268+
// FS6712X seems to have 15 of these, no idea about FS6706T (so I keep min_count at 1)
269+
// currently the upper limit doesn't matter so I just use 10000
270+
{ 0x1b21, 0x2806, 1, 10000 },
271+
},
248272
.leds = &asustor_fs6700_gpio_leds_lookup,
249273
.keys = &asustor_fs6700_gpio_keys_lookup,
250274
};
251275

252276
static struct asustor_driver_data asustor_6700_driver_data = {
253277
.name = "AS67xx",
278+
// AS67xx needs to match PCI devices because it has the same DMI data as *F*S67xx
279+
.pci_matches = {
280+
// PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01)
281+
// *F*S67xx seems to use these, I think *A*S67xx doesn't, so expect 0
282+
{ 0x1b21, 0x2806, 0, 0 },
283+
},
254284
.leds = &asustor_6700_gpio_leds_lookup,
255285
.keys = &asustor_6100_gpio_keys_lookup,
256286
};
@@ -260,6 +290,8 @@ static struct asustor_driver_data asustor_6600_driver_data = {
260290
// because it seems to use the same GPIO numbers,
261291
// but listed extra for the different name
262292
.name = "AS66xx",
293+
// This (and the remaining systems) don't need to match PCI devices to be detected,
294+
// so they're not set here (and thus initialized to all-zero)
263295
.leds = &asustor_6700_gpio_leds_lookup,
264296
.keys = &asustor_6100_gpio_keys_lookup,
265297
};
@@ -276,15 +308,28 @@ static struct asustor_driver_data asustor_600_driver_data = {
276308
.keys = &asustor_600_gpio_keys_lookup,
277309
};
278310

279-
#define VALID_OVERRIDE_NAMES "AS6xx, AS61xx, AS66xx, AS67xx, FS67xx"
280-
311+
// NOTE: Don't use this table with dmi_first_match(), because it has two entries that
312+
// match the same (AS67xx and FS67xx). find_matching_asustor_system() handles
313+
// that by also matching PCI devices from asustor_driver_data::pci_matches.
314+
// This table only exists in this form (instead of just using an array of
315+
// asustor_driver_data) for MODULE_DEVICE_TABLE().
281316
static const struct dmi_system_id asustor_systems[] = {
317+
// NOTE: each entry in this table must have its own unique asustor_driver_data
318+
// (having a unique .name) set as .driver_data
282319
{
283-
// Note: This not only matches (and works with) AS670xT (Lockerstore Gen2),
284-
// but also AS540xT (Nimbustor Gen2) and, unfortunately, also
285-
// Flashstor (FS6706T and FS6712X) which can't be detected with DMI but is
286-
// different enough from the AS67xx devices to need different treatment.
287-
// asustor_init() has additional code to detect FS67xx based on available PCIe devices
320+
// Note: yes, this is the same DMI match as the next entry, because just by DMI,
321+
// FS67xx and AS67xx can't be told apart. But our custom matching logic
322+
// in find_matching_asustor_system() also takes driver_data->pci_matches[]
323+
// into account, so that should be ok.
324+
.matches = {
325+
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
326+
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"),
327+
},
328+
.driver_data = &asustor_fs6700_driver_data,
329+
},
330+
{
331+
// Note: This not only matches (and works with) AS670xT (Lockerstor Gen2),
332+
// but also AS540xT (Nimbustor Gen2)
288333
.matches = {
289334
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
290335
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"),
@@ -358,6 +403,116 @@ static struct gpio_chip *find_chip_by_name(const char *name)
358403
}
359404
#endif
360405

406+
/**
407+
* dmi_matches - check if dmi_system_id structure matches system DMI data
408+
* @dmi: pointer to the dmi_system_id structure to check
409+
*/
410+
// copied from dmi_matches() in Linux 6.11.2 drivers/firmware/dmi_scan.c where it's private (static)
411+
// with only one small change (dmi_val = dmi_get_system_info(s) instead of dmi_ident[s])
412+
static bool dmi_matches(const struct dmi_system_id *dmi)
413+
{
414+
int i;
415+
const char *dmi_val;
416+
417+
for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) {
418+
int s = dmi->matches[i].slot;
419+
if (s == DMI_NONE)
420+
break;
421+
if (s == DMI_OEM_STRING) {
422+
/* DMI_OEM_STRING must be exact match */
423+
const struct dmi_device *valid;
424+
425+
valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
426+
dmi->matches[i].substr, NULL);
427+
if (valid)
428+
continue;
429+
} else if ((dmi_val = dmi_get_system_info(s)) != NULL) {
430+
if (dmi->matches[i].exact_match) {
431+
if (!strcmp(dmi_val, dmi->matches[i].substr))
432+
continue;
433+
} else {
434+
if (strstr(dmi_val, dmi->matches[i].substr))
435+
continue;
436+
}
437+
}
438+
439+
/* No match */
440+
return false;
441+
}
442+
return true;
443+
}
444+
445+
// how often does the PCI(e) device with given vendor/device IDs exist in this system?
446+
static int count_pci_device_instances(unsigned int vendor, unsigned int device)
447+
{
448+
// start with -1 as the do-while loop runs at least once even if nothing is found
449+
int ret = -1;
450+
struct pci_dev *pd = NULL;
451+
do {
452+
++ret;
453+
pd = pci_get_device(vendor, device, pd);
454+
} while (pd != NULL);
455+
return ret;
456+
}
457+
458+
// check if sys->pci_matches[] match with the PCI devices in the system
459+
// NOTE: this only checks if the devices in sys->pci_matches[] exist in the system
460+
// with the expected count (or do *not* exist if the counts are 0),
461+
// PCI devices existing that aren't listed in sys->pci_matches[] is expected
462+
// and does not make this function fail.
463+
static bool pci_devices_match(const struct asustor_driver_data *sys)
464+
{
465+
int i;
466+
for (i = 0; i < ARRAY_SIZE(sys->pci_matches); ++i) {
467+
int dev_cnt;
468+
const struct pci_device_match *pdm;
469+
pdm = &sys->pci_matches[i];
470+
if (pdm->vendorID == 0 && pdm->deviceID == 0) {
471+
// no more entries, the previous ones matched
472+
// or we would've returned false already
473+
return true;
474+
}
475+
dev_cnt = count_pci_device_instances(pdm->vendorID,
476+
pdm->deviceID);
477+
if (dev_cnt < pdm->min_count || dev_cnt > pdm->max_count) {
478+
return false;
479+
}
480+
}
481+
return true;
482+
}
483+
484+
// find out which ASUSTOR system this is, based on asustor_systems[], including
485+
// their linked asustor_driver_data's pci_matches
486+
// returns NULL if this isn't a known system
487+
static const struct dmi_system_id *find_matching_asustor_system(void)
488+
{
489+
int as_idx;
490+
491+
for (as_idx = 0; as_idx < ARRAY_SIZE(asustor_systems); as_idx++) {
492+
struct asustor_driver_data *dd;
493+
const struct dmi_system_id *sys;
494+
sys = &asustor_systems[as_idx];
495+
dd = sys->driver_data;
496+
if (dd == NULL) {
497+
// no driverdata? must be at end of table
498+
break;
499+
}
500+
501+
if (!dmi_matches(sys)) {
502+
continue;
503+
}
504+
505+
if (!pci_devices_match(dd)) {
506+
continue;
507+
}
508+
509+
// DMI and PCI devices matched => this is it
510+
return sys;
511+
}
512+
513+
return NULL;
514+
}
515+
361516
static char *force_device = "";
362517
module_param(force_device, charp, S_IRUSR | S_IRGRP | S_IROTH);
363518
MODULE_PARM_DESC(
@@ -375,58 +530,34 @@ static int __init asustor_init(void)
375530
driver_data = NULL;
376531
// allow overriding detection with force_device kernel parameter
377532
if (force_device && *force_device) {
378-
// special case: FS67xx isn't in the asustor_systems table, as it can't
379-
// be detected through DMI
380-
if (strcmp(force_device, "FS67xx") == 0) {
381-
driver_data = &asustor_fs6700_driver_data;
382-
} else {
383-
for (i = 0; i < ARRAY_SIZE(asustor_systems); i++) {
384-
struct asustor_driver_data *dd =
385-
asustor_systems[i].driver_data;
386-
if (dd && dd->name &&
387-
strcmp(force_device, dd->name) == 0) {
388-
driver_data = dd;
389-
break;
390-
}
391-
}
392-
if (driver_data == NULL) {
393-
pr_err("force_device parameter set to invalid value \"%s\"!\n",
394-
force_device);
395-
pr_info(" valid force_device values are: %s\n",
396-
VALID_OVERRIDE_NAMES);
397-
return -EINVAL;
533+
for (i = 0; i < ARRAY_SIZE(asustor_systems); i++) {
534+
struct asustor_driver_data *dd =
535+
asustor_systems[i].driver_data;
536+
if (dd && dd->name &&
537+
strcmp(force_device, dd->name) == 0) {
538+
driver_data = dd;
539+
break;
398540
}
399541
}
542+
if (driver_data == NULL) {
543+
pr_err("force_device parameter set to invalid value \"%s\"!\n",
544+
force_device);
545+
pr_info(" valid force_device values are: %s\n",
546+
VALID_OVERRIDE_NAMES);
547+
return -EINVAL;
548+
}
400549
pr_info("force_device parameter is set to \"%s\", treating your machine as "
401550
"that device instead of trying to detect it!\n",
402551
force_device);
403-
} else { // try to detect the ASUSTOR device
404-
system = dmi_first_match(asustor_systems);
552+
} else { // try to detect the ASUSTOR system
553+
system = find_matching_asustor_system();
405554
if (!system) {
406555
pr_info("No supported ASUSTOR mainboard found");
407556
return -ENODEV;
408557
}
409558

410559
driver_data = system->driver_data;
411560

412-
// figure out if this is really an AS67xx or instead a FS67xx ("Flashstor"),
413-
// which has different LEDs and only supports m.2 SSDs (no SATA drives)
414-
if (driver_data == &asustor_6700_driver_data) {
415-
// this matches the "ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3
416-
// Packet Switch" (rev 01) PCI bridge (vendor ID 0x1b21, device ID 0x2806
417-
// aka 1b21:2806), which AFAIK is only used in Flashtor devices
418-
// (though unfortunately we only had FS6712X and AS5402T to check)
419-
// see also https://github.com/mafredri/asustor-platform-driver/pull/21#issuecomment-2420883171
420-
// and following.
421-
if (pci_get_device(0x1b21, 0x2806, NULL) != NULL) {
422-
// TODO: if necessary, we could count those devices; the current
423-
// assumption is that even the bigger *A*S67xx (or AS54xx)
424-
// devices don't have this at all
425-
426-
driver_data = &asustor_fs6700_driver_data;
427-
}
428-
}
429-
430561
pr_info("Found %s or similar (%s/%s)\n", driver_data->name,
431562
system->matches[0].substr, system->matches[1].substr);
432563
}

0 commit comments

Comments
 (0)