Skip to content

Commit 463649a

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 4b0d9a9 commit 463649a

File tree

2 files changed

+169
-45
lines changed

2 files changed

+169
-45
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: 168 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -224,23 +224,47 @@ static struct gpiod_lookup_table asustor_600_gpio_keys_lookup = {
224224
};
225225
// clang-format on
226226

227+
struct pci_device_match {
228+
// match PCI devices with the given vendorID and productID (to help identify ASUSTOR systems)
229+
uint16_t vendorID;
230+
uint16_t deviceID;
231+
232+
int16_t min_count; // how often that device should exist at least
233+
int16_t max_count; // how often that device should exist at most
234+
};
235+
227236
// ASUSTOR Platform.
228237
struct asustor_driver_data {
229238
const char *name;
239+
240+
struct pci_device_match pci_matches[4];
241+
230242
struct gpiod_lookup_table *leds;
231243
struct gpiod_lookup_table *keys;
232244
};
233245

234246
// NOTE: if you add another device here, update VALID_OVERRIDE_NAMES accordingly!
235-
236247
static struct asustor_driver_data asustor_fs6700_driver_data = {
237248
.name = "FS67xx",
249+
.pci_matches = {
250+
// PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01)
251+
// FS6712X seems to have 15 of these, no idea about FS6706T (so I keep min_count at 1)
252+
// currently the upper limit doesn't matter so I just use 10000
253+
{ 0x1b21, 0x2806, 1, 10000 },
254+
{}
255+
},
238256
.leds = &asustor_fs6700_gpio_leds_lookup,
239257
.keys = &asustor_fs6700_gpio_keys_lookup,
240258
};
241259

