Im
letzten Artikel haben wir uns mit der Theorie des Managements von tausend kleinen Dingen im Redd-Komplex befasst, aber um das Volumen nicht zu vergrößern, haben wir die Übung beim nächsten Mal verschoben. Es ist an der Zeit, praktische Experimente durchzuführen. Diejenigen, die den Redd-Komplex nicht verwenden, werden in diesem Artikel auch nützliche Informationen finden, nämlich die Methodik zum Senden von Vendor-Befehlen an USB-Laufwerke von Linux, da der STM32-Controller im Komplex, wie bereits erwähnt, die Funktion eines SD-Lesegeräts übernimmt, d. H. fahren.

Laufwerksklassifizierung nach Befehlssystemen
Bei der Arbeit mit Laufwerken sollten Sie zwischen einer physischen Schnittstelle und einem Befehlssystem unterscheiden. Insbesondere CD / DVD / BD-Laufwerke und andere Optiken. Traditionell werden sie an ein SATA-Kabel (früher IDE) angeschlossen. Aber speziell auf dieser Leitung laufen im laufenden Betrieb nur PACKET-Befehle, in deren Datenblock nach einem völlig anderen Prinzip codierte Befehle abgelegt sind (von welchem werden wir gleich erfahren). Deshalb werden wir jetzt nicht so sehr über Drähte sprechen, sondern über die Teams, die in ihnen laufen. Ich kenne drei gängige Befehlssysteme für die Arbeit mit Antrieben.
- MMC Es wird von SD-Karten verstanden. Ehrlich gesagt ist dies für mich das mysteriöseste Befehlssystem. Wie man sie einreicht, scheint klar, aber wie man das Laufwerk verwaltet, ohne das Dokument sorgfältig zu lesen, das viele Übergangsdiagramme enthält - ich bin immer verwirrt. Zum Glück stört uns das heute nicht mehr, denn obwohl wir mit einer SD-Karte arbeiten, arbeitet der STM32-Controller im „Black-Box“ -Modus damit.
- ATA Diese Befehle wurden zunächst auf dem IDE-Bus und dann auf SATA ausgeführt. Ein wunderbares Befehlssystem, aber wir erwähnen heute auch nur, dass es existiert.
- SCSI Dieses Befehlssystem wird auf einer Vielzahl von Geräten verwendet. Betrachten Sie die Verwendung in Laufwerken. Dort laufen heute SCSI-Teams zunächst über die Leitungen des SAS-Busses (übrigens sind mittlerweile auch SSDs mit SAS-Schnittstelle in Mode). Seltsamerweise funktionieren optische Laufwerke, die physisch an den SATA-Bus angeschlossen sind, auch über SCSI-Befehle. Auf dem USB-Bus werden die Befehle auch im SCSI-Format abgelegt, wenn nach dem Massenspeichergerätestandard gearbeitet wird. Der STM32-Mikrocontroller ist über den USB-Bus mit dem Redd-Komplex verbunden. In unserem Fall folgen die Befehle dem folgenden Pfad:

Vom PC zum Controller über USB liegen die Befehle im SCSI-Format vor. Der Controller codiert die Befehle gemäß der MMC-Regel und sendet sie über den SDIO-Bus. Wir müssen aber ein Programm für den PC schreiben, damit die Teams uns im SCSI-Format lassen. Sie werden vom Massenspeichergerät-Gerätetreiber vorbereitet, mit dem wir über den Dateisystemtreiber kommunizieren. Ist es möglich, Anforderungen mit anderen Geräten für diese Anforderungen zu mischen? Lass es uns richtig machen.
Details zum SCSI-Befehlssystem
Wenn Sie sich der Sache formal nähern, finden Sie die Beschreibung des SCSI-Standards auf t10.org, aber wir sind realistisch. Niemand wird es freiwillig lesen. Genauer gesagt, nicht seine, sondern ihre: Es gibt einen ganzen Stapel offener Dokumente und einen Berg geschlossener Dokumente. Nur ein extremer Bedarf lässt Sie in die komplexe Sprache eintauchen, in der der Standard geschrieben ist (dies gilt übrigens für den ATA-Standard auf t13.org). Es ist viel einfacher, die Dokumentation für echte Laufwerke zu lesen. Es ist in einer lebendigeren Sprache geschrieben, und hypothetische, aber nicht wirklich verwendete Teile werden daraus herausgeschnitten. Während der Vorbereitung des Artikels stieß ich auf ein ziemlich neues Dokument (2016) aus dem
SCSI Commands Reference Manual von Seagate (direkter Link
www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf), aber wie immer Ich weiß nicht, wie lange sie leben wird. Ich denke, wenn jemand dieses Befehlssystem beherrschen möchte, sollte er mit diesem Dokument beginnen. Wir erinnern uns nur, dass SD-Lesegeräte eine noch kleinere Teilmenge der Befehle aus dieser Beschreibung implementieren.
Kurz gesagt, eine Befehlseinheit mit einer Länge von 6 bis 16 Bytes wird an das Laufwerk gesendet. Ein Datenblock kann entweder vom PC zum Laufwerk oder vom Laufwerk zum PC an den Befehlsblock angehängt werden (der SCSI-Standard ermöglicht auch den bidirektionalen Austausch, für das Massenspeichergerät über USB ist jedoch nur ein Block zulässig, was bedeutet, dass die Richtung nur einer ist). Im Anweisungsblock ist das erste Byte immer der Befehlscode. Die verbleibenden Bytes sind die Argumente. Die Regeln zum Ausfüllen der Argumente werden ausschließlich durch die Details der Implementierung des Befehls beschrieben.

Zuerst habe ich viele Beispiele in den Artikel eingefügt, aber dann habe ich festgestellt, dass sie das Lesen erschweren. Daher empfehle ich jedem, die Felder des Befehls READ CAPACITY (10) aus Tabelle 119 des Seigate-Dokuments und die Felder des Befehls READ (10) aus Tabelle 97 desselben Dokuments zu vergleichen (siehe Link oben). Wer keine Verbindung gefunden hat - sei nicht beunruhigt. Das wollte ich zeigen. Neben dem Feld „Befehl“ im Null-Byte hängt der Zweck aller Felder ausschließlich von den Besonderheiten eines bestimmten Befehls ab. Sie müssen das Dokument immer öffnen und den Zweck der verbleibenden Felder darin untersuchen.
Also:
- Um mit dem Antrieb zu kommunizieren, sollten Sie einen Befehlsblock mit einer Länge von 6 bis 16 Bytes bilden (abhängig vom Format des Befehls ist die genaue Anzahl in der Dokumentation dazu angegeben).
- Das wichtigste ist das Null-Byte des Blocks: Er legt den Befehlscode fest.
- Die verbleibenden Blockbytes haben keinen eindeutigen Zweck. Um zu verstehen, wie diese ausgefüllt werden, sollten Sie die Dokumentation für ein bestimmtes Team öffnen.
- Ein Datenblock, der zu oder von einem Laufwerk übertragen werden kann, kann an einen Befehl angehängt werden.
Eigentlich ist das alles. Wir haben die Regeln für die Ausgabe von SCSI-Befehlen kennengelernt. Jetzt können wir sie einreichen, es würde eine Dokumentation darüber geben. Aber wie geht das auf Betriebssystemebene?
Linux SCSI-Befehle
Suchen Sie nach dem Zielgerät
Öffnen Sie zum Ausgeben von Befehlen das Festplattengerät. Lass uns seinen Namen finden. Dazu gehen wir genauso vor wie im
Artikel über serielle Schnittstellen . Sehen wir uns die Liste der „Dateien“ im
Verzeichnis / dev an (denken Sie daran, dass auf Linux-Geräten auch Dateien angezeigt werden und ihre Liste mit demselben
ls-Befehl angezeigt wird).
Heute widmen wir uns dem virtuellen Verzeichnislaufwerk:

Wir schauen uns den Inhalt an:

Ein vertrauter Satz verschachtelter Verzeichnisse! Wir versuchen, das Verzeichnis
by-id mit der
Option –l des
Befehls ls zu betrachten , die uns bereits aus dem Artikel über serielle Schnittstellen bekannt ist:

Hervorgehobene Wörter sprechen für sich. Dies ist ein Laufwerk, das die interne SD-Karte des Redd-Komplexes enthält. Großartig! Jetzt wissen wir, dass das Gerät
MIR_Redd_Internal_SD dem Gerät
/ dev / sdb und / dev / sdb1 entspricht . Die, die ohne die Nummer das Laufwerk selbst ist, werden wir damit arbeiten, und mit der Nummer ist es das Dateisystem, das sich auf dem eingelegten Datenträger befindet.
Wenn Sie mit einer SD-Karte arbeiten, ist
/ dev / sdb das Lesegerät und
/ dev / sdb1 das Dateisystem der eingelegten Karte.
Funktion des Betriebssystems zum Ausgeben von Befehlen
In der Regel werden in jedem Betriebssystem alle nicht standardmäßigen Vorgänge mit Geräten durch direkte Anforderungen an den Treiber ausgeführt. Unter Linux steht die Funktion
ioctl () zum Senden solcher Anforderungen zur Verfügung. Unser Fall ist keine Ausnahme. Als Argument übergeben wir die in der Header-Datei sg.h beschriebene SG_IO-Anforderung.
Dort ist auch die Struktur
sg_io_hdr_t beschrieben , die die Anforderungsparameter enthält. Ich werde nicht die vollständige Struktur angeben, da nicht alle Felder ausgefüllt werden müssen. Ich werde nur das Wichtigste von ihnen geben:
typedef struct sg_io_hdr { int interface_id; /* [i] 'S' for SCSI generic (required) */ int dxfer_direction; /* [i] data transfer direction */ unsigned char cmd_len; /* [i] SCSI command length ( <= 16 bytes) */ unsigned char mx_sb_len; /* [i] max length to write to sbp */ unsigned short int iovec_count; /* [i] 0 implies no scatter gather */ unsigned int dxfer_len; /* [i] byte count of data transfer */ void * dxferp; /* [i], [*io] points to data transfer memory or scatter gather list */ unsigned char * cmdp; /* [i], [*i] points to command to perform */ unsigned char * sbp; /* [i], [*o] points to sense_buffer memory */ unsigned int timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */
Es macht keinen Sinn, Felder zu beschreiben, die in Kommentaren gut dokumentiert sind (
interface_id, dxfer_direction, timeout ). Der Artikel wächst bereits.
Das Feld
cmd_len enthält die Anzahl der Bytes im Befehlsblock, und
cmdp enthält einen Zeiger auf diesen Block. Sie können nicht auf einen Befehl verzichten, daher darf die Anzahl der Bytes nicht Null sein (von 6 bis 16).
Daten sind optional. Wenn dies der
Fall ist, wird die Länge des ausgewählten Puffers im Feld
dxfer_len und ein Zeiger darauf im Feld
dxferp angegeben. Ein Laufwerk kann physisch weniger Daten als die angegebene Puffergröße übertragen. Die Übertragungsrichtung wird im Feld
dxfer_direction angegeben. Gültige Werte für USB-Massenspeichergeräte sind:
SG_DXFER_NONE, SG_DXFER_TO_DEV, SG_DXFER_FROM_DEV . In der Header-Datei gibt es noch etwas, aber der Massenspeichergeräte-Standard erlaubt keine physische Implementierung.
Sie können auch die Rückgabe eines erweiterten Fehlercodes (
SENSE )
anfordern . Was es ist, ist im Abschnitt 2.4 des Segate-Dokuments zu finden. Die Länge des zugewiesenen Puffers wird im Feld
mx_sb_len angegeben , und der Zeiger auf den Puffer selbst wird im Feld
sbp angegeben .
Wie Sie sehen können, ist alles, worüber ich oben gesprochen habe, in dieser Struktur enthalten (und Sie können erweiterte Informationen über den Fehler erhalten). Weitere
Informationen zum Arbeiten mit
SG_IO- Anfragen finden Sie hier:
sg.danny.cz/sg/sg_io.htmlWir senden einen Standardbefehl an das Laufwerk
Nun, wir haben das Format des Befehls herausgefunden, wir haben herausgefunden, an welches Gerät es gesendet werden soll, wir haben herausgefunden, welche Funktion aufzurufen ist. Versuchen wir, einen Standardbefehl an unser Gerät zu senden. Sei dies der Befehl, um den Laufwerksnamen zu erhalten. So wird es im Sigeyt-Dokument beschrieben:

Bitte beachten Sie, dass gemäß der SCSI-Ideologie alle Felder in Standardbefehlen in der Big-Endian-Notation ausgefüllt sind, d. H. Dem höchsten Byte vorwärts. Deshalb füllen wir das Feld mit der Pufferlänge nicht im Format "0x80, 0x00", sondern im Gegenteil - "0x00, 0x80". Dies ist aber in Standardbefehlen. In Sonderfällen ist alles möglich, konsultieren Sie immer die Beschreibung. Eigentlich müssen nur der Befehlscode (
12h ) und die Länge ausgefüllt werden. Wir fordern eine Seite Null an, und die übrigen Felder sind entweder reserviert oder veraltet oder standardmäßig auf Null gesetzt. Also füllen Sie sie alle mit Nullen.
Wir machen ein Programm, das diesen Befehl gibt: #include <cstdio> #include <stdint.h> #include <string.h> #include <fcntl.h> // open #include <unistd.h> // close #include <sys/ioctl.h> #include <scsi/scsi.h> #include <scsi/sg.h> int main() { printf("hello from SdAccessTest!\n"); int s_fd = open("/dev/sdb", O_NONBLOCK | O_RDWR); if (s_fd < 0) { printf("Cannot open file\n"); return -1; } sg_io_hdr_t header; memset(&header;, 0, sizeof(header)); uint8_t cmd12h[] = { 0x12,0x00,0x00,0x00,0x80,0x00}; uint8_t data[0x80]; uint8_t sense[0x80]; header.interface_id = 'S'; // 'S' // header.cmd_len = sizeof(cmd12h); header.cmdp = cmd12h; // header.dxfer_len = sizeof(data); header.dxferp = data; header.dxfer_direction = SG_DXFER_TO_FROM_DEV; // header.mx_sb_len = sizeof(sense); header.sbp = sense; // header.timeout = 100; // 100 int res = ioctl(s_fd, SG_IO, &header;); close(s_fd); return 0; }
Wie man solche Programme auf einem entfernten Redd-Gerät ausführt, haben wir bereits in einem der
vorhergehenden Artikel besprochen. Richtig, ich habe beim ersten Start sofort einen Fehler beim Aufrufen der Funktion
open () erhalten . Es stellte sich heraus, dass der Benutzer standardmäßig nicht über ausreichende Rechte zum Öffnen von Plattengeräten verfügt. Welcher von mir ein Linux-Spezialist ist, habe ich oft geschrieben, aber im Netzwerk konnte ich feststellen, dass Sie zur Behebung dieses Problems die Zugriffsrechte auf das Gerät ändern können, indem Sie den folgenden Befehl eingeben:
sudo chmod 666 / dev / sdbMein Chef (und er ist ein großartiger Spezialist für dieses Betriebssystem) stellte später jedoch fest, dass die Lösung gültig ist, bis das Betriebssystem neu gestartet wird. Um die Rechte sicher zu erhalten, müssen Sie den Benutzer der
Datenträgergruppe hinzufügen.
Welchen dieser beiden Pfade wir auch gehen, setzen Sie, nachdem alles funktioniert hat, einen Haltepunkt auf die Zeile
close (s_fd); und überprüfen Sie die Ergebnisse zu dem Zeitpunkt, zu dem sie in der Entwicklungsumgebung erzielt werden (da das Programm nicht einmal eintägig ist, haben wir keine Zeit und Mühe, Mapper einzufügen, wenn die Entwicklungsumgebung uns alles zeigen kann). Der Wert von
res ist Null. Das Team hat also fehlerfrei gearbeitet.

Was ist in den Puffer gekommen? Als ich das Wort
data in die Adresse für den Speicherauszug eingab, wurde mir mitgeteilt, dass der Wert nicht berechnet werden konnte. Ich musste
& data eingeben
. . Es ist seltsam, denn
Daten sind ein Zeiger. Wenn Sie unter Windows debuggen, funktioniert alles, aber ich stelle nur fest, dass dies folgendermaßen funktioniert: Sehen Sie sich das so erhaltene Ergebnis an:

Richtig, sie haben uns den Namen und die Revision des Laufwerks zurückgegeben. Weitere Informationen zum Format der resultierenden Struktur finden Sie im Segate-Dokument (Abschnitt 3.6.2, Tabelle 59). Der Lesepuffer wurde nicht gefüllt, aber die IOCTL-Beschreibung der Anforderung besagt, dass er nur gefüllt wird, wenn ein Fehler auftritt, der etwas in diesem Puffer zurückgibt. Wörtlich:
Sense-Daten (werden nur verwendet, wenn 'status' CHECK CONDITION ist oder (driver_status & DRIVER_SENSE) true ist) .
Benutzerdefiniertes Befehlsformat für Redd Internal SD Drive
Nachdem wir nicht nur die trockene Beschreibung der Norm studiert haben, sondern auch alles in der Praxis ausprobiert haben und fühlen, was ein Befehlsblock ist, können wir bereits das Befehlsformat anzeigen, mit dem Sie nicht standardmäßige Funktionen aufrufen können, die der STM32-Steuerung auf der Platine des Komplexes "geflasht" werden. Ich habe den Befehlscode am Anfang des herstellerspezifischen Befehlsbereichs ausgewählt. Es ist gleich 0xC0. In den Beschreibungen der SCSI-Befehle wird
traditionell C0h geschrieben . Die Länge des Befehls beträgt immer 10 Bytes. Das Format des Teams ist einheitlich und in der folgenden Tabelle dargestellt.
Wie Sie sehen, werden die Argumente in der Little-Endian-Notation angegeben. Auf diese Weise können Sie den Befehl in Form einer Struktur beschreiben und direkt auf seine Felder zugreifen, ohne auf die Byte-Permutationsfunktion zurückgreifen zu müssen. Ausrichtungsprobleme (Doppelwörter in der Struktur haben Offsets, die nicht ein Vielfaches von vier sind) auf x86- und x64-Architekturen sind es nicht wert.
Unterbefehlscodes werden durch die folgende Aufzählung beschrieben:
enum vendorSubCommands { subCmdSdEnable = 0, // 00 Switch SD card to PC or Outside subCmdSdPower, // 01 Switch Power of SD card On/Off subCmdSdReinit, // 02 Reinitialize SD card (for example, after Power Cycle) subCmdSpiFlashEnable, // 03 Switch SPI Flash to PC or Outside subCmdSpiFlashWritePage, // 04 Write Page to SPI Flash subCmdSpiFlashReadPage, // 05 Read Page from SPI Flash subCmdSpiFlashErasePage,// 06 Erase Pages on SPI Flash (4K block) subCmdRelaysOn, // 07 Switch relays On by mask subCmdRelaysOff, // 08 Switch relays off by mask subCmdRelaysSet, // 09 Set state of all relays by data subCmdFT4222_1_Reset, // 0A Activate Reset State or switch chip to normal mode subCmdFT4222_2_Reset, // 0B Activate Reset State or switch chip to normal mode subCmdFT4222_3_Reset, // 0C Activate Reset State or switch chip to normal mode subCmdFT4232_Reset, // 0D Activate Reset State or switch chip to normal mode subCmdFT2232_Reset, // 0E Activate Reset State or switch chip to normal mode subCmdMAX3421_Reset, // 0F Activate Reset State or switch chip to normal mode subCmdFT4222_1_Cfg, // 10 Write to CFG pins of FT4222_1 subCmdFT4222_2_Cfg, // 11 Write to CFG pins of FT4222_2 subCmdFT4222_3_Cfg, // 12 Write to CFG pins of FT4222_3 };
Sie können in Gruppen eingeteilt werden.
Geräte in internen und externen Modus schalten
Die
Befehle subCmdSdEnable und
subCmdSpiFlashEnable schalten die SD-Karte bzw. den SPI-Flash um. Der Parameter
arg1 übergibt einen der folgenden Werte:
enum enableMode { enableModeToPC = 0, enableModeOutside };
Standardmäßig sind beide Geräte an einen PC angeschlossen.
Netzschalter
Das SDIO-Protokoll erfordert während der Initialisierung einige Manipulationen. Manchmal ist es nützlich, die SD-Karte in den Ausgangszustand zurückzusetzen (z. B. wenn die Leitungen zu einem externen Anschluss umgeschaltet werden). Schalten Sie dazu das Gerät aus und wieder ein. Dies kann mit dem
Befehl subCmdSdPower erfolgen . Im Argument
arg1 wird einer der folgenden Werte übergeben: 0 - Ausschalten, 1 - Einschalten. Denken Sie daran, Zeit zu geben, um die Kondensatoren auf der Stromleitung zu entladen.
Nach dem Einschalten sollte die Karte, sofern sie mit dem PC verbunden ist, neu initialisiert werden. Verwenden Sie dazu den Befehl
subCmdSdReinit (er hat keine Argumente).
Arbeiten Sie mit dem SPI-Flash-Laufwerk
Wenn die SD-Karte als volles Laufwerk an das System angeschlossen ist, ist der Zugriffschip in der aktuellen Version sehr begrenzt. Sie können jeweils nur auf die einzelnen Seiten (256 Byte) zugreifen. Die Speicherkapazität des Mikrokreislaufs ist so bemessen, dass der Vorgang selbst bei der Arbeit an der Seite nicht viel Zeit in Anspruch nimmt. Dieser Ansatz vereinfacht jedoch die „Firmware“ des Mikrocontrollers erheblich.
Der Befehl
subCmdSpiFlashReadPage liest die Seite. Die Adresse wird im Parameter arg1 angegeben, die Anzahl der zu sendenden Seiten im Parameter arg2. In der aktuellen Version sollte die Anzahl der Seiten jedoch gleich eins sein. Der Befehl gibt 256 Datenbytes zurück.
Für sie ist der Befehl
subCmdSpiFlashWritePage gespiegelt. Argumente für sie werden nach dem gleichen Prinzip ausgefüllt. Die Richtung der Datenübertragung ist zum Gerät.
Die Besonderheit des Flash-Speichers besteht darin, dass während der Aufzeichnung nur einzelne Bits durch Null-Bits ersetzt werden können. Um sie auf einen einzelnen Wert zurückzusetzen, sollten die Seiten gelöscht werden.
Hierfür gibt es einen Befehl
subCmdSpiFlashErasePage . Aufgrund der Eigenschaften der verwendeten Mikroschaltung wird zwar keine einzelne Seite, die im Parameter
arg1 festgelegt ist, gelöscht, sondern ein 4-Kilobyte-Block, der diese Seite enthält.
Solid State Relay Management
Der Komplex verfügt über sechs Halbleiterrelais. Es gibt drei Teams, die sie verwalten.
subCmdRelaysSet - legt den Wert aller sechs Relais gleichzeitig fest. Im Parameter
arg1 wird ein Wert übergeben, von dem jedes Bit seinem eigenen Relais entspricht (Nullbit - Relais mit Index 0, erstes Bit mit Index 1 usw.). Ein einziger Bitwert schließt das Relais, ein Nullwert öffnet es.
Diese Arbeitsweise ist gut, wenn alle Relais als eine Gruppe arbeiten. Wenn sie unabhängig voneinander arbeiten, müssen Sie bei diesem Ansatz eine Puffervariable starten, die den Statuswert aller Relais speichert. Wenn verschiedene Relais von verschiedenen Programmen gesteuert werden, wird das Problem des Speicherns des Gesamtwerts extrem akut. In diesem Fall können Sie zwei weitere Befehle verwenden:
subCmdRelaysOn -
Aktiviert ausgewählte Relais nach Maske. Die Relais, die den Einheitenbits im Argument
arg1 entsprechen, werden aktiviert. Die Relais, die Nullen in der Maske entsprechen, behalten ihren aktuellen Status bei.
Der Befehl
subCmdRelaysOff, der es spiegelt, schaltet die ausgewählten Relais über die Maske aus. Die Relais, die den einzelnen Bits im Argument
arg1 entsprechen , werden ausgeschaltet. Die Relais, die Nullen in der Maske entsprechen, behalten ihren aktuellen Status bei.
Setzen Sie die FTDI- und Maxim-Controller zurück
Um Rücksetzsignale an FTDI- und Maxim-Mikroschaltungen zu senden, wird die Befehlsgruppe
subCmdFT4222_1_Reset ,
subCmdFT4222_2_Reset ,
subCmdFT4222_3_Reset ,
subCmdFT4232_Reset ,
subCmdFT2232_Reset und
subCmdMAX3421_ verwendet . An ihren Namen können Sie erkennen, welche Chips sie durch Rücksetzsignale steuern. Die FT4222-Brücken sind, wie bereits erwähnt, zwei in der Schaltung (ihre Indizes sind 1 und 2). Eine weitere FT4222-Brücke überträgt Daten an den MAX3421-Chip, auf die wir im nächsten Artikel eingehen werden.
Der Parameter
arg1 übergibt einen der folgenden Werte:
enum ResetState { resetStateActive =0, resetStateNormalOperation };
Standardmäßig sind alle Brücken in einem normalen Betriebszustand. Wie bereits in einem
vorherigen Artikel erwähnt , sind wir uns nicht sicher, ob diese Funktionalität benötigt wird. Wenn jedoch kein direkter Zugriff auf das Gerät besteht, ist es besser, alles und jedes remote zurücksetzen zu können.
Konfigurationszeilen von FT4222-Chips wechseln
FT4222-Chips haben vier Modi. Es ist unwahrscheinlich, dass jemand einen anderen Modus als "00" benötigt. Wenn Sie ihn jedoch plötzlich benötigen, können Sie die
Befehle subCmdFT4222_1_Cfg ,
subCmdFT4222_2_Cfg und
subCmdFT4222_3_Cfg zum
Umschalten für den ersten, zweiten und dritten
Chip verwenden . Der Wert der Zeilen CFG0 und CFG1 wird in den unteren beiden Bits des Parameters
arg1 eingestellt .
Praktische Erfahrung in der Ausgabe von Befehlen an die STM32-Steuerung
Um das theoretische Material in der Praxis zu testen, werden wir versuchen, die SD-Karte auszutauschen.
Setzen Sie dazu den Befehl
subCmdSdEnable mit dem Code 0x00 und dem Argument
enableModeOutside mit dem Code 0x01 ab. Großartig. Wir haben das Programm wie folgt umgeschrieben.
Umgeschriebenes Programm: #include <cstdio> #include <stdint.h> #include <string.h> #include <fcntl.h> // open #include <unistd.h> // close #include <sys/ioctl.h> #include <scsi/scsi.h> #include <scsi/sg.h> int main() { printf("hello from SdAccessTest!\n"); int s_fd = open("/dev/sdb", O_NONBLOCK | O_RDWR); if (s_fd < 0) { printf("Cannot open file\n"); return -1; } sg_io_hdr_t header; memset(&header;, 0, sizeof(header)); uint8_t cmdSdToOutside[] = { 0xC0,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; uint8_t cmdSdToPC[] = { 0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; uint8_t sense[32]; memset(sense, 0, sizeof(sense)); header.interface_id = 'S'; // 'S' // header.cmd_len = sizeof(cmdSdToOutside); header.cmdp = cmdSdToOutside; // ( ) header.dxfer_len = 0; header.dxferp = 0; header.dxfer_direction = SG_DXFER_NONE; // header.mx_sb_len = sizeof(sense); header.sbp = sense; // header.timeout = 100; // 100 int res = ioctl(s_fd, SG_IO, &header;); // header.cmdp = cmdSdToPC; res = ioctl(s_fd, SG_IO, &header;); close(s_fd); return 0; }
Wir haben die Befehlslänge auf zehn Bytes geändert und den Datenblock entfernt. Nun, sie haben den Befehlscode nach Bedarf mit Argumenten aufgeschrieben. Ansonsten bleibt alles beim Alten. Wir fangen an ... und ... nichts funktioniert. Die Funktion
ioctl () gibt einen Fehler zurück. Der Grund ist im
SG_IO- Befehlsdokument beschrieben. Tatsache ist, dass wir den herstellerspezifischen Befehl
C0h geben und Folgendes wörtlich über sie gesagt wird:
Jeder andere SCSI-Befehl (Opcode), der nicht für den SG-Treiber angegeben wurde, benötigt O_RDWR. Jeder andere SCSI-Befehl (Opcode), der nicht für die Blockschicht SG_IO ioctl erwähnt wird, benötigt einen Benutzer mit der Fähigkeit CAP_SYS_RAWIO.
Wie der Chef mir erklärte (ich erzähle nur seine Worte),
werden die Werte der
Fähigkeiten einer ausführbaren Datei zugewiesen. Aus diesem Grund musste ich mich von der Entwicklungsumgebung aus als
root anmelden . Nicht die beste Lösung, aber zumindest etwas. Unter Windows sind für die Anforderung
IOCTL_SCSI_PASS_THROUGH_DIRECT Administratorrechte erforderlich. Vielleicht gibt jemand in den Kommentaren Ratschläge, wie man das Tracing-Problem ohne solch drastische Schritte löst, aber Sie können das bereits geschriebene Programm ohne
root ausführen, wenn Sie die richtigen
Fähigkeiten dafür registrieren. Ändern Sie in der Zwischenzeit den Benutzernamen in der Entwicklungsumgebung und setzen Sie einen Haltepunkt in der Zeile:
int res = ioctl(s_fd, SG_IO, &header;);
Bevor wir die Funktion
ioctl () aufrufen, sehen wir uns die Liste der Speichergeräte an:

Rufen Sie
ioctl () auf und sehen Sie sich die Liste noch einmal an:

Das Gerät
/ dev / sdb ist geblieben (grob gesagt, das ist der SD-Kartenleser selbst), und
/ dev / sdb1 ist verschwunden. Dieses Gerät entspricht dem Dateisystem auf dem Datenträger. Der Träger wurde vom Computer getrennt - er war nicht mehr sichtbar. Wir verfolgen weiter. Nach dem Aufruf der zweiten
ioctl () -Funktion sehen wir uns noch einmal die Liste der Geräte an:

Die SD-Karte wird erneut mit dem System verbunden, sodass
/ dev / sdb1 wieder in Position ist. Tatsächlich haben wir gelernt, wie man herstellerspezifische Befehle ausgibt und ein Gerät verwaltet, das auf dem STM32-Mikrocontroller im Redd-Komplex basiert. Andere Befehle werden den Lesern zum unabhängigen Studium überlassen. Sie können den Betrieb einiger von ihnen auf ähnliche Weise steuern. Wenn ein
ftdi- Chip in einen Reset-Zustand wechselt, wird das entsprechende Gerät aus dem System entfernt. Der Betrieb des Relais und die Steuerung der Beine der Konfiguration müssen durch Messinstrumente gesteuert werden. Sie können die Arbeit mit einem Flash-Laufwerk überprüfen, indem Sie Seiten mit anschließender Lesesteuerung schreiben.
Fazit
Wir haben zwei große Themen untersucht, die nicht mit FPGAs im Redd-Komplex zusammenhängen. Der dritte blieb bestehen - er arbeitete mit dem MAX3421-Chip, mit dem USB 2.0 FS-Geräte implementiert werden können. In der Tat gibt es auch Hosts, aber es gibt viele Hosts und das Motherboard. Die Funktionalität des Geräts ermöglicht es dem Komplex, sich als USB-Flash-Laufwerk (zum Senden von Firmware-Updates), USB-Tastatur (zum Steuern externer Geräte) usw. auszugeben. Wir werden dieses Thema im nächsten Artikel betrachten.