S'entraîner à travailler avec des pneus personnalisés du complexe Redd

Dans le dernier article, nous avons examiné la théorie de la gestion de mille petites choses dans le complexe Redd, mais afin de ne pas gonfler le volume, nous avons reporté la pratique la prochaine fois. Le moment est venu de mener des expériences pratiques. Ceux qui n'utilisent pas le complexe Redd pourront également trouver des connaissances utiles dans cet article, à savoir la méthodologie pour envoyer des commandes de fournisseur aux lecteurs USB à partir de Linux, car, comme déjà mentionné, le contrôleur STM32 du complexe remplit la fonction d'un lecteur SD, c'est-à-dire, conduire.




Classification des disques par systèmes de commande


Lorsque vous travaillez avec des disques, vous devez faire la distinction entre une interface physique et un système de commande. En particulier, les lecteurs CD / DVD / BD et autres optiques. Traditionnellement, ils se connectent à un câble SATA (anciennement IDE). Mais spécifiquement sur ce fil, seules les commandes PACKET s'exécutent en cours de fonctionnement, dans le bloc de données desquelles sont placées des commandes codées selon un principe complètement différent (nous allons bientôt découvrir par laquelle). Par conséquent, maintenant, nous ne parlerons pas tant des câbles, mais des équipes qui les exécutent. Je connais trois systèmes de commande courants pour travailler avec des disques.

  • MMC Il est compris par les cartes SD. Honnêtement, c'est pour moi le système de commandement le plus mystérieux. La façon de les soumettre, semble-t-il, est claire, mais comment gérer le lecteur sans lire attentivement le document contenant beaucoup de graphiques de transition - je suis toujours confus. Heureusement, cela ne nous dérange pas aujourd'hui, car bien que nous travaillions avec une carte SD, le contrôleur STM32 en mode «boîte noire» fonctionne avec.
  • ATA Initialement, ces commandes s'exécutaient sur le bus IDE, puis sur SATA. Un merveilleux système de commandement, mais aujourd'hui, nous mentionnons également qu'il existe.
  • SCSI Ce système de commande est utilisé sur une large gamme d'appareils. Considérez son utilisation dans les lecteurs. Aujourd'hui, les équipes SCSI courent tout d'abord le long des câbles du bus SAS (d'ailleurs, même les SSD avec interface SAS sont désormais à la mode). Curieusement, les lecteurs optiques physiquement connectés au bus SATA fonctionnent également via des commandes SCSI. Sur le bus USB lorsque vous travaillez selon la norme du périphérique de stockage de masse, les commandes sont également transmises au format SCSI. Le microcontrôleur STM32 est connecté au complexe Redd via le bus USB, c'est-à-dire que, dans notre cas, les commandes suivent le chemin suivant:



Du PC au contrôleur, via USB, les commandes sont au format SCSI. Le contrôleur transcode les commandes selon la règle MMC et les envoie via le bus SDIO. Mais nous devons écrire un programme pour le PC, donc les équipes nous laissent au format SCSI. Ils sont préparés par le pilote de périphérique du périphérique de stockage de masse, avec lequel nous communiquons via le pilote du système de fichiers. Est-il possible de mélanger des demandes avec d'autres appareils pour ces demandes? Faisons les choses correctement.

Détails du système de commande SCSI


Si vous abordez la question de manière formelle, la description de la norme SCSI est disponible sur t10.org, mais nous serons réalistes. Personne ne le lira volontairement. Plus précisément, pas le sien, mais le leur: il y a tout un tas de documents ouverts et une montagne de documents fermés. Seul un besoin extrême vous fera vous immerger dans le langage complexe dans lequel la norme est écrite (ceci, soit dit en passant, s'applique à la norme ATA sur t13.org). Il est beaucoup plus facile de lire la documentation des vrais disques. Il est écrit dans une langue plus vivante, et des parties hypothétiques mais pas vraiment utilisées en sont découpées. Lors de la préparation de l'article, je suis tombé sur un document plutôt nouveau (2016) du manuel de référence des commandes SCSI de Seagate (lien direct www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf mais, comme toujours, Je ne sais pas combien de temps elle vivra). Je pense que si quelqu'un veut maîtriser ce système de commandes, il devrait commencer par ce document. Nous nous souvenons seulement que les lecteurs SD implémentent un sous-ensemble encore plus petit des commandes de cette description.

Pour parler très brièvement, une unité de commande d'une longueur de 6 à 16 octets est envoyée au variateur. Un bloc de données peut être attaché au bloc de commande soit du PC vers le lecteur, soit du lecteur vers le PC (la norme SCSI autorise également l'échange bidirectionnel, mais pour le périphérique de stockage de masse via USB, un seul bloc est autorisé, ce qui signifie que la direction n'est qu'un). Dans le bloc d'instructions, le premier octet est toujours le code de commande. Les octets restants sont ses arguments. Les règles de remplissage des arguments sont décrites exclusivement par les détails de l'implémentation de la commande.



Au début, j'ai inséré beaucoup d'exemples dans l'article, mais j'ai ensuite réalisé qu'ils rendaient la lecture difficile. Par conséquent, je suggère à chacun de comparer les champs de la commande READ CAPACITY (10) du tableau 119 du document Seigate et les champs de la commande READ (10) du tableau 97 du même document (voir lien ci-dessus). Qui n'a trouvé aucune connexion - ne vous inquiétez pas. Voilà ce que je voulais montrer. En plus du champ «commande» dans l'octet zéro, le but de tous les champs dépend uniquement des spécificités d'une commande particulière. Vous devez toujours ouvrir le document et étudier la fonction des autres champs qu'il contient.

Donc:

  • Pour communiquer avec le lecteur, vous devez former un bloc de commande d'une longueur de 6 à 16 octets (selon le format de la commande, le nombre exact est indiqué dans la documentation correspondante).
  • Le plus important est l'octet zéro du bloc: c'est lui qui définit le code de commande.
  • Les octets de bloc restants n'ont pas un objectif clair. Pour comprendre comment les remplir, vous devez ouvrir la documentation d'une équipe spécifique.
  • Un bloc de données qui peut être transféré vers ou depuis un lecteur peut être attaché à une commande.

En fait, c'est tout. Nous avons appris les règles d'émission des commandes SCSI. Nous pouvons maintenant les soumettre, il y aurait de la documentation à leur sujet. Mais comment le faire au niveau du système d'exploitation?

Commandes Linux SCSI


Rechercher le périphérique cible


Pour émettre des commandes, ouvrez le périphérique de disque. Trouvons son nom. Pour ce faire, nous procéderons exactement de la même manière que dans l' article sur les ports série . Voyons la liste des «fichiers» dans le répertoire / dev (rappelez-vous que sur les périphériques Linux sont également affichés sous forme de fichiers et leur liste est affichée avec la même commande ls ).

Aujourd'hui, nous prêtons attention au disque du répertoire virtuel:



Nous regardons son contenu:



Un ensemble familier de répertoires imbriqués! Nous essayons de considérer le répertoire by-id , en utilisant le commutateur –l de la commande ls , que nous connaissons déjà dans l'article sur les ports série:



Les mots surlignés parlent d'eux-mêmes. Il s'agit d'un lecteur contenant la carte SD interne du complexe Redd. Super! Nous savons maintenant que le périphérique MIR_Redd_Internal_SD correspond au périphérique / dev / sdb et / dev / sdb1 . Celui sans le numéro est le lecteur lui-même, nous travaillerons avec lui, et avec le numéro, c'est le système de fichiers situé sur le support inséré. En termes de travail avec une carte SD, / dev / sdb est le lecteur et / dev / sdb1 est le système de fichiers sur la carte insérée.

Fonction du système d'exploitation pour l'émission des commandes


Habituellement, dans n'importe quel système d'exploitation, toutes les choses non standard avec les périphériques sont effectuées via des demandes directes au pilote. Sous Linux, la fonction ioctl () est disponible pour envoyer de telles requêtes. Notre cas ne fait pas exception. Comme argument, nous transmettons la requête SG_IO décrite dans le fichier d'en-tête sg.h. La structure sg_io_hdr_t contenant les paramètres de requête y est également décrite. Je ne donnerai pas la structure complète, car tous ses champs ne doivent pas être remplis. Je ne donnerai que les plus importants d'entre eux:

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) */ 

Cela n'a aucun sens de décrire les champs qui sont bien documentés dans les commentaires ( interface_id, dxfer_direction, timeout ). L'article s'agrandit déjà.

Le champ cmd_len contient le nombre d'octets dans le bloc de commandes et cmdp contient un pointeur vers ce bloc. Vous ne pouvez pas vous passer d'une commande, le nombre d'octets doit donc être différent de zéro (de 6 à 16).

Les données sont facultatives. Si tel est le cas, la longueur du tampon sélectionné est spécifiée dans le champ dxfer_len et un pointeur vers celui-ci est spécifié dans le champ dxferp . Un lecteur peut physiquement transférer moins de données que la taille de tampon spécifiée. Le sens de transmission est spécifié dans le champ dxfer_direction . Les valeurs de périphérique de stockage de masse USB valides sont: SG_DXFER_NONE, SG_DXFER_TO_DEV, SG_DXFER_FROM_DEV . Il y a encore une chose dans le fichier d'en-tête, mais la norme du périphérique de stockage de masse ne permet pas de l'implémenter physiquement.

Vous pouvez également demander le retour d'un code d'erreur étendu ( SENSE ). Ce que c'est peut être trouvé dans le document Segate, section 2.4. La longueur du tampon alloué est indiquée dans le champ mx_sb_len et le pointeur vers le tampon lui-même est indiqué dans le champ sbp .

Comme vous pouvez le voir, tout ce dont j'ai parlé ci-dessus est rempli dans cette structure (en plus, vous pouvez obtenir des informations détaillées sur l'erreur). En savoir plus sur l' utilisation de la demande SG_IO ici: sg.danny.cz/sg/sg_io.html

Nous envoyons une commande standard au lecteur


Eh bien, nous avons déterminé le format de la commande, nous avons déterminé à quel appareil l'envoyer, nous avons déterminé la fonction à appeler. Essayons d'envoyer une commande standard à notre appareil. Que ce soit la commande pour obtenir le nom du lecteur. Voici comment cela est décrit dans le document Sigeyt:



Veuillez noter que selon l'idéologie SCSI, tous les champs des commandes standard sont remplis en notation Big Endian, c'est-à-dire l'octet le plus élevé en avant. Par conséquent, nous remplissons le champ avec la longueur du tampon non pas au format "0x80, 0x00", mais au contraire - "0x00, 0x80". Mais c'est dans les commandes standard. En non standard tout est possible, vous devriez toujours consulter la description. En fait, seul le code de commande ( 12h ) et la longueur que nous devons remplir. Nous demanderons une page zéro, et les champs restants sont soit réservés, soit obsolètes, soit par défaut à zéro. Alors remplissez-les tous de zéros.

Nous faisons un programme qui donne cette commande:
 #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; } 



Comment exécuter de tels programmes sur un périphérique Redd distant, nous avons déjà discuté dans l'un des articles précédents . Certes, en le démarrant pour la première fois, j'ai immédiatement reçu une erreur lors de l'appel de la fonction open () . Il s'est avéré que l'utilisateur par défaut ne dispose pas de droits suffisants pour ouvrir des périphériques de disque. Lequel de moi est un spécialiste Linux, j'ai écrit plusieurs fois, mais sur le réseau, j'ai réussi à trouver que pour résoudre ce problème, vous pouvez changer les droits d'accès à l'appareil en émettant la commande:

sudo chmod 666 / dev / sdb

Cependant, mon patron (et il est un grand spécialiste de ce système d'exploitation) a noté plus tard que la solution était valide jusqu'au redémarrage du système d'exploitation. Pour obtenir les droits à coup sûr, vous devez ajouter l'utilisateur au groupe de disques .

Quel que soit le chemin emprunté , mais après que tout a fonctionné, mettez un point d'arrêt sur la ligne close (s_fd); et inspecter les résultats au moment où il est atteint dans l'environnement de développement (puisque le programme n'est même pas d'une journée, ce qui signifie que nous n'avons pas le temps de consacrer du temps et des efforts à l'insertion de mappeurs si l'environnement de développement peut tout nous montrer). La valeur de res est nulle. L'équipe a donc travaillé sans erreur.



Qu'est-il arrivé au tampon? Quand j'ai entré le mot data dans l'adresse du vidage, ils m'ont dit qu'ils ne pouvaient pas calculer la valeur, j'ai dû entrer & data; . C'est étrange, car les données sont un pointeur, lors du débogage sous Windows, tout fonctionne, mais je note juste ce fait, cela fonctionne comme ceci: regardez le résultat obtenu comme ceci:



C'est vrai, ils nous ont retourné le nom et la révision du lecteur. Vous trouverez plus d'informations sur le format de la structure résultante dans le document Segate (section 3.6.2, tableau 59). Le tampon de détection ne s'est pas rempli, mais la description IOCTL de la demande indique qu'il n'est rempli que lorsqu'une erreur se produit qui renvoie quelque chose dans ce tampon. Littéralement: données de détection (utilisées uniquement lorsque «status» est CHECK CONDITION ou (driver_status & DRIVER_SENSE) est true) .

Format de commande personnalisé pour le lecteur SD interne Redd


Maintenant que nous avons non seulement étudié la description sèche de la norme, mais que nous avons également tout essayé dans la pratique, ressentant ce qu'est un bloc de commande, nous pouvons déjà montrer le format de commande avec lequel vous pouvez appeler des fonctions non standard qui sont «flashées» sur le contrôleur STM32 sur la carte du complexe. J'ai sélectionné le code de commande depuis le début de la gamme de commandes spécifiques au fournisseur . Il est égal à 0xC0. Traditionnellement, dans les descriptions des commandes SCSI, écrivez C0h . La longueur de la commande est toujours de 10 octets. Le format de l'équipe est unifié et présenté dans le tableau ci-dessous.

OctetRendez-vous
0Code de commande C0h
1Code de sous-commande
2Argument arg1. Défini en notation Little Endian (octet bas en avant)
3
4
5
6Argument arg2. Défini en notation Little Endian (octet bas en avant)
7
8
9

Comme vous pouvez le voir, les arguments sont donnés dans la notation Little Endian. Cela vous permettra de décrire la commande sous la forme d'une structure et d'accéder directement à ses champs, sans recourir à la fonction de permutation d'octets. Les problèmes d'alignement (les mots doubles dans la structure ont des décalages qui ne sont pas multiples de quatre) sur les architectures x86 et x64 n'en valent pas la peine.

Les codes de sous-commande sont décrits par l'énumération suivante:
 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 }; 

Ils peuvent être divisés en groupes.

Passage des appareils en modes interne et externe


Les commandes subCmdSdEnable et subCmdSpiFlashEnable commutent respectivement la carte SD et le flash SPI. Le paramètre arg1 transmet l'une des valeurs suivantes:

 enum enableMode { enableModeToPC = 0, enableModeOutside }; 

Par défaut, les deux appareils sont connectés à un PC.

Commutation de puissance


Le protocole SDIO nécessite pas mal de manipulations lors de l'initialisation. Parfois, il est utile de réinitialiser la carte SD à son état initial (par exemple, lors du basculement de ses lignes vers un connecteur externe). Pour ce faire, éteignez-le, puis rallumez-le. Cela peut être fait à l'aide de la commande subCmdSdPower . Dans l'argument arg1, l' une des valeurs suivantes est transmise: 0 - mise hors tension, 1 - mise sous tension. N'oubliez pas de donner le temps de décharger les condensateurs sur la ligne électrique.

Après la mise sous tension, la carte, si elle est connectée au PC, doit être réinitialisée. Pour ce faire, utilisez la commande subCmdSdReinit (elle n'a pas d'arguments).

Travailler avec le lecteur flash SPI


Si la carte SD est connectée au système en tant que lecteur complet, la puce d'accès dans la version actuelle est assez limitée. Vous ne pouvez accéder qu'à ses pages individuelles (256 octets) et une seule à la fois. La quantité de mémoire dans le microcircuit est telle que même lorsque vous travaillez sur la page, le processus ne prendra pas beaucoup de temps de toute façon, mais cette approche simplifie considérablement le «firmware» du microcontrôleur.

La commande subCmdSpiFlashReadPage lit la page. L'adresse est spécifiée dans le paramètre arg1, le nombre de pages à transmettre dans le paramètre arg2. Mais dans la version actuelle, le nombre de pages doit être égal à un. La commande renverra 256 octets de données.

La commande subCmdSpiFlashWritePage est en miroir pour elle. Les arguments pour elle sont complétés par le même principe. La direction du transfert de données est vers l'appareil.

La particularité de la mémoire flash est que seuls des bits simples peuvent être remplacés par des bits zéro pendant l'enregistrement. Pour les ramener à une seule valeur, les pages doivent être effacées. Il existe une commande subCmdSpiFlashErasePage pour cela . Certes, en raison des caractéristiques du microcircuit utilisé, ce n'est pas une seule page définie dans le paramètre arg1 qui est effacée, mais un bloc de 4 kilo-octets le contenant.

Gestion des relais statiques


Le complexe dispose de six relais statiques. Il y a trois équipes pour les gérer.

subCmdRelaysSet - définit la valeur des six relais simultanément. Dans le paramètre arg1, une valeur est transmise, chaque bit correspondant à son propre relais (bit zéro - relais avec index 0, premier bit avec index 1, etc.). Une seule valeur de bit ferme le relais, une valeur nulle provoque son ouverture.

Cette méthode de fonctionnement est bonne lorsque tous les relais fonctionnent comme un seul groupe. S'ils fonctionnent indépendamment les uns des autres, avec cette approche, vous devez démarrer une variable tampon qui stocke la valeur d'état de tous les relais. Si différents relais sont contrôlés par différents programmes, le problème du stockage de la valeur agrégée devient extrêmement aigu. Dans ce cas, vous pouvez utiliser deux autres commandes:

subCmdRelaysOn - active les relais sélectionnés par masque. Les relais qui correspondent aux bits unitaires dans l'argument arg1 seront activés. Les relais qui correspondent à des zéros dans le masque conserveront leur état actuel.

La commande subCmdRelaysOff en miroir désactivera les relais sélectionnés par masque. Les relais qui correspondent aux bits simples de l'argument arg1 seront désactivés. Les relais qui correspondent à des zéros dans le masque conserveront leur état actuel.

Réinitialiser les contrôleurs FTDI et Maxim


Pour envoyer des signaux de réinitialisation aux microcircuits FTDI et Maxim, le groupe de commandes subCmdFT4222_1_Reset , subCmdFT4222_2_Reset , subCmdFT4222_3_Reset , subCmdFT4232_Reset , subCmdFT2232_Reset et subCmdMAX3421_ . De leurs noms, vous pouvez voir les puces qu'ils contrôlent par des signaux de réinitialisation. Les ponts FT4222, comme nous l'avons considéré précédemment, sont deux dans le circuit (leurs indices sont 1 et 2), un autre pont FT4222 transfère les données à la puce MAX3421, que nous examinerons dans l'article suivant.

Le paramètre arg1 transmet l'une des valeurs suivantes:

 enum ResetState { resetStateActive =0, resetStateNormalOperation }; 

Par défaut, tous les ponts sont en état de fonctionnement normal. Comme déjà indiqué dans un article précédent , nous ne savons pas nous-mêmes si cette fonctionnalité est nécessaire, mais lorsqu'il n'y a pas d'accès direct à l'appareil, il est préférable de pouvoir réinitialiser à distance tout et tout.

Commutation des lignes de configuration des puces FT4222


Les puces FT4222 ont quatre modes. Il est peu probable que quelqu'un ait besoin d'un mode autre que "00", mais si vous en avez soudainement besoin, vous pouvez utiliser les commandes subCmdFT4222_1_Cfg , subCmdFT4222_2_Cfg et subCmdFT4222_3_Cfg pour commuter les première, deuxième et troisième puces. La valeur des lignes CFG0 et CFG1 est définie dans les deux bits inférieurs du paramètre arg1 .

Expérience pratique dans l'émission de commandes vers le contrôleur STM32


Pour tester le matériel théorique obtenu dans la pratique, nous allons essayer d'éteindre la carte SD. Pour ce faire, exécutez la commande subCmdSdEnable avec le code 0x00 avec l'argument enableModeOutside avec le code 0x01. Super. Nous réécrivons le programme à partir de l'expérience passée comme suit.

Programme réécrit:
 #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; } 


Nous avons modifié la longueur de la commande à dix octets et supprimé le bloc de données. Eh bien, ils ont écrit le code de commande avec des arguments, comme requis. Sinon, tout reste le même. Nous commençons ... Et ... Rien ne fonctionne. La fonction ioctl () renvoie une erreur. La raison est décrite dans le document de commande SG_IO . Le fait est que nous donnons la commande spécifique au vendeur C0h , et ce qui suit est dit à leur sujet littéralement:
Toute autre commande SCSI (opcode) non mentionnée pour le pilote sg nécessite O_RDWR. Toute autre commande SCSI (opcode) non mentionnée pour la couche de bloc SG_IO ioctl a besoin d'un utilisateur avec la capacité CAP_SYS_RAWIO.

Comme le patron m'a expliqué (je ne fais que répéter ses mots), des valeurs de capacités sont affectées à un fichier exécutable. Pour cette raison, j'ai dû tracer à partir de l'environnement de développement en me connectant en tant que root . Pas la meilleure solution, mais au moins quelque chose. En fait, sous Windows, la demande IOCTL_SCSI_PASS_THROUGH_DIRECT nécessite également des droits d'administrateur. Peut-être que dans les commentaires, quelqu'un donnera des conseils sur la façon de résoudre le problème de traçage sans ces étapes drastiques, mais vous pouvez exécuter le programme déjà écrit sans root , si vous enregistrez les capacités appropriées pour cela . En attendant, changez le nom d'utilisateur dans l'environnement de développement et définissez un point d'arrêt sur la ligne:

 int res = ioctl(s_fd, SG_IO, &header;); 

et avant d'appeler la fonction ioctl () , nous regardons la liste des périphériques de stockage:



Appelez ioctl () et regardez à nouveau la liste:



Le périphérique / dev / sdb est resté (en gros, c'est le lecteur de carte SD lui-même), et / dev / sdb1 a disparu. Cet appareil correspond au système de fichiers sur le support. Le transporteur s'est déconnecté de l'ordinateur - il n'était plus visible. Nous continuons le traçage. Après avoir appelé la deuxième fonction ioctl () , nous regardons à nouveau la liste des périphériques:



La carte SD est reconnectée au système, donc / dev / sdb1 est de nouveau en place. En fait, nous avons appris à émettre des commandes spécifiques au fournisseur et à gérer un périphérique basé sur le microcontrôleur STM32 dans le complexe Redd. D'autres commandes seront laissées aux lecteurs pour étude indépendante. Vous pouvez contrôler le fonctionnement de certains d'entre eux de manière similaire. Si une puce ftdi passe dans un état de réinitialisation, le périphérique correspondant disparaîtra du système. Le fonctionnement du relais et le contrôle des jambes de la configuration devront être contrôlés par des instruments de mesure. Eh bien, vous pouvez vérifier le travail avec un lecteur flash en écrivant des pages avec leur contrôle de lecture ultérieur.

Conclusion


Nous avons examiné deux grands sujets qui ne sont pas liés aux FPGA dans le complexe Redd. Le troisième est resté - travailler avec la puce MAX3421, qui permet d'implémenter des périphériques USB 2.0 FS. En fait, il y a aussi des hôtes, mais il y a beaucoup d'hôtes et la carte mère. La fonctionnalité de l'appareil permettra au complexe de faire semblant d'être un lecteur flash USB (pour envoyer des mises à jour de «firmware»), un clavier USB (pour contrôler les unités externes), etc. Nous examinerons ce sujet dans le prochain article.

Source: https://habr.com/ru/post/fr484706/


All Articles