Eigentlich wollte ich nur etwas ganz Simples: Aufnahmen von meinem alten DOS-Rechner machen. BIOS-POST, der Speichertest, ein paar DOS-Spiele, einmal das alles sauber als Video festgehalten. Dafür landete ein „VGA to USB 3.0 HD 1080P Video Capture Card“ in meinem Warenkorb, so ein billiger Dongle für ein paar Euro. Spoiler: in genau der Form hat das nicht geklappt. Und warum es nicht klappt, ist die eigentlich spannende Geschichte. Es geht um einen 8051 mit recyceltem Mask-ROM, eine EDID die der Quelle einen Monitor vorlügt, ein „1080p“ das horizontal gar keins ist, und eine Strings-Modifikation die ich bis heute nicht überlistet habe.
Das konkrete Gerät, falls es jemand nachvollziehen will, gibt es bei Amazon unter diesem Link. Es ist einer von hunderten optisch identischen Sticks, die alle den gleichen MacroSilicon-Chipsatz tragen. Die Erkenntnisse hier gelten also für eine ganze Geräteklasse, nicht nur dieses eine Exemplar.

Was steckt auf der Platine?
Aufgeschraubt zeigt sich eine winzige Platine mit Silkscreen-Aufdruck AFN_VGA_Captor 2020/0615_V1.1, einem VGA-Stecker, einer USB-A-Buchse und einem 3-poligen Audio-Header. Drei Halbleiter machen die eigentliche Arbeit:
| Ref | Chip | Funktion |
|---|---|---|
| U3 | MacroSilicon MS2109 | 8051-basierte UVC-Bridge, USB 2.0 High-Speed |
| U7 | MacroSilicon MS9288A | Analoger VGA-Empfänger (PLL plus ADC plus Scaler) |
| U2 | HK / Holtek 24C16 | I²C-EEPROM, 2 KiB |
Der MS2109 ist berühmt-berüchtigt. Er steckt in den meisten der spottbilligen HDMI-Capture-Sticks, die seit Jahren durchs Netz geistern. Hier sitzt derselbe Chip in einer VGA-Variante, mit dem MS9288A als analogem Frontend davor. Wie eng die beiden Welten verwandt sind, wird sich beim Mask-ROM-Dump zeigen: die Firmware ist nachweislich aus der HDMI-Variante recycelt.



