Skip to content

DS2415+ (Avoton) and DX1215: how to control the hardware from plain Linux, and where it could fit in synodeb #1

Closed

Description

@luuvasara

Offered as inspiration. Nothing here is expected to be merged as is: if it doesn't fit the
spirit or direction of synodeb, feel free to take only the findings, reshape them, or close
this.

Hi, and thanks for synodeb. It is what got us started: synodeb is the inspiration and the base
of our work on an older unit, a DS2415+ (Atom C2538, DSM platform avoton) with a DX1215
expansion unit. With the stage-1 boot chain proposed in the boot PRs, synodeb is also what
gets Unraid booting on this hardware; our separately hosted Unraid plugins then add the
hardware support (fans, LEDs, the expansion unit). Along the way we measured how the fans, LEDs and the expansion unit can be driven from a stock Linux kernel. This issue collects the findings so you can judge what is useful for synodeb, and asks how you would like them contributed.

Labels used below:

  • measured: seen on our unit (one DS2415+, one DX1215).
  • DSM-derived: read from DSM's own binaries/GPL source or logged from DSM's own tools; behaviour confirmed where marked measured.
  • inferred: our interpretation, not tested.

Hardware-test status: everything marked measured was tested on Unraid 7.3.4 (kernel 6.18.58-Unraid) through two Unraid plugins, "Synology Hardware" (userspace MCU/fan daemon) and "Synology Expansion Unit Driver" (the out-of-tree kernel module synology_dx). Both are maintained and hosted by us in our own repository (https://github.com/luuvasara/unraid-synology-hardware and https://github.com/luuvasara/unraid-synology-expansion), are not proposed for synodeb, and are mentioned only as the field-tested reference. The libata quirk was measured on Debian 6.12.

Status of the synodeb port

The findings are implemented in synodeb's own format (kernel/patches + kernel/src driver, synohwd board support, a pkg/synology-ds2415p package), offered as PRs 07-11 below. Identifiers say ds2415p: Synology sells non-plus models in this family that are different hardware (DS1515 vs DS1515+), and DSM itself spells the model DS2415p; the DS923+ needs no suffix because there is no non-plus DS923. Facts below are therefore split into:

  • hardware-tested on Unraid via the synology_dx module / Synology Hardware plugin, and
  • build-tested only in synodeb form (nothing in synodeb form has been run on the unit yet, unless stated).

Implementation branches

PR content status
07 kernel/patches/0009: libata API for PMP vendor registers from a quiet EH round (16-bit register numbers) build-tested (6.18.58, W=1); DS923+ drivers/ata objects byte-identical
08 kernel/src/drivers/ata/synology_ds2415p.c + patch 0010 + kernel/ds2415p.config (opt-in EXTRA_CONFIGS): 88SE9215 GPIO init, orange bay LEDs incl. PCH GPIO 49 as LED class, DX1215 hwmon (fan level, alarms), DX bay LEDs, switch build-tested only; the same register accesses hardware-tested through synology_dx
09 synohwd: DS2415PDaemon (MCU on ttyS1, allow-list, DSM fan tables, DX1215 policy) offline tests (138 checks, the old tests unchanged and green); the policy hardware-tested in the Unraid plugin
10 pkg/synology-ds2415p dpkg-deb --build only; not installed on the unit
11 docs/ds2415p-hardware.md design note —

The boot-stall quirk is PR 02 (kernel/patches/0008).

1. DS2415+ microcontroller (MCU)

  • Device: I/O 0x2F8, irq 3, /dev/ttyS1, ASCII commands (measured).
  • Line settings: 9600 8N1 is assumed, taken from the DS923+ support in synodeb. It was not measured on this MCU. Writing without any stty worked from an initramfs, and the daemon opens the port 9600 8N1 raw.
  • Byte map: DSM's own syno_microp_control was run with synobios logging on, and the bytes written to the port were recorded (measured).
Function Bytes
buzzer short 2
power LED on / blink / off 4 / 5 / 6
status LED off 7
status green on / blink 8 / 9
status orange on / blink : / ;
USB LED on / blink / off @ / A / B
alert LED on / blink / off LA1 / LA2 / LA3
eSATA LED on / blink / off LE1 / LE2 / LE3
fan duty, percent V10 .. V99
fan PWM frequency W20 (DSM uses 20 Hz)
fan checker on / off u / t
version query O
Wake-on-LAN on / off l / k
AC recovery on / off q / p
  • Keepalive: DSM's scemd sends u about every 10 s (measured). We keep sending it. What the MCU does if the stream stops was not measured (hypothesis: fan-fail beep or full speed).
  • DSM sends t at shutdown (the SynoDisableFanCheck service; inferred to be t).
  • Never-send list in our daemon (allow-list, everything else refused): 1 (power off), C (reset), 3 (long buzzer, never seen on the wire), and V below 10 so that a bug cannot stop the fan. 1 and C are from third-party reports for other models and were not tested.
  • The MCU sends 3 and K at about 4 Hz each; meaning unknown. Power-button and fan-fail event bytes are not decoded.
  • Fan policy: the DS2415+ scemd.xml tables (read from DSM, values only): disk 0/46/50/54 C to 20/40/70/99 %, alert at 58 C; CPU 0/65/85 C to 20/70/99 %, alert at 85 C.

DS2415+ fan sweep (measured; idle, duty held until no temperature moved more than 0.5 C in 600 s):

Duty sent CPU max (end) Disk in DS bay 1 Disk in DX1215
20 % 33 C 31 C 28 C
15 % 35 C 31 C 28 C
12 % 36 C 32 C 28 C
10 % 37 C (max 38) 33 C 28 C
20 % (again) 34 C 32 C 28 C
40 % 30 C 30 C 28 C
70 % 28 C 29 C 28 C
99 % 27 C 28 C 28 C

Every step settled; 10 % is safe at idle. The DX1215 disk did not move, so the DS2415+ fans do not cool the expansion unit.

2. DX1215 topology and its GPIO register file

  • Topology (measured): the expansion port is a Marvell 88SE9235 (ata19-22). Behind each port is a Marvell 88SM9705 port multiplier (1b4b:9705) that mainline reports as 6 ports; DSM reports 5. Each PMP has an EMID (measured 0..3 for ata19..22 on our unit); the EMID-0 PMP is the master. Page 0 carries EMID in bits 7:5 and the unit id 0x13 (DX1215) in bits 4:0.
  • Access (DSM-derived, then measured): PMP READ (0xE4) and WRITE (0xE8) on PMP port 15 (the control port), vendor registers 0x3a0 (output data), 0x3a4 (output enable, 0xfffff for a write) and 0x3b0 (input). The register number is 16 bits (high byte in the HOB feature field). DSM wraps this in a sysfs file in its patched kernel. A host-side write selects a "page" through address bits and pulses write/read strobe bits; the 8 data bits appear as GPO/GPI 1..8.

Pages (EMID-0 master; slaves read 0x3f on pages 2-4, not wired):

Page Bits Meaning Label
0 4:0, 7:5 unit id (0x13), EMID measured
1 0-2 orange fault of the PMP's local disk 0-2 (1 = orange) measured (DX bays 1, 2 lit)
1 5 PSU buzzer-mute pulse DSM-derived, never set by us
1 6 1 = all disk LEDs off measured (0x40 at boot on all four)
2 0 fan 1 alarm measured
2 1 fan 2 alarm measured (DSM checks only bit 0)
2 5 power on DSM-derived, not tested by cutting power
2 6, 7 PSU 1 / PSU 2 status polarity unknown (one PSU fitted, both read 0)
3 0 DEFAULT/MANUAL switch, 1 = DEFAULT measured at the box
4 1:0 fan code measured
4 3:2 RAID-ready LED DSM-derived
4 4 power-toggle pulse DSM-derived; forced to 0 on every write
4 5 power-button disable DSM-derived
4 6 alarm LED DSM-derived
  • Fan code, page 4 low 2 bits, written only on the EMID-0 master, other bits written back as read: 00 FULL, 01 MIDDLE, 10 LOW, 11 STOP. Idle state with no host writes: LOW (10).
  • Measured: MIDDLE, FULL and LOW writes each took about 0.05 s, read back as page 4 = 0x01, 0x00, 0x02 and were stable 30 s later with no fan alarm. A 30 s STOP test gave code 3, a fan alarm within 5 s and page 2 0x3c to 0x3f (both alarm bits). Reverting to LOW cleared it. So STOP really stops both fans, and nothing exists between LOW and STOP. The disk behind the DX1215 (28 C) was unaffected. No new libata reset lines in any step.
  • The fans are two-wire alarm type: no tach, only the alarm lines and the commanded code can be read.
  • DSM's DX1215 temperature table (values from scemd.xml): disk temperature only, LOW at 0 C, MIDDLE at 48 C, FULL at 55 C, FULL plus shutdown at 61 C, period 300 s. Our daemon follows it, treats an alarm as FULL, an unreadable temperature as MIDDLE, and never sends STOP.
  • Caution (DSM-derived): never leave page-4 bit 4 set (it acts as a power-switch press) and never write page 4 without read-modify-write.

3. Bay LEDs

DS2415+ internal bays (three 88SE9215, measured on hardware):

  • Green: the 9215 has its own GPIO block behind the AHCI vendor window (ABAR+0xA8 = index, +0xAC = data). DSM programs GPIO0-3 as link/activity of ports 0-3, and the chip then blinks the LED itself. Mainline ahci never touches this window, so the bays are dark. Init values (same as DSM's GPL ahci code): index 0x07071024 (output enable) = 0, 0x07071020 (data out) = 0, 0x07071058 (pin function) = 0x00B6D8D1. Before init they read oe=0xffffffff out=0 func=0x2db6c688. Green present/activity then works on both units.
  • Orange: GPIO4-7 are the fault LEDs of ports 0-3, data-out bit 4+port, 1 = orange. Setting only these bits does nothing (measured, 12 steps, no tray changed). The orange LEDs additionally need Avoton PCH GPIO 49 driven high, bit 17 of GPIOBASE+0x88 (SUS-well bank), as DSM's synobios does on the first disk-LED call and never undoes. With both set, the DS orange walk worked at the box. Role of GPIO 49 (enable for the LED buffer) is inferred.

DX1215 bays: page 1 of each 88SM9705 as in the table above: bits 0-2 orange fault, bit 6 = 1 turns all disk LEDs off (it is 1 at power-up, so the LEDs are dark until cleared). With bit 6 cleared the green LEDs work. Orange on DX bays 1 and 2 was seen at the box. Whether green comes purely from hardware is inferred.

Restore on unload: the module puts the load-time values back.

4. Bay maps (confirmed at the box by blink count)

  • DS2415+: bay = DSM disk index + 1. The 88SE9215 at 03:00.0 gives bays 1-4, 05:00.0 gives bays 5-8, 06:00.0 gives bays 9-12, in controller port order. Front grid is 2 columns by 6 rows (1 2 / 3 4 / ... / 11 12).
  • DX1215: bay = 3 x EMID + fan-out port + 1, three bays per PMP (confirmed for all 12). Our DX disk sits on EMID 2, port 1, which is bay 8.

5. Boot: the 88SM9705 SEMB link stall

  • Symptom (measured, Debian 6.12): link 5 of each 88SM9705 is the SEMB (enclosure) device. It never answers SRST, so error handling loops (softreset failed (1st FIS failed), "reset failed, giving up", "hard resetting link", repeat). With one internal and one DX1215 disk: internal disk attached at 200.6 s, DX1215 disk at 203.3 s, because the SCSI scan is serialised behind libata EH.
  • libata.force=19.05:disable,... has no effect: disable is a device horkage applied after IDENTIFY, which link 5 never reaches.
  • Partial fix, libata.force=19.05:norst,20.05:norst,21.05:norst,22.05:norst (measured, Unraid 6.18.58): last DX EH complete at 103 s instead of 200 s. The hard reset is still tried, so link 5 still shows failed to resume link (SControl 0).
  • Full fix, a libata-pmp quirk setting ATA_LFLAG_DISABLED on link 5 of a 1b4b:9705, modelled on the existing Marvell 4140 SEMB quirk (measured, Debian 6.12): softreset failures from dozens to 0, EH complete in 8.6-18.5 s, internal disk at 18.6 s, DX1215 disk at 21.3 s (about 19 s overall versus 200 s). One failed to resume link per PMP remains. The patch is tested on 6.12 only (build-tested on 6.18.58), offered as kernel/patches/0008 in PR 02; it could go to mainline later.
  • Unraid's kernel has libata built in, so the quirk cannot be shipped as a module there; that is why the module synology_dx exists and why Unraid only gets the norst workaround.

6. What exists today

Two Unraid plugins, maintained and hosted by us (https://github.com/luuvasara/unraid-synology-hardware and https://github.com/luuvasara/unraid-synology-expansion), not proposed for synodeb, are the working, field-tested reference:

  • Synology Hardware: MCU/fan daemon with the allow-list, DS2415+ fan tables and modes, MAC fix, DX1215 fan control, history graphs.
  • Synology Expansion Unit Driver: the out-of-tree module synology_dx with a sysfs API under /sys/kernel/synology_dx/ and a hwmon device (fan1_alarm, fan2_alarm, pwm1, pwm1_enable). Fan writes only on the EMID-0 master, STOP only as a time-limited test with a kernel-side revert, bay LEDs optional and off by default.

The same approach serves Debian in synodeb's own form in PRs 07-10 (in-tree, through libata's own EH context instead of the module's workaround for Unraid's built-in libata).

Questions for you

  1. Do you want DS2415+/DX1215 support inside synodeb at all (as PRs 07-10, or as a DKMS package), or would you prefer it in a fork / separate repository that synodeb references?
  2. How would you like other models' hardware support structured (per-model profile data, per-platform drivers, a common interface)? The MCU port, byte map and fan tables are clearly per-model.
  3. Is a driver in kernel/src (with hwmon + LED class, PR 08) the right place for the DX1215 fan and LED code, or would you rather keep it in userspace?
  4. Would you take the libata-pmp quirk through kernel/patches now (PR 02), or wait until it is in mainline?
  5. One published kernel for both models (the DS923+ drivers are DMI-gated, ours is DMI + PCI-topology gated), or a separate build for the DS2415+?

Open questions (ours)

  • MCU baud rate on the DS2415+ is unmeasured (9600 assumed).
  • What the MCU does when the u keepalive stops, and the meaning of the 3 and K bytes it sends.
  • Power-button and fan-fail event bytes; no clean shutdown on button press yet.
  • DX1215 PSU bits (page 2 bits 6 and 7): polarity unknown; power-on bit not tested by cutting power.
  • Role of PCH GPIO 49 (inferred as an LED buffer enable).
  • Whether DX1215 green LEDs are purely hardware once page 1 bit 6 is 0.
  • DS2415+ fan tach and fan-fail events are not read; no DX1215 fan sweep has been run (no tach).
  • Remaining failed to resume link (SControl 0) per PMP after the quirk was not investigated.
  • Other DX/RX units: only the DX1215 was tested; the unit id table (other 9705-class ids) is DSM-derived only.

Activity

  1. rylos commented on Oct 28, 2026

    @rylos
    Owner

    Thank you, this is a great write-up, and it's nice to see synodeb used as a base.

    Short answers to your questions, with the details in the RFC (#6):

    1. Yes, I'd like other models inside synodeb rather than in forks, but with a structure that
      scales: platforms (avoton for the DS2415+), one board file per model, support tiers and a
      maintainer per platform. The DS2415+ would be the first community platform, with you as its
      maintainer if you're up for it.
    2. Per-platform drivers and board support, per-model data files (boards/ds2415plus.conf),
      common interfaces for the DOM, the boot entries and synohwd.
    3. A kernel driver in the platform directory (hwmon + LED class) is the right place for the
      DX1215 fans and LEDs; the fan policy stays in synohwd.
    4. The 88SM9705 SEMB quirk (kernel: libata-pmp quirk for the Marvell 88SM9705 SEMB port #3): yes, now. Please also send it to linux-ide.
    5. One kernel per series for all models, drivers bound by DMI/PCI. The kernel now has two
      series (longterm 6.18 as the default, stable 7.3); patches live in kernel/patches/<x.y>.

    One request for everything that lands in this repository: hardware facts are fine, but please
    leave out references to DSM binaries or Synology code, and include a boot log from the real
    unit running the synodeb form (not only Unraid).

    If you have a DX517 owner around: the port multiplier ID of a DX517 (from dmesg under DSM)
    would tell us whether #3/#5 also matter for DS923+ users.

  2. Repository owner locked and limited conversation to collaborators on Oct 28, 2026
  3. converted this issue into a discussion #8 on Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions