@@ -57,11 +57,11 @@ static struct gpio_led asustor_leds[] = {
57
57
{ .name = "red:power" , .default_state = LEDS_GPIO_DEFSTATE_OFF }, // 3
58
58
{ .name = "green:status" , .default_state = LEDS_GPIO_DEFSTATE_ON }, // 4
59
59
{
60
- .name = "red:status" ,
60
+ .name = "red:status" , // 5
61
61
.default_state = LEDS_GPIO_DEFSTATE_OFF ,
62
62
.panic_indicator = 1 ,
63
63
.default_trigger = "panic" ,
64
- }, // 5
64
+ },
65
65
{ .name = "blue:usb" , .default_state = LEDS_GPIO_DEFSTATE_OFF }, // 6
66
66
{ .name = "green:usb" , .default_state = LEDS_GPIO_DEFSTATE_OFF }, // 7
67
67
{ .name = "blue:lan" , .default_state = LEDS_GPIO_DEFSTATE_ON }, // 8
@@ -103,7 +103,7 @@ static struct gpiod_lookup_table asustor_fs6700_gpio_leds_lookup = {
103
103
// 6
104
104
// 7
105
105
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
107
107
GPIO_LOOKUP_IDX (GPIO_IT87 , 12 , NULL , 21 , GPIO_ACTIVE_LOW ), // nvme1:green:disk
108
108
GPIO_LOOKUP_IDX (GPIO_IT87 , 13 , NULL , 22 , GPIO_ACTIVE_LOW ), // nvme1:red:disk
109
109
{}
@@ -234,23 +234,53 @@ static struct gpiod_lookup_table asustor_600_gpio_keys_lookup = {
234
234
};
235
235
// clang-format on
236
236
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
+
237
249
// ASUSTOR Platform.
238
250
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
+
240
255
struct gpiod_lookup_table * leds ;
241
256
struct gpiod_lookup_table * keys ;
242
257
};
243
258
259
+ #define VALID_OVERRIDE_NAMES "AS6xx, AS61xx, AS66xx, AS67xx, FS67xx"
260
+
244
261
// NOTE: if you add another device here, update VALID_OVERRIDE_NAMES accordingly!
245
262
246
263
static struct asustor_driver_data asustor_fs6700_driver_data = {
247
264
.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
+ },
248
272
.leds = & asustor_fs6700_gpio_leds_lookup ,
249
273
.keys = & asustor_fs6700_gpio_keys_lookup ,
250
274
};
251
275
252
276
static struct asustor_driver_data asustor_6700_driver_data = {
253
277
.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
+ },
254
284
.leds = & asustor_6700_gpio_leds_lookup ,
255
285
.keys = & asustor_6100_gpio_keys_lookup ,
256
286
};
@@ -260,6 +290,8 @@ static struct asustor_driver_data asustor_6600_driver_data = {
260
290
// because it seems to use the same GPIO numbers,
261
291
// but listed extra for the different name
262
292
.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)
263
295
.leds = & asustor_6700_gpio_leds_lookup ,
264
296
.keys = & asustor_6100_gpio_keys_lookup ,
265
297
};
@@ -276,15 +308,28 @@ static struct asustor_driver_data asustor_600_driver_data = {
276
308
.keys = & asustor_600_gpio_keys_lookup ,
277
309
};
278
310
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().
281
316
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
282
319
{
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)
288
333
.matches = {
289
334
DMI_EXACT_MATCH (DMI_SYS_VENDOR , "Intel Corporation" ),
290
335
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)
358
403
}
359
404
#endif
360
405
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
+
361
516
static char * force_device = "" ;
362
517
module_param (force_device , charp , S_IRUSR | S_IRGRP | S_IROTH );
363
518
MODULE_PARM_DESC (
@@ -375,58 +530,34 @@ static int __init asustor_init(void)
375
530
driver_data = NULL ;
376
531
// allow overriding detection with force_device kernel parameter
377
532
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 ;
398
540
}
399
541
}
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
+ }
400
549
pr_info ("force_device parameter is set to \"%s\", treating your machine as "
401
550
"that device instead of trying to detect it!\n" ,
402
551
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 ( );
405
554
if (!system ) {
406
555
pr_info ("No supported ASUSTOR mainboard found" );
407
556
return - ENODEV ;
408
557
}
409
558
410
559
driver_data = system -> driver_data ;
411
560
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
-
430
561
pr_info ("Found %s or similar (%s/%s)\n" , driver_data -> name ,
431
562
system -> matches [0 ].substr , system -> matches [1 ].substr );
432
563
}
0 commit comments