Wie meldet sich das Ding am USB?
Beim Anstecken kommt der erste Hinweis darauf, dass die Verpackung schwindelt. „USB 3.0″ steht drauf, der Kernel sieht aber ein High-Speed-Gerät, also USB 2.0:
usb 1-1.1: new high-speed USB device number 15 using xhci_hcd usb 1-1.1: New USB device found, idVendor=534d, idProduct=2109, bcdDevice=21.00 usb 1-1.1: Manufacturer: MACROSILICON usb 1-1.1: Found UVC 1.00 device <unnamed> (534d:2109) hid-generic 0003:534D:2109.0009: hiddev4,hidraw8: USB HID v1.10 Device
Vendor 0x534d ist MacroSilicon, Produkt 0x2109 der nackte MS2109. Das Gerät enumeriert als UVC-1.00-Kamera plus USB-Audio plus ein vendor-spezifisches HID-Interface. Genau dieses HID-Interface ist später der Schlüssel: darüber kommt man an das EEPROM und sogar an den Arbeitsspeicher des 8051 heran, ganz ohne Lötkolben am I²C-Bus.
Das Werkzeug: ms-tools
Die zentrale Referenz für alles, was mit MS2109 zu tun hat, ist das Projekt ms-tools von Bertold Van den Bergh. Das ist eine kleine Goldgrube: EEPROM lesen und schreiben über die HID-Schnittstelle, Live-Zugriff auf den XDATA-Speicher zur Laufzeit (per eingeschleustem 8051-Patch-Code), das Werkzeug mshack zum Injizieren eigener 8051-Routinen, Ghidra-Skripte mit teildisassemblierter Firmware und ein dump-rom, das den kompletten 64-KiB-Mask-ROM ausliest.
Auf Ubuntu 24.04 ist das schnell gebaut:
sudo apt install golang-go libhidapi-dev libudev-dev git clone https://github.com/BertoldVdb/ms-tools.git cd ms-tools/cli && go build -o ../msctl .
Der EEPROM-Dump und eine erste Korrektur
Manche MS2109-Revisionen mögen das übliche RAM-Patching nicht, das ms-tools für komfortable Zugriffe nutzt. Mit --no-patch liest der Dump trotzdem sauber, weil dann der rohe HID-Pfad ohne vorgeschaltete Firmware-Manipulation genommen wird:
msctl --raw-path /dev/hidraw8 --no-patch read EEPROM 0 2048 --filename=dumps/eeprom-vga-raw.bin
Von den 2048 Bytes sind nur 962 belegt. Die Aufteilung sieht so aus:
| Offset | Größe | Inhalt |
|---|---|---|
| 0x000 bis 0x00F | 16 B | Header: Magic a5 5a, dann Build-Datum 19 11 20 00 = 2019-11-20 |
| 0x010 bis 0x02F | 32 B | Vendor-Strings im Pascal-Format: AFN_Cap video, AFN_Cap audio |
| 0x030 bis 0x079 | 74 B | 8051-Patch-Code #1 (ROM-Hook-Routinen) |
| 0x07A bis 0x179 | 256 B | Vollständige EDID (128 Base plus 128 CTA-861-Extension) |
| 0x17A bis 0x3C1 | 584 B | 8051-Patch-Code #2, Keil-C51-kompiliert |
Die ersten Bytes mit dem Hexeditor angeschaut, da steht die Geschichte schon drin:
0000 a5 5a 03 8e 09 10 ff ff ff ff ff ff 19 11 20 00 .Z............ . 0010 0e 41 46 4e 5f 43 61 70 20 76 69 64 65 6f ff ff .AFN_Cap video.. 0020 0e 41 46 4e 5f 43 61 70 20 61 75 64 69 6f ff ff .AFN_Cap audio.. 0030 90 de 07 ef f0 12 cd d6 90 de 07 e0 ff 02 cf 25 ...............%
Das 0e vor jedem String ist die Pascal-Längenangabe (14 Zeichen). Ab 0x30 beginnt der erste 8051-Patch-Block. Spannend ist die EDID-Sektion ab 0x07A. Mein erster Analyse-Versuch ging davon aus, der klassische EDID-Header 00 FF FF FF FF FF FF 00 sei wegoptimiert und werde erst zur Laufzeit ergänzt. Das war falsch. Nach dem Mask-ROM-Dump und einem genaueren Blick steht der Header sauber im EEPROM. Solche Korrektur-Momente sind mir lieber als ein zu glattes Narrativ, also schreibe ich sie hier auch hin.
Die EDID selbst enthält einen Monitor-Namen MACROSILICON (EDID-Descriptor mit Tag 0xFC), als erstes Detailed-Timing 1280×720 bei 60 Hz mit 74,25 MHz Pixeltakt, ein zweites Timing 1280×768, dazu eine CTA-861-Extension mit nativem VIC=4 (720p60) plus VICs für 1080p60, 1080i, 720p50, 480p und 576p. Es gibt sogar einen HDMI-VSDB-Block mit dem OUI 00:0C:03 von HDMI Licensing und einer Source Physical Address. Eine VGA-only-Platine, die HDMI-Strukturen in ihrer EDID trägt. Das ist schon der erste deutliche Fingerabdruck der gemeinsamen Codebasis.
Der Mask-ROM verrät die Herkunft
Der eigentliche Programmcode des 8051 liegt in einem nicht beschreibbaren Mask-ROM. dump-rom lädt dafür eigenen 8051-Code in den USERRAM, der per MOVC den ROM ausliest und über HID zurückschiebt:
msctl --raw-path /dev/hidraw8 --no-patch dump-rom dumps/maskrom-vga.bin
64 KiB komplette CODE-Memory, davon rund 36 KiB belegt im Bereich 0x0000 bis 0x8FFF und weitere 12 KiB ab 0xC000. Die Highlights:
- 0x7738: eine zweite, im ROM fest verdrahtete Default-EDID, intakt mit Standard-Header, Hersteller-ID „HJW“ und Monitor-Name „HDMI TO USB“. Das ist der Beweis: diese Firmware wurde ursprünglich für den HDMI-Bruder geschrieben.
- 0x77A9: der ASCII-String „HDMI TO USB“ noch einmal direkt.
- 0x7087 und 0x709B: die USB-String-Descriptor-Fallbacks „USB Video“ und „USB Digital Audio“ in UTF-16LE. Diese beiden werden uns gleich noch ärgern.
- 0x0000: der Reset-Vector
02 41 49, alsoLJMP 0x4149. - Ein
LCALL 0xCC10bei ROM-Adresse0x478F: das ist der zentrale Einstieg vom Mask-ROM in den EEPROM-Patch-Code, der zur Laufzeit in den RAM kopiert wurde.
Der Bootloader kopiert dabei die EEPROM-Bytes ab Offset 0x30 in den USERRAM ab Adresse 0xCBD0. Die Strings-Sektion 0x10 bis 0x2F wird explizit nicht mitkopiert. Die Patch-Firmware liest die Strings stattdessen zur Laufzeit direkt per I²C aus dem EEPROM und baut daraus die USB-String-Descriptoren an festen RAM-Adressen zusammen. Diese Mapping-Tabelle ist der Kern, um den herum sich die ganze Bastelei dreht:
| EEPROM | Code-RAM | Was |
|---|---|---|
| 0x030 | 0xCC00 | Patch #1, kleine Hook-Routinen |
| 0x07A | 0xCC4A | EDID-Header 00 FF FF FF FF FF FF 00 |
| 0x080 | 0xCC50 | EDID-Body |
| 0x0FA | 0xCCCA | CTA-861-Extension |
| 0x17A | 0xCD4A | Patch #2, C51-Startup |
| 0x180 | 0xCD50 | MOV SP,#0x3B; LJMP 0xCD91 |
| 0x1C1 | 0xCD91 | main() der Patch-Firmware |
Erkenntnis 1: Die EDID lügt der Quelle einen Monitor vor
Über die DDC-Leitungen am VGA-Stecker (Pins 12 und 15) präsentiert der Dongle dem Quell-PC eine EDID. Damit gibt sich das Gerät als Monitor namens „MACROSILICON“ mit 720p60 als nativer Auflösung aus. Genau deshalb liefert ein Quell-PC, an dem gar kein echter Monitor hängt, trotzdem ein sinnvolles Bild: er glaubt, ein 720p-Display gefunden zu haben. So weit, so clever.
Erkenntnis 2: Das „1080p“ ist Marketing
Die UVC-Frame-Tabelle im Mask-ROM listet zwar brav 1920×1080 mit 30 fps als MJPEG. Die Realität sieht anders aus. Mit v4l2-ctl lässt sich die Format-Liste auslesen:
$ v4l2-ctl -d /dev/video2 --list-formats-ext [0]: 'MJPG' (Motion-JPEG, compressed) Size: Discrete 1920x1080 30/25/20/10/5 fps Size: Discrete 1280x720 60/50/30/20/10 fps Size: Discrete 1024x768 60/50/30/20/10 fps [1]: 'YUYV' (YUYV 4:2:2) Size: Discrete 1920x1080 5 fps Size: Discrete 640x480 30/20/10/5 fps
Unkomprimiertes YUYV bei 1080p ist auf magere 5 fps gedeckelt. Das native Detailed-Timing der EDID ist 720p60, das 1080p wird intern hochskaliert. Und USB 3.0 ist nirgends, der Chip kann nur High-Speed. Drei Behauptungen auf der Verpackung, drei mal geschummelt. Auf die 5-fps-Grenze komme ich am Ende noch genauer zurück, die hat einen sehr konkreten technischen Grund.
Erkenntnis 3: Kein DOS, und das lässt sich nicht reparieren
Jetzt zum eigentlichen Frustpunkt, der Grund, warum mein DOS-Plan scheiterte. Die UVC-Frame-Tabelle enthält keine 70-Hz-Modi. Ein DOS-BIOS gibt aber im Standard-VGA-Textmodus 720×400 bei 70 Hz aus. Das analoge Frontend, der MS9288A, könnte dieses Signal vermutlich locken, der Horizontaltakt von 31,469 kHz ist derselbe wie bei 640×480 bei 60 Hz. Aber die MS2109-Firmware bietet schlicht keinen passenden UVC-Frame-Mode an, an dem ein Aufnahmeprogramm andocken könnte.
Und das Bittere: im EEPROM kann man das nicht reparieren. Die Frame-Tabelle liegt im nicht-flashbaren Mask-ROM. Das EEPROM steuert nur EDID, Strings und ein paar Patch-Routinen, nicht die Liste der angebotenen Auflösungen. Wer mit so einem Dongle echte DOS-Signale aufnehmen will, kommt um eine vorgeschaltete Scaler-Box nicht herum. Mehr dazu am Ende.
Was wir trotzdem geändert haben: die EDID
Wenn die Frame-Tabelle schon unantastbar ist, dann wenigstens die EDID anfassen. Ich habe das erste Detailed-Timing von 720p60 auf 1920×1080 bei 60 Hz umgeschrieben (148,5 MHz Pixeltakt, das Standard-CEA-861-1080p60-Timing), die EDID-Checksumme neu berechnet und das EEPROM zurückgeflasht. Das neue DTD #1 sieht so aus:
02 3a 80 18 71 38 2d 40 58 2c 45 00 00 00 00 00 00 1e
Nach einem Replug liefert das Gerät die modifizierte EDID. Zumindest dachte ich das. Ob sie auch wirklich bei einer Quelle ankommt, war eine ganz eigene Odyssee, dazu komme ich beim A/B-Test. Vorweggenommen: Die Mod tut technisch genau das, was sie soll, sie ändert nur am Ende nichts an dem, was aufgenommen wird. Eine reine „ich sage der Quelle, ich kann 1080p“-Kosmetik.
Was NICHT geklappt hat: die Strings
Das ist der lehrreichste Teil des Projekts, gerade weil er bis heute offen ist. Ich wollte die USB-Function-Strings ändern, aus dem hässlichen „AFN_Cap video“ sollte ein sauberes „VGA Capture HD“ werden. Das EEPROM-Schreiben verifiziert sauber per Readback-Hash. Die EDID-Mod aus demselben Reflash funktioniert. Aber die Strings fallen nach dem Replug auf die ROM-Defaults „USB Video“ und „USB Digital Audio“ zurück. Irgendetwas in der Firmware sagt „nein“.
Also empirisch rangegangen, drei Single-Byte-Tests in der Strings-Region 0x10 bis 0x2F: einmal ein ASCII-Zeichen geändert, einmal ein FF-Padding-Byte, einmal das Längen-Byte. Jeder einzelne dieser Eingriffe löst den Fallback aus. Egal welches Byte, egal welcher Inhalt. Das ist eine klare Indikation für ein Content-Gate über die gesamte 32-Byte-Region, nicht für eine simple Längen- oder Inhaltsprüfung an einer Stelle.
Ein Gegencheck zur eigenen Analyse hat ein paar Punkte geschärft. Die Bytes 0x02 und 0x03 (03 8E) sind verifiziert die Payload-Länge und keine Checksumme: 910 Bytes ab 0x30 reichen bis 0x3BD, exakt bis vor den End-Marker. Der Verdacht: ein lokales Validierungs-Gate auf den 32-Byte-Strings-Slots, wahrscheinlich ein Hash oder ein Exact-Content-Compare in einem ROM-Helper, nicht im Patch-Code selbst. Ein Quervergleich mit dem usbkvm-Projekt zeigt dasselbe Header-Muster bei verwandter Hardware.
Der spannendste Nebenbefund kam aus einem XDATA-Boot-Trace bei 0xC630 bis 0xC67F: die „AFN_Cap“-Strings landen gar nicht dauerhaft im RAM. Was dort steht, ist die Fallback-Variante. Die echten Strings werden erst zur Laufzeit beim USB-GetDescriptor aus dem EEPROM gebaut, und genau in diesem Moment greift offenbar das Gate. Wer das knacken will, müsste den USB-Control-Transfer mit usbmon und Wireshark während der Enumeration mitschneiden, die ROM-Helper bei 0x6345 und Nachbarn disassemblieren, oder mit mshack einen Bypass-Patch auf den Fallback-Branch setzen. Der schnellste Weg wäre allerdings, ein vom offiziellen MacroSilicon-Windows-Tool editiertes EEPROM byteweise gegen mein eigenes zu diffen. Dieses Tool schreibt vermutlich versteckte Metadaten mit, die das öffentliche Reverse-Engineering noch nicht dokumentiert hat. Das ist mein heißester Kandidat fürs Strings-Mysterium, aber bisher unbewiesen.
Der A/B-Test, oder: die Suche nach einer ehrlichen VGA-Quelle
Ich wollte zwei Dinge empirisch klären. Erstens: Kommt die modifizierte 1080p-EDID überhaupt bei der Quelle an? Zweitens: Löst das Ding echtes 1080p auf oder ist das hochskalierter Matsch? Beides braucht eine VGA-Quelle, die ihre DDC-Leitung auch wirklich ausliest. Das war schwerer zu finden als gedacht.
Erster Anlauf, ein Notebook mit „VGA-Out“. Plot-Twist: der VGA-Anschluss lief über einen aktiven DisplayPort-auf-VGA-Adapter und tauchte als DP-4 auf. Der Adapter liefert ein eigenes synthetisches EDID („NVD“, „LCD_VGA“, 1280×1024). Das ist nicht das EDID aus dem Dongle. Der EDID-Pfad vom Dongle bis zur GPU ist also unterbrochen. Erste Lehre: aktive DP-auf-VGA-Adapter verhalten sich wie eine eigene EDID-Quelle und maskieren alles dahinter.
Zweiter Anlauf, ein ThinkPad L560 mit echter VGA-Buchse hinten. Wieder gescheitert, und zwar aus einem prinzipiellen Grund. Ab Haswell und Skylake hat Intel den analogen RAMDAC komplett aus der GPU geworfen. Die physische VGA-Buchse hängt an einem Onboard-DP-auf-VGA-Bridge-Chip. Der Kernel verrät es selbst:
$ xrandr --verbose | grep -i subconnector
subconnector: VGA
$ cat /sys/class/drm/card1-DP-2/edid | wc -c
0
Null Byte EDID. Die Bridge reicht das DDC vom Dongle nicht durch, der Connector bietet nur die VESA-Default-Modi an. Exakt dasselbe Problem wie beim ersten Notebook, nur diesmal onboard statt als Dongle. Auf beiden getesteten Bridge-Pfaden kam die EDID des Capture-Sticks nicht zur Quelle durch. Als Trend lässt sich sagen: neuere Intel-Notebooks haben den analogen RAMDAC verloren (der fiel etwa um Haswell und Broadwell, also rund 2013 bis 2015) und führen „VGA“ über DP-Bridges, die DDC abschneiden. Das als „kein Notebook taugt je“ zu verkaufen wäre übertrieben, es ist ein Trend, kein Beweis.
Ein netter Nebenbefund vom L560, den ich erst für einen kaputten Aufbau hielt: nach jedem Auflösungswechsel liefert der Dongle erstmal 1 bis 6 Sekunden ein komplett schwarzes Bild, bis die MS9288A-PLL wieder eingerastet ist. Ich hatte reihenweise schwarze Frames gegrabbt und schon den Analogpfad für tot erklärt. In Wirklichkeit hatte ich nur immer ins Re-Lock-Fenster reingeschossen. Mit 4 bis 6 Sekunden Settle-Zeit kommt das Bild stabil. Passt zur dokumentierten Sync-Trägheit des Chips.
Der A/B-Test selbst
Trotz blockiertem DDC habe ich den A/B durchgezogen, einfach um es schwarz auf weiß zu haben. Als Quelle ein 1920×1080-Testbild mit vier Quadranten aus 1-Pixel-Gittern: Schachbrett, vertikale Linien, horizontale Linien, Diagonale. Dazu grüne Eck-Marker, um sicher zu sein, dass der ganze Frame ankommt.

Erst der Befund, der mich am meisten interessiert hat: 1px-Vertikallinien und Schachbrett kollabieren zu flachem Grau, das horizontale Feindetail ist weg. 1px-Horizontallinien und Diagonale zeigen schwache Resttextur. Die vertikale Auflösung kommt also weitgehend durch (jede Scanline ist eine eigene analoge Zeile), die horizontale ist deutlich begrenzt. Wichtiges Ehrlichkeits-Caveat: das ist die komplette Analogkette, also Bridge-DAC im L560, Kabel und MS9288A-ADC zusammen. Mit diesem Aufbau lässt sich nicht sauber trennen, wieviel davon der Dongle ist.
Dann der eigentliche EEPROM-A/B. EEPROM gegen das Original-Backup getauscht, Readback-Hash jedes Mal verifiziert. Etwas, das ich erst lernen musste: nach dem Flashen muss der Dongle einmal physisch ab- und wieder angesteckt werden. Ein reiner USB-Bus-Reset reicht nicht, der 8051 läuft einfach weiter und liest das neue EEPROM gar nicht ein. Software-seitig ging das nicht (kein schaltbarer Port), also von Hand. Die Zahlen, ImageMagick MAE auf einer Skala von 0 bis 1:
| Vergleich | MAE | pro 255 |
|---|---|---|
| Rauschen mod3 (zwei Aufnahmen, gleiches EEPROM) | 0,0041 | 1,05 |
| Rauschen Original | 0,0043 | 1,10 |
| mod3 gegen Original | 0,0071 | 1,82 |
Der Cross-Wert liegt nur minimal über dem Rauschpegel. Das maximal verstärkte Differenzbild zeigt keine strukturierte Änderung, nur MJPEG-Blockrauschen in den hochfrequenten Quadranten. Die kleine Differenz ist Re-Lock- und AGC-Varianz zwischen den Sessions, nicht das EDID.

