@@ -224,23 +224,47 @@ static struct gpiod_lookup_table asustor_600_gpio_keys_lookup = {
224
224
};
225
225
// clang-format on
226
226
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
+
227
236
// ASUSTOR Platform.
228
237
struct asustor_driver_data {
229
238
const char * name ;
239
+
240
+ struct pci_device_match pci_matches [4 ];
241
+
230
242
struct gpiod_lookup_table * leds ;
231
243
struct gpiod_lookup_table * keys ;
232
244
};
233
245
234
246
// NOTE: if you add another device here, update VALID_OVERRIDE_NAMES accordingly!
235
-
236
247
static struct asustor_driver_data asustor_fs6700_driver_data = {
237
248
.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
+ },
238
256
.leds = & asustor_fs6700_gpio_leds_lookup ,
239
257
.keys = & asustor_fs6700_gpio_keys_lookup ,
240
258
};
241
259
242
260
static struct asustor_driver_data asustor_6700_driver_data = {
243
261
.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
+ },
244
268
.leds = & asustor_6700_gpio_leds_lookup ,
245
269
.keys = & asustor_6100_gpio_keys_lookup ,
246
270
};
@@ -250,6 +274,8 @@ static struct asustor_driver_data asustor_6600_driver_data = {
250
274
// because it seems to use the same GPIO numbers,
251
275
// but listed extra for the different name
252
276
.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)
253
279
.leds = & asustor_6700_gpio_leds_lookup ,
254
280
.keys = & asustor_6100_gpio_keys_lookup ,
255
281
};
@@ -268,13 +294,26 @@ static struct asustor_driver_data asustor_600_driver_data = {
268
294
269
295
#define VALID_OVERRIDE_NAMES "AS6xx, AS61xx, AS66xx, AS67xx, FS67xx"
270
296
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().
271
302
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
+ },
272
314
{
273
315
// 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)
278
317
.matches = {
279
318
DMI_EXACT_MATCH (DMI_SYS_VENDOR , "Intel Corporation" ),
280
319
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)
348
387
}
349
388
#endif
350
389
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
+
351
498
static char * force_device = "" ;
352
499
module_param (force_device , charp , S_IRUSR | S_IRGRP | S_IROTH );
353
500
MODULE_PARM_DESC (
@@ -365,58 +512,34 @@ static int __init asustor_init(void)
365
512
driver_data = NULL ;
366
513
// allow overriding detection with force_device kernel parameter
367
514
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 ;
388
522
}
389
523
}
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
+ }
390
531
pr_info ("force_device parameter is set to \"%s\", treating your machine as "
391
532
"that device instead of trying to detect it!\n" ,
392
533
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 ( );
395
536
if (!system ) {
396
537
pr_info ("No supported ASUSTOR mainboard found" );
397
538
return - ENODEV ;
398
539
}
399
540
400
541
driver_data = system -> driver_data ;
401
542
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
-
420
543
pr_info ("Found %s or similar (%s/%s)\n" , driver_data -> name ,
421
544
system -> matches [0 ].substr , system -> matches [1 ].substr );
422
545
}
0 commit comments