@@ -224,23 +224,49 @@ 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
+ // 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
+ },
238
257
.leds = & asustor_fs6700_gpio_leds_lookup ,
239
258
.keys = & asustor_fs6700_gpio_keys_lookup ,
240
259
};
241
260
242
261
static struct asustor_driver_data asustor_6700_driver_data = {
243
262
.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
+ },
244
270
.leds = & asustor_6700_gpio_leds_lookup ,
245
271
.keys = & asustor_6100_gpio_keys_lookup ,
246
272
};
@@ -250,6 +276,8 @@ static struct asustor_driver_data asustor_6600_driver_data = {
250
276
// because it seems to use the same GPIO numbers,
251
277
// but listed extra for the different name
252
278
.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)
253
281
.leds = & asustor_6700_gpio_leds_lookup ,
254
282
.keys = & asustor_6100_gpio_keys_lookup ,
255
283
};
@@ -268,13 +296,26 @@ static struct asustor_driver_data asustor_600_driver_data = {
268
296
269
297
#define VALID_OVERRIDE_NAMES "AS6xx, AS61xx, AS66xx, AS67xx, FS67xx"
270
298
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().
271
304
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
+ },
272
316
{
273
317
// 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)
278
319
.matches = {
279
320
DMI_EXACT_MATCH (DMI_SYS_VENDOR , "Intel Corporation" ),
280
321
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)
348
389
}
349
390
#endif
350
391
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
+
351
501
static char * force_device = "" ;
352
502
module_param (force_device , charp , S_IRUSR | S_IRGRP | S_IROTH );
353
503
MODULE_PARM_DESC (
@@ -365,58 +515,34 @@ static int __init asustor_init(void)
365
515
driver_data = NULL ;
366
516
// allow overriding detection with force_device kernel parameter
367
517
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 ;
388
525
}
389
526
}
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
+ }
390
534
pr_info ("force_device parameter is set to \"%s\", treating your machine as "
391
535
"that device instead of trying to detect it!\n" ,
392
536
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 ( );
395
539
if (!system ) {
396
540
pr_info ("No supported ASUSTOR mainboard found" );
397
541
return - ENODEV ;
398
542
}
399
543
400
544
driver_data = system -> driver_data ;
401
545
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
546
pr_info ("Found %s or similar (%s/%s)\n" , driver_data -> name ,
421
547
system -> matches [0 ].substr , system -> matches [1 ].substr );
422
548
}
0 commit comments