Fazit des A/B: Die EDID-Mod von 720p auf 1080p verändert das aufgenommene Bild exakt null. Aus erstem Prinzip war das klar, die EDID ist das, was das Gerät der Quelle anbietet, sie steuert nicht den Aufnahmepfad. Aber jetzt steht es empirisch da.
Endlich eine echte Analog-Quelle, und zwei dicke Befunde
Dritter Anlauf, diesmal ein Desktop mit einer NVIDIA GeForce GT 630 (Kepler). Kepler hat noch einen echten analogen RAMDAC, über einen passiven DVI-I-auf-VGA-Adapter kommt echtes analoges VGA raus. Endlich die Hardware, die den Notebooks fehlte.
Befund 1: Die EDID-Mod ist sogar grundsätzlich für die Katz. Der NVIDIA-Treiber cached die analoge EDID hartnäckig (analoges VGA hat kein Hotplug), also einmal lightdm neugestartet für einen frischen Read. Das Ergebnis im Xorg-Log:
(--) NVIDIA(GPU-0): CRT-0: connected (WW) NVIDIA(0): CRT-0 does not have an EDID
Der Dongle liefert auf seinem VGA-Eingang gar keine DDC und keine EDID. Sauber belegt: derselbe Adapter hat vorher die EDID eines echten Monitors gelesen (ein Fujitsu P24-9, mit 1920×1080 und physischen Maßen), der Adapter reicht DDC also durch. Es ist der Dongle, der nichts treibt. Das kippt eine Annahme aus meinen eigenen Notizen, wo stand, das Patch-Modul beantworte „wahrscheinlich“ DDC-Reads. Dieses „wahrscheinlich“ war nie gemessen.
Ehrlich bei der Konfidenz bleiben: hoch dafür, dass in diesem GT-630-Test keine lesbare EDID vom Dongle kam. Nur mittel für das universelle „keine Quelle sieht die EDID je“. Restzweifel, die ich noch nicht ausgeräumt habe: NVIDIA-Eigenheiten bei analogem DDC, ob die Dongle-DDC die 5 Volt auf VGA-Pin 9 braucht, oder ob der DDC-Responder erst nach Sync-Lock aufwacht. Ein Pin-9-Check plus Logic-Analyzer auf den Pins 12 und 15 (mit dem Fujitsu als Positiv-Kontrolle) würde es hart machen. Arbeitshypothese: auf dem getesteten echten Analogpfad präsentierte der Dongle keine lesbare EDID, vermutlich weil der VGA-DDC-Pfad in der geteilten HDMI/VGA-Codebasis schlicht nicht verdrahtet ist. HDMI hat HPD und DDC, VGA hier offenbar nicht.
Befund 2: Das „1080p“ ist vertikal echt, horizontal Fake. Mit echtem RAMDAC konnte ich endlich den Dongle-Anteil am Blur isolieren (eigene xorg.conf mit UseEDID false und forciertem 1080p-Modeline, NVIDIA lehnt xrandr-Modelines ab). Testbild: reine 1px-Schwarz-Weiß-Gitter per xsetroot. Das Resultat, gemessen als Standardabweichung der Luma (ein ideales Gitter liegt bei rund 128, flaches Grau bei 0):
| 1px-Gitter bei echtem Analog-1080p | Std-Abw. | Bedeutung |
|---|---|---|
| horizontale Linien | 119 | vertikal sauber aufgelöst |
| vertikale Linien | 0,6 | horizontal komplett verschmiert |
| Schachbrett | 0,55 | weg |

