Skip to content

Commit cc12e21

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 cc12e21

File tree

2 files changed

+172
-45
lines changed

2 files changed

+172
-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: 171 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -224,23 +224,49 @@ 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+
// FS67xx needs to match PCI devices because it has the same DMI data as *A*S67xx
250+
.pci_matches = {
251+
// PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01)
252+
// FS6712X seems to have 15 of these, no idea about FS6706T (so I keep min_count at 1)
253+
// currently the upper limit doesn't matter so I just use 10000
254+
{ 0x1b21, 0x2806, 1, 10000 },
255+
{}
256+
},
238257
.leds = &asustor_fs6700_gpio_leds_lookup,
239258
.keys = &asustor_fs6700_gpio_keys_lookup,
240259
};
241260

242261
static struct asustor_driver_data asustor_6700_driver_data = {
243262
.name = "AS67xx",
263+
// AS67xx needs to match PCI devices because it has the same DMI data as *F*S67xx
264+
.pci_matches = {
265+
// PCI bridge: ASMedia Technology Inc. ASM2806 4-Port PCIe x2 Gen3 Packet Switch (rev 01)
266+
// *F*S67xx seems to use these, I think *A*S67xx doesn't, so expect 0
267+
{ 0x1b21, 0x2806, 0, 0 },
268+
{}
269+
},
244270
.leds = &asustor_6700_gpio_leds_lookup,
245271
.keys = &asustor_6100_gpio_keys_lookup,
246272
};
@@ -250,6 +276,8 @@ static struct asustor_driver_data asustor_6600_driver_data = {
250276
// because it seems to use the same GPIO numbers,
251277
// but listed extra for the different name
252278
.name = "AS66xx",
279+
// This (and the remaining systems) don't need to match PCI devices to be detected,
280+
// so they're not set here (and thus initialized to all-zero)
253281
.leds = &asustor_6700_gpio_leds_lookup,
254282
.keys = &asustor_6100_gpio_keys_lookup,
255283
};
@@ -268,13 +296,26 @@ static struct asustor_driver_data asustor_600_driver_data = {
268296

269297
#define VALID_OVERRIDE_NAMES "AS6xx, AS61xx, AS66xx, AS67xx, FS67xx"
270298

299+
// NOTE: Don't use this table with dmi_first_match(), because it has two entries that
300+
// match the same (AS67xx and FS67xx). find_matching_asustor_system() handles
301+
// that by also matching PCI devices from driver_data
302+
// This table only exists in this form (instead of just using an array of
303+
// asustor_driver_data) for MODULE_DEVICE_TABLE().
271304
static const struct dmi_system_id asustor_systems[] = {
305+
{
306+
// Note: yes, this is the same DMI match as the next entry, because just by DMI,
307+
// FS67xx and AS67xx can't be told apart. But our custom matching logic
308+
// in find_matching_asustor_system() also takes driver_data->pci_matches[]
309+
// into account, so that should be ok.
310+
.matches = {
311+
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
312+
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"),
313+
},
314+
.driver_data = &asustor_fs6700_driver_data,
315+
},
272316
{
273317
// 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
318+
// but also AS540xT (Nimbustor Gen2)
278319
.matches = {
279320
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
280321
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jasper Lake Client Platform"),
@@ -348,6 +389,115 @@ static struct gpio_chip *find_chip_by_name(const char *name)
348389
}
349390
#endif
350391

392+
/**
393+
* dmi_matches - check if dmi_system_id structure matches system DMI data
394+
* @dmi: pointer to the dmi_system_id structure to check
395+
*/
396+
// copied from dmi_matches() in Linux 6.11.2 drivers/firmware/dmi_scan.c where it's private (static)
397+
// with only one small change (dmi_val = dmi_get_system_info(s) instead of dmi_ident[s])
398+
static bool dmi_matches(const struct dmi_system_id *dmi)
399+
{
400+
int i;
401+
const char *dmi_val;
402+
403+
for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) {
404+
int s = dmi->matches[i].slot;
405+
if (s == DMI_NONE)
406+
break;
407+
if (s == DMI_OEM_STRING) {
408+
/* DMI_OEM_STRING must be exact match */
409+
const struct dmi_device *valid;
410+
411+
valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
412+
dmi->matches[i].substr, NULL);
413+
if (valid)
414+
continue;
415+
} else if ((dmi_val = dmi_get_system_info(s)) != NULL) {
416+
if (dmi->matches[i].exact_match) {
417+
if (!strcmp(dmi_val, dmi->matches[i].substr))
418+
continue;
419+
} else {
420+
if (strstr(dmi_val, dmi->matches[i].substr))
421+
continue;
422+
}
423+
}
424+
425+
/* No match */
426+
return false;
427+
}
428+
return true;
429+
}
430+
431+
// how often does the PCI(e) device with given vendor/device IDs exist in this system?
432+
static int count_pci_device_instances(unsigned int vendor, unsigned int device)
433+
{
434+
// start with -1 as the do-while loop runs at least once even if nothing is found
435+
int ret = -1;
436+
struct pci_dev *pd = NULL;
437+
do {
438+
++ret;
439+
pd = pci_get_device(vendor, device, pd);
440+
} while (pd != NULL);
441+
return ret;
442+
}
443+
444+
// check if sys->pci_matches[] match with the PCI devices in the system
445+
// NOTE: this only checks if the device in sys->pci_matches[] exist in the system
446+
// with the expected count (or do *not* exist if the counts are 0),
447+
// PCI devices existing that aren't listed in sys->pci_matches[] is expected
448+
// and does not make this function fail.
449+
static bool pci_devices_match(const struct asustor_driver_data *sys)
450+
{
451+
int i;
452+
for (i = 0; i < ARRAY_SIZE(sys->pci_matches); ++i) {
453+
int dev_cnt;
454+
const struct pci_device_match *pdm;
455+
pdm = &sys->pci_matches[i];
456+
if (pdm->vendorID == 0 && pdm->deviceID == 0) {
457+
// no more entries, the previous ones matched
458+
// or we would've returned false already
459+
return true;
460+
}
461+
dev_cnt = count_pci_device_instances(pdm->vendorID,
462+
pdm->deviceID);
463+
if (dev_cnt < pdm->min_count || dev_cnt > pdm->max_count) {
464+
return false;
465+
}
466+
}
467+
return true;
468+
}
469+
470+
// find out which ASUSTOR system this is, based on asustor_systems
471+
// returns NULL if this isn't a known system
472+
static const struct dmi_system_id *find_matching_asustor_system(void)
473+
{
474+
int as_idx;
475+
476+
for (as_idx = 0; as_idx < ARRAY_SIZE(asustor_systems); as_idx++) {
477+
struct asustor_driver_data *dd;
478+
const struct dmi_system_id *sys;
479+
sys = &asustor_systems[as_idx];
480+
dd = sys->driver_data;
481+
if (dd == NULL) {
482+
// no driverdata? must be at end of table
483+
break;
484+
}
485+
486+
if (!dmi_matches(sys)) {
487+
continue;
488+
}
489+
490+
if (!pci_devices_match(dd)) {
491+
continue;
492+
}
493+
494+
// DMI and PCI devices matched => this is it
495+
return sys;
496+
}
497+
498+
return NULL;
499+
}
500+
351501
static char *force_device = "";
352502
module_param(force_device, charp, S_IRUSR | S_IRGRP | S_IROTH);
353503
MODULE_PARM_DESC(
@@ -365,58 +515,34 @@ static int __init asustor_init(void)
365515
driver_data = NULL;
366516
// allow overriding detection with force_device kernel parameter
367517
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;
518+
for (i = 0; i < ARRAY_SIZE(asustor_systems); i++) {
519+
struct asustor_driver_data *dd =
520+
asustor_systems[i].driver_data;
521+
if (dd && dd->name &&
522+
strcmp(force_device, dd->name) == 0) {
523+
driver_data = dd;
524+
break;
388525
}
389526
}
527+
if (driver_data == NULL) {
528+
pr_err("force_device parameter set to invalid value \"%s\"!\n",
529+
force_device);
530+
pr_info(" valid force_device values are: %s\n",
531+
VALID_OVERRIDE_NAMES);
532+
return -EINVAL;
533+
}
390534
pr_info("force_device parameter is set to \"%s\", treating your machine as "
391535
"that device instead of trying to detect it!\n",
392536
force_device);
393-
} else { // try to detect the ASUSTOR device
394-
system = dmi_first_match(asustor_systems);
537+
} else { // try to detect the ASUSTOR system
538+
system = find_matching_asustor_system();
395539
if (!system) {
396540
pr_info("No supported ASUSTOR mainboard found");
397541
return -ENODEV;
398542
}
399543

400544
driver_data = system->driver_data;
401545

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-
420546
pr_info("Found %s or similar (%s/%s)\n", driver_data->name,
421547
system->matches[0].substr, system->matches[1].substr);
422548
}

0 commit comments

Comments
 (0)