242260
static struct asustor_driver_data asustor_6700_driver_data = {
243261
.name = "AS67xx",
262+
.pci_matches = {
263+
// PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01)
264+
// *F*S67xx seems to use these, I think *A*S67xx doesn't, so expect 0
265+
{ 0x1b21, 0x2806, 0, 0 },
266+
{}
267+
},
244268
.leds = &asustor_6700_gpio_leds_lookup,
245269
.keys = &asustor_6100_gpio_keys_lookup,
246270
};
@@ -250,6 +274,8 @@ static struct asustor_driver_data asustor_6600_driver_data = {
250274
// because it seems to use the same GPIO numbers,
251275
// but listed extra for the different name
252276
.name = "AS66xx",
277+
// This (and the remaining systems) don't need to match PCI devices to be detected,
278+
// so they're not set here (and thus initialized to all-zero)
253279
.leds = &asustor_6700_gpio_leds_lookup,
254280
.keys = &asustor_6100_gpio_keys_lookup,
255281
};
@@ -268,13 +294,26 @@ static struct asustor_driver_data asustor_600_driver_data = {
268294

269295
#define VALID_OVERRIDE_NAMES "AS6xx, AS61xx, AS66xx, AS67xx, FS67xx"
270296

297+
// NOTE: Don't use this table with dmi_first_match(), because it has two entries that
298+
// match the same (AS67xx and FS67xx). find_matching_asustor_system() handles
299+
// that by also matching PCI devices from driver_data
300+
// This table only exists in this form (instead of just using an array of
301+
// asustor_driver_data) for MODULE_DEVICE_TABLE().
271302
static const struct dmi_system_id asustor_systems[] = {
303+
{
304+
// Note: yes, this is the same DMI match as the next entry, because just by DMI,
305+
// FS67xx and AS67xx can't be told apart. But our custom matching logic
306+
// in find_matching_asustor_system() also takes driver_data->pci_matches[]
307+
// into account, so that should be ok.
308+
.matches = {
309+
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
310+
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"),
311+
},
312+
.driver_data = &asustor_fs6700_driver_data,
313+
},
272314
{
273315
// Note: This not only matches (and works with) AS670xT (Lockerstore Gen2),
274-
// but also AS540xT (Nimbustor Gen2) and, unfortunately, also
275-
// Flashstor (FS6706T and FS6712X) which can't be detected with DMI but is
276-
// different enough from the AS67xx devices to need different treatment.
277-
// asustor_init() has additional code to detect FS67xx based on available PCIe devices
316+
// but also AS540xT (Nimbustor Gen2)
278317
.matches = {
279318
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
280319
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"),
@@ -348,6 +387,114 @@ static struct gpio_chip *find_chip_by_name(const char *name)
348387
}
349388
#endif
350389

390+
/**
391+
* dmi_matches - check if dmi_system_id structure matches system DMI data
392+
* @dmi: pointer to the dmi_system_id structure to check
393+
*/
394+
// heavily based on dmi_matches() from Linux 6.11.2 drivers/firmware/dmi_scan.c where it's private (static)
395+
static bool dmi_matches(const struct dmi_system_id *dmi)
396+
{
397+
int i;
398+
const char *dmi_val;
399+
400+
for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) {
401+
int s = dmi->matches[i].slot;
402+
if (s == DMI_NONE)
403+
break;
404+
if (s == DMI_OEM_STRING) {
405+
/* DMI_OEM_STRING must be exact match */
406+
const struct dmi_device *valid;
407+
408+
valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
409+
dmi->matches[i].substr, NULL);
410+
if (valid)
411+
continue;
412+
} else if ((dmi_val = dmi_get_system_info(s)) != NULL) {
413+
if (dmi->matches[i].exact_match) {
414+
if (!strcmp(dmi_val, dmi->matches[i].substr))
415+
continue;
416+
} else {
417+
if (strstr(dmi_val, dmi->matches[i].substr))
418+
continue;
419+
}
420+
}
421+
422+
/* No match */
423+
return false;
424+
}
425+
return true;
426+
}
427+
428+
// how often does the PCI device with given vendor/device IDs exist in this system?
429+
static int count_pci_device_instances(unsigned int vendor, unsigned int device)
430+
{
431+
// start with -1 as the do-while loop runs at least once even if nothing is found
432+
int ret = -1;
433+
struct pci_dev *pd = NULL;
434+
do {
435+
++ret;
436+
pd = pci_get_device(vendor, device, pd);
437+
} while (pd != NULL);
438+
return ret;
439+
}
440+
441+
// check if sys->pci_matches[] match with the PCI devices in the system
442+
// NOTE: this only checks if the device in sys->pci_matches[] exist in the system
443+
// with the expected count (or do *not* exist if the counts are 0),
444+
// PCI devices existing that aren't listed in sys->pci_matches[] is expected
445+
// and does not make this function fail.
446+
static bool pci_devices_match(const struct asustor_driver_data *sys)
447+
{
448+
int i;
449+
for (i = 0; i < ARRAY_SIZE(sys->pci_matches); ++i) {
450+
int dev_cnt;
451+
const struct pci_device_match *pdm;
452+
pdm = &sys->pci_matches[i];
453+
if (pdm->vendorID == 0 && pdm->deviceID == 0) {
454+
// no more entries, the previous ones matched
455+
// or we would've returned false already
456+
return true;
457+
}
458+
dev_cnt = count_pci_device_instances(pdm->vendorID,
459+
pdm->deviceID);
460+
if (dev_cnt < pdm->min_count || dev_cnt > pdm->max_count) {
461+
return false;
462+
}
463+
}
464+
return true;
465+
}
466+
467+
// find out which ASUSTOR system this is, based on asustor_systems
468+
// returns NULL if this isn't a known system
469+
static const struct dmi_system_id *find_matching_asustor_system(void)
470+
{
471+
int as_idx;
472+
473+
for (as_idx = 0; as_idx < ARRAY_SIZE(asustor_systems); as_idx++) {
474+
struct asustor_driver_data *dd;
475+
const struct dmi_system_id *sys;
476+
sys = &asustor_systems[as_idx];
477+
dd = sys->driver_data;
478+
if (dd == NULL) {
479+
// no driverdata? must be at end of table
480+
break;
481+
}
482+
483+
if (!dmi_matches(sys)) {
484+
continue;
485+
}
486+
487+
if (!pci_devices_match(dd)) {
488+
continue;
489+
}
490+
491+
// DMI and PCI devices matched => this is it
492+
return sys;
493+
}
494+
495+
return NULL;
496+
}
497+
351498
static char *force_device = "";
352499
module_param(force_device, charp, S_IRUSR | S_IRGRP | S_IROTH);
353500
MODULE_PARM_DESC(
@@ -365,58 +512,34 @@ static int __init asustor_init(void)
365512
driver_data = NULL;
366513
// allow overriding detection with force_device kernel parameter
367514
if (force_device && *force_device) {
368-
// special case: FS67xx isn't in the asustor_systems table, as it can't
369-
// be detected through DMI
370-
if (strcmp(force_device, "FS67xx") == 0) {
371-
driver_data = &asustor_fs6700_driver_data;
372-
} else {
373-
for (i = 0; i < ARRAY_SIZE(asustor_systems); i++) {
374-
struct asustor_driver_data *dd =
375-
asustor_systems[i].driver_data;
376-
if (dd && dd->name &&
377-
strcmp(force_device, dd->name) == 0) {
378-
driver_data = dd;
379-
break;
380-
}
381-
}
382-
if (driver_data == NULL) {
383-
pr_err("force_device parameter set to invalid value \"%s\"!\n",
384-
force_device);
385-
pr_info(" valid force_device values are: %s\n",
386-
VALID_OVERRIDE_NAMES);
387-
return -EINVAL;
515+
for (i = 0; i < ARRAY_SIZE(asustor_systems); i++) {
516+
struct asustor_driver_data *dd =
517+
asustor_systems[i].driver_data;
518+
if (dd && dd->name &&
519+
strcmp(force_device, dd->name) == 0) {
520+
driver_data = dd;
521+
break;
388522
}
389523
}
524+
if (driver_data == NULL) {
525+
pr_err("force_device parameter set to invalid value \"%s\"!\n",
526+
force_device);
527+
pr_info(" valid force_device values are: %s\n",
528+
VALID_OVERRIDE_NAMES);
529+
return -EINVAL;
530+
}
390531
pr_info("force_device parameter is set to \"%s\", treating your machine as "
391532
"that device instead of trying to detect it!\n",
392533
force_device);
393-
} else { // try to detect the ASUSTOR device
394-
system = dmi_first_match(asustor_systems);
534+
} else { // try to detect the ASUSTOR system
535+
system = find_matching_asustor_system();
395536
if (!system) {
396537
pr_info("No supported ASUSTOR mainboard found");
397538
return -ENODEV;
398539
}
399540

400541
driver_data = system->driver_data;
401542

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

0 commit comments

Comments
 (0)