Der Dongle hat also echte 1080 Zeilen vertikal, aber horizontal kommt weit weniger als 1920 Pixel an. Und weil hier kein Bridge mehr dazwischen sitzt: der Blur ist der Dongle selbst, nicht das billige Notebook. Das ist die belastbare Aussage.
Den Mechanismus habe ich auf einen Vorschlag aus dem Gegencheck hin gleich nachgemessen, ein Phasen- und Balkenbreiten-Test. Das 1px-Gitter ist bei Phase 0 und Phase 1 gleich grau (Std-Abw. 0,18 gegen 0,19), ein PLL- oder Phasen-Mislock ist damit ausgeschlossen, sonst würde der 1px-Versatz den Kontrast kippen. Und der Kontrast steigt sauber mit der Balkenbreite: 1px liegt bei rund 0, 2px bei 35, 3px bei 61, 4px bei 79 (Ideal rund 128). Das ist die klassische Tiefpass-Signatur, also ein echtes Horizontal-Auflösungs-Limit und kein Lock-Artefakt. Ob das an der ADC-Abtastrate oder an einem internen Rescale liegt, kann ich noch nicht trennen, aber beides heißt: der Dongle begrenzt die Horizontalauflösung.

Warum YUYV bei 1080p nur 5 fps macht
Zum Schluss noch die berüchtigte 5-fps-Grenze, diesmal gemessen statt aus der Tabelle abgeschrieben (Quelle GT 630 bei 1080p60, v4l2-ctl --stream-mmap):
| Format bei 1920×1080 | gemessen |
|---|---|
| MJPEG | 29 fps (Tabelle: 30) |
| YUYV (unkomprimiert) | exakt 5,0 fps |
Meine erste Erklärung mit „ungefähr 40 MB/s USB-2.0-Bandbreite“ war zu schludrig. Der genaue Grund ist das Payload-Limit des isochronen UVC-Endpoints. Das größte Altsetting des Video-Endpoints ist 3 mal 1024 Bytes pro Microframe, also 24,576 MB/s (steht so im lsusb -v). Unkomprimiertes YUYV bei 1080p sind 1920 mal 1080 mal 2 Bytes, also 4,15 MB pro Frame. Die Firmware-Frametabelle ist genau so gewählt, dass die YUYV-Raten knapp darunter passen:
- 1080p mal 5 fps = 20,7 MB/s
- 720p mal 10 fps = 18,4 MB/s
- 480p mal 30 fps = 20,7 MB/s
Also keine generische „USB-2.0-Bandbreite“, sondern der High-Speed-Isoch-Endpoint plus die fest verdrahteten Frametabellen. MJPEG komprimiert vorher, deshalb bleiben dort 30 fps bei 1080p und bis zu 60 bei 720p und darunter. Klare Ansage: am MS2109 ist MJPEG bei hoher Auflösung Pflicht. Und „USB 3.0″ auf der Verpackung ist und bleibt gelogen, das Ding enumeriert als USB-2.0-High-Speed, ohne SuperSpeed.
Stabiles Device-Naming per udev
Damit das Gerät im Alltag immer unter demselben Pfad auftaucht, eine udev-Regel. Die Regeln stehen bewusst jeweils auf einer Zeile, weil mehrzeilige Fortsetzungen mit Backslash schnell zur Fehlerquelle werden:
# /etc/udev/rules.d/70-vga-capture-ms2109.rules
SUBSYSTEM=="video4linux", ENV{ID_VENDOR_ID}=="534d", ENV{ID_MODEL_ID}=="2109", ENV{ID_V4L_PRODUCT}=="*AFN_Cap*", ATTR{index}=="0", SYMLINK+="video-vga"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="534d", ATTRS{idProduct}=="2109", ATTRS{product}=="USB3.0 Capture", GOTO="ms2109_vga_end"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="534d", ATTRS{idProduct}=="2109", SYMLINK+="hidraw-vga", MODE="0660", GROUP="plugdev"
LABEL="ms2109_vga_end"
Die GOTO-Konstruktion ist nötig, weil ATTRS{product}!="..." nicht so funktioniert, wie man denkt. udev sperrt die Attribut-Suche auf einen Parent-Device-Walk, und „Fehlen eines Attributs“ verhält sich anders als ein echtes Ungleich. Deshalb der Umweg über ein positives Match plus Sprungmarke.
Fazit, und der Workaround der wirklich hilft
Was bleibt? Der Dongle ist ein nettes Studienobjekt, aber für mein ursprüngliches Ziel, alte DOS-Signale aufnehmen, ist er unbrauchbar. Es gibt keine 70-Hz-Modi, und reparieren lässt sich das nicht, weil die Frame-Tabelle im Mask-ROM festgebrannt ist. Das „1080p“ ist horizontal Marketing, „USB 3.0″ eine glatte Lüge, und die EDID-Mod ist auf dem getesteten Analogpfad wirkungslos, weil der Dongle gar keine EDID treibt. Drei Korrektur-Momente gegenüber meinen ersten Annahmen, alle drei lehrreicher als wenn alles gleich funktioniert hätte.
Wer wirklich exotische VGA-Signale wie DOS-Textmodi sauber aufnehmen will, schaltet eine externe Scaler-Box vor. Mein Tipp ist GBS-Control, eine offene Firmware für die rund 20 Euro teuren GBS-8200-Boards. Sie nimmt das krumme Quellsignal, lockt sauber und gibt ein normgerechtes Bild aus, das jeder Capture-Stick frisst. Die andere Liga ist der Open Source Scan Converter, dessen Firmware-Update ich hier schon beschrieben habe. Wer es noch günstiger mag, fängt einen gebrauchten Extron-Scaler aus einer Konferenzraum-Ausmusterung.
Offen bleibt das Strings-Mysterium (ohne das Vendor-Tool komme ich an den Gate-Algorithmus nicht ran), die Disassembly des zweiten Patch-Blocks in Ghidra, ein Latenz-Benchmark für den Einsatz als KVM-Console-Viewer, und der finale DOS-Test mit GBS-Control davor. Material für einen zweiten Teil ist also reichlich da.
Siehe auch:
- Open Source Scan Converter: Firmware-Update von 1.08a auf 1.21 nachgeholt
- LCR-T4-Plus v2: m-firmware flashen, Display-Tuning und die 8-MHz-Quartz-Falle
- TC1 Multifunction Tester: Open-Source Firmware flashen, kalibrieren und die Stolperfallen dabei
Habt ihr selbst schon so einen MS2109-Stick auseinandergenommen oder das Strings-Gate geknackt? Dann lasst es mich gerne wissen, ihr dürft mich jederzeit fragen.


























































































