No
último artigo, examinamos a teoria de gerenciar mil pequenas coisas no complexo Redd, mas, para não aumentar o volume, adiamos a prática da próxima vez. Chegou a hora de realizar experimentos práticos. Aqueles que não usam o complexo Redd também poderão encontrar conhecimento útil neste artigo, a saber, a metodologia para enviar comandos do Fornecedor para drives USB a partir do Linux, porque, como já mencionado, o controlador STM32 no complexo desempenha a função de um leitor SD, ou seja, dirigir.

Artigos anteriores do ciclo Classificação da unidade por sistemas de comando
Ao trabalhar com unidades, você deve distinguir entre uma interface física e um sistema de comando. Em particular, unidades de CD / DVD / BD e outras ópticas. Tradicionalmente, eles se conectam a um cabo SATA (anteriormente IDE). Porém, especificamente nesse fio, apenas os comandos PACKET são executados durante a operação, no bloco de dados em que os comandos codificados de acordo com um princípio completamente diferente são colocados (em breve descobriremos qual). Portanto, agora falaremos não tanto sobre fios, mas sobre as equipes que os rodeiam. Conheço três sistemas de comando comuns para trabalhar com unidades.
- MMC É entendido por cartões SD. Honestamente, para mim este é o sistema de comando mais misterioso. Como enviá-los, ao que parece, é claro, mas como gerenciar a unidade sem ler atentamente o documento que contém muitos gráficos de transição - estou sempre confuso. Felizmente, isso não nos incomoda atualmente, pois, embora trabalhemos com um cartão SD, o controlador STM32 no modo "caixa preta" trabalha com ele.
- ATA Inicialmente, esses comandos eram executados no barramento IDE e depois no SATA. Um sistema de comando maravilhoso, mas hoje também mencionamos que ele existe.
- SCSI Este sistema de comando é usado em uma ampla variedade de dispositivos. Considere seu uso em unidades. Hoje, as equipes de SCSI executam, antes de tudo, os fios do barramento SAS (a propósito, até SSDs com interface SAS estão na moda). Curiosamente, as unidades ópticas conectadas fisicamente ao barramento SATA também funcionam através de comandos SCSI. No barramento USB ao trabalhar de acordo com o padrão do dispositivo de armazenamento em massa, os comandos também vão no formato SCSI. O microcontrolador STM32 é conectado ao complexo Redd através do barramento USB, ou seja, no nosso caso, os comandos seguem o seguinte caminho:

Do PC ao controlador, via USB, os comandos estão no formato SCSI. O controlador transcodifica os comandos de acordo com a regra do MMC e os envia pelo barramento SDIO. Mas temos que escrever um programa para o PC, para que as equipes nos deixem no formato SCSI. Eles são preparados pelo driver de dispositivo do dispositivo de armazenamento em massa, com o qual nos comunicamos através do driver do sistema de arquivos. É possível misturar solicitações com outros dispositivos para essas solicitações? Vamos acertar.
Detalhes do sistema de comando SCSI
Se você abordar o assunto formalmente, a descrição do padrão SCSI estará disponível no t10.org, mas seremos realistas. Ninguém o lerá voluntariamente. Mais precisamente, não o dele, mas o deles: existe uma pilha inteira de documentos abertos e uma montanha de documentos fechados. Somente uma necessidade extrema fará com que você mergulhe na linguagem complexa em que o padrão está escrito (isso, a propósito, se aplica ao padrão ATA em t13.org). É muito mais fácil ler a documentação para unidades reais. Está escrito em uma linguagem mais animada, e partes hipotéticas, mas não realmente usadas, são cortadas. Ao preparar o artigo, me deparei com um documento bastante novo (2016) do
Manual de referência de comandos SCSI da Seagate (link direto
www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf , mas, como sempre, Não sei quanto tempo ela vai viver). Eu acho que se alguém quiser dominar esse sistema de comandos, ele deve começar com este documento. Lembramos apenas que os leitores SD implementam um subconjunto ainda menor dos comandos dessa descrição.
Falando muito brevemente, uma unidade de comando com um comprimento de 6 a 16 bytes é enviada para a unidade. Um bloco de dados pode ser anexado ao bloco de comando do PC para a unidade ou da unidade para o PC (o padrão SCSI também permite trocas bidirecionais, mas para o Dispositivo de armazenamento em massa via USB apenas um bloco é permitido, o que significa que a direção é apenas uma). No bloco de instruções, o primeiro byte é sempre o código de comando. Os bytes restantes são seus argumentos. As regras para preenchimento dos argumentos são descritas exclusivamente pelos detalhes da implementação do comando.

No começo, inseri muitos exemplos no artigo, mas depois percebi que eles dificultam a leitura. Portanto, sugiro que todos comparem os campos do comando READ CAPACITY (10) da tabela 119 do documento Seigate e os campos do comando READ (10) da tabela 97 do mesmo documento (consulte o link acima). Quem não encontrou nenhuma conexão - não se assuste. Era isso que eu queria mostrar. Além do campo "comando" no byte zero, o objetivo de todos os campos depende exclusivamente das especificidades de um comando específico. Você sempre precisa abrir o documento e estudar a finalidade dos campos restantes nele.
Então:
- Para se comunicar com a unidade, você deve formar um bloco de comando com um comprimento de 6 a 16 bytes (dependendo do formato do comando, o número exato é indicado na documentação para ele).
- O mais importante é o byte zero do bloco: é ele quem define o código de comando.
- Os bytes restantes do bloco não têm um objetivo claro. Para entender como preenchê-los, você deve abrir a documentação para uma equipe específica.
- Um bloco de dados que pode ser transferido para ou de uma unidade pode ser anexado a um comando.
Na verdade, é tudo. Aprendemos as regras para emitir comandos SCSI. Agora podemos enviá-los, haveria documentação sobre eles. Mas como fazer isso no nível do sistema operacional?
Comandos Linux SCSI
Pesquisar dispositivo de destino
Para emitir comandos, abra o dispositivo de disco. Vamos encontrar o nome dele. Para fazer isso, seguiremos exatamente da mesma maneira que no
artigo sobre portas seriais . Vamos ver a lista de “arquivos” no
diretório / dev (lembre-se de que nos dispositivos Linux também são mostrados como arquivos e sua lista é exibida com o mesmo
comando ls ).
Hoje prestamos atenção ao
disco do diretório virtual:

Analisamos seu conteúdo:

Um conjunto familiar de diretórios aninhados! Estamos tentando considerar o diretório
by-id , usando a opção
–l do
comando ls, que já sabíamos no artigo sobre portas seriais:

Palavras destacadas falam por si. Esta é uma unidade que contém o cartão SD interno do complexo Redd. Ótimo! Agora sabemos que o dispositivo
MIR_Redd_Internal_SD corresponde ao dispositivo
/ dev / sdb e / dev / sdb1 . Aquele que sem o número é a própria unidade, trabalharemos com ela e, com o número, é o sistema de arquivos localizado na mídia inserida nela. Em termos de trabalho com um cartão SD,
/ dev / sdb é o leitor e
/ dev / sdb1 é o sistema de arquivos no cartão inserido nele.
Função do sistema operacional para emitir comandos
Normalmente, em qualquer sistema operacional, todas as coisas não padronizadas dos dispositivos são feitas através de solicitações diretas ao driver. No Linux, a função
ioctl () está disponível para enviar esses pedidos. Nosso caso não é exceção. Como argumento, passamos a solicitação SG_IO descrita no arquivo de cabeçalho
sg.h. A estrutura
sg_io_hdr_t que contém os parâmetros de solicitação também é descrita lá. Não darei a estrutura completa, pois nem todos os seus campos devem ser preenchidos. Vou dar apenas o mais importante deles:
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) */
Não faz sentido descrever os campos que estão bem documentados nos comentários (
interface_id, dxfer_direction, timeout ). O artigo já está crescendo.
O campo
cmd_len contém o número de bytes no bloco de comando e o
cmdp contém um ponteiro para esse bloco. Você não pode ficar sem um comando, portanto, o número de bytes deve ser diferente de zero (de 6 a 16).
Os dados são opcionais. Se estiverem, o comprimento do buffer selecionado é especificado no campo
dxfer_len e um ponteiro para ele é especificado no campo
dxferp . Uma unidade pode transferir fisicamente menos dados que o tamanho do buffer especificado. A direção da transmissão é especificada no campo
dxfer_direction . Os valores válidos do dispositivo de armazenamento em massa USB são:
SG_DXFER_NONE, SG_DXFER_TO_DEV, SG_DXFER_FROM_DEV . Há mais uma coisa no arquivo de cabeçalho, mas o padrão do dispositivo de armazenamento em massa não permite implementá-lo fisicamente.
Você também pode solicitar o retorno de um código de erro estendido (
SENSE ). O que é encontrado pode ser encontrado no documento Segate, seção 2.4. O comprimento do buffer alocado é indicado no campo
mx_sb_len , e o ponteiro para o buffer em si é indicado no campo
sbp .
Como você pode ver, tudo o que eu falei acima está preenchido nesta estrutura (além disso, você pode obter informações estendidas sobre o erro). Leia mais sobre como trabalhar com a solicitação
SG_IO aqui:
sg.danny.cz/sg/sg_io.htmlEnviamos um comando padrão para a unidade
Bem, descobrimos o formato do comando, descobrimos para qual dispositivo enviá-lo, descobrimos qual função chamar. Vamos tentar enviar algum comando padrão para o nosso dispositivo. Seja este o comando para obter o nome da unidade. É assim que é descrito no documento Sigeyt:

Observe que, de acordo com a ideologia SCSI, todos os campos nos comandos padrão são preenchidos na notação Big Endian, ou seja, o maior byte a frente. Portanto, preenchemos o campo com o tamanho do buffer não no formato "0x80, 0x00", mas, pelo contrário - "0x00, 0x80". Mas isso está em comandos padrão. No não padrão, tudo é possível, você deve sempre consultar a descrição. Na verdade, apenas o código de comando (
12h ) e o comprimento que precisamos preencher. Solicitaremos uma página zero e os campos restantes serão reservados ou desatualizados ou o padrão será zero. Então, preencha todos com zeros.
Nós criamos um programa que fornece este comando: #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; }
Como executar esses programas em um dispositivo Redd remoto, já discutimos em um dos
artigos anteriores . É verdade que, iniciando-o pela primeira vez, recebi imediatamente um erro ao chamar a função
open () . Descobriu-se que o usuário por padrão não tem direitos suficientes para abrir dispositivos de disco. Qual de mim é especialista em Linux, escrevi várias vezes, mas na rede consegui descobrir que, para resolver esse problema, você pode alterar os direitos de acesso ao dispositivo emitindo o comando:
sudo chmod 666 / dev / sdbNo entanto, meu chefe (e ele é um grande especialista neste sistema operacional) mais tarde observou que a solução é válida até que o sistema operacional seja reiniciado. Para obter os direitos com certeza, você precisa adicionar o usuário ao grupo de
discos .
Qualquer que seja o caminho, mas depois que tudo tiver funcionado, coloque um ponto de interrupção na linha
close (s_fd); e inspecione os resultados até o momento em que é alcançado no ambiente de desenvolvimento (já que o programa nem sequer é de um dia, o que significa que não temos tempo para gastar tempo e esforço na inserção de mapeadores, se o ambiente de desenvolvimento puder nos mostrar tudo). O valor de
res é zero. Então a equipe trabalhou sem erros.

O que veio para o buffer? Quando inseri a palavra
dados no endereço do despejo, eles me disseram que não podiam calcular o valor, tive que inserir o
& data; . É estranho, porque os
dados são um ponteiro, ao depurar no Windows tudo funciona, mas apenas observe esse fato, funciona assim: veja o resultado obtido assim:

É isso mesmo, eles retornaram o nome e a revisão da unidade para nós. Mais informações sobre o formato da estrutura resultante podem ser encontradas no documento Segate (seção 3.6.2, tabela 59). O buffer de
detecção não foi preenchido, mas a descrição IOCTL da solicitação diz que é preenchida apenas quando ocorre um erro que retorna algo nesse buffer. Literalmente:
dados de detecção (usados apenas quando 'status' é CHECK CONDITION ou (driver_status & DRIVER_SENSE) são verdadeiros) .
Formato de comando personalizado para a unidade SD interna Redd
Agora que não apenas estudamos a descrição seca do padrão, mas também tentamos tudo na prática, tendo experimentado o que é um bloco de comando, já podemos mostrar o formato de comando com o qual você pode chamar funções fora do padrão que são "piscadas" para o controlador STM32 na placa complexa. O código de comando que selecionei desde o início do intervalo de comandos
específicos do
fornecedor É igual a 0xC0. Tradicionalmente, nas descrições dos comandos SCSI, escreva
C0h . O comprimento do comando é sempre 10 bytes. O formato da equipe é unificado e apresentado na tabela abaixo.
Como você pode ver, os argumentos são dados na notação Little Endian. Isso permitirá que você descreva o comando na forma de uma estrutura e acesse seus campos diretamente, sem recorrer à função de permutação de bytes. Problemas de alinhamento (palavras duplas na estrutura têm compensações que não são múltiplas de quatro) nas arquiteturas x86 e x64 não valem a pena.
Os códigos de subcomando são descritos pela seguinte enumeração:
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 };
Eles podem ser divididos em grupos.
Alternando dispositivos para modos interno e externo
Os
comandos subCmdSdEnable e
subCmdSpiFlashEnable alternam o cartão SD e o SPI, respectivamente. O parâmetro
arg1 passa um dos seguintes valores:
enum enableMode { enableModeToPC = 0, enableModeOutside };
Por padrão, os dois dispositivos estão conectados a um PC.
Comutação de energia
O protocolo SDIO requer muita manipulação durante a inicialização. Às vezes, é útil redefinir o cartão SD para seu estado inicial (por exemplo, ao mudar suas linhas para um conector externo). Para fazer isso, desligue e ligue a energia. Isso pode ser feito usando o
comando subCmdSdPower . No argumento
arg1, um dos seguintes valores é passado: 0 - desligado, 1 - ligado. Lembre-se de reservar um tempo para descarregar os capacitores na linha de energia.
Depois de ligar a alimentação, o cartão, se estiver conectado ao PC, deve ser reinicializado. Para fazer isso, use o comando
subCmdSdReinit (ele não possui argumentos).
Trabalhar com a unidade flash SPI
Se o cartão SD estiver conectado ao sistema como uma unidade completa, o chip de acesso na versão atual é bastante limitado. Você pode acessar apenas suas páginas individuais (256 bytes) e apenas uma de cada vez. A quantidade de memória no microcircuito é tal que, mesmo quando se trabalha na página, o processo não leva muito tempo, mas essa abordagem simplifica bastante o "firmware" do microcontrolador.
O comando
subCmdSpiFlashReadPage lê a página. O endereço é especificado no parâmetro arg1, o número de páginas a serem transmitidas no parâmetro arg2. Mas na versão atual, o número de páginas deve ser igual a uma. O comando retornará 256 bytes de dados.
Espelhado para ela está o comando
subCmdSpiFlashWritePage . Os argumentos para ela são preenchidos pelo mesmo princípio. A direção da transferência de dados é para o dispositivo.
A peculiaridade da memória flash é que apenas bits únicos podem ser substituídos por zero durante a gravação. Para devolvê-los a um único valor, as páginas devem ser apagadas. Existe um comando
subCmdSpiFlashErasePage para
isso . É verdade que, devido aos recursos do microcircuito usado, não é uma única página configurada no parâmetro
arg1 que é apagada, mas um bloco de 4 kilobytes que o contém.
Gerenciamento de relés de estado sólido
O complexo possui seis relés de estado sólido. Existem três equipes para gerenciá-los.
subCmdRelaysSet - define o valor de todos os seis relés simultaneamente. No parâmetro
arg1, um valor é passado, cada bit correspondendo ao seu próprio relé (zero bit - relé com índice 0, primeiro bit com índice 1, etc.). Um valor de bit único fecha o relé, um valor zero faz com que ele seja aberto.
Este método de operação é bom quando todos os relés funcionam como um único grupo. Se eles funcionarem independentemente um do outro, com essa abordagem, você precisará iniciar uma variável de buffer que armazene o valor do estado de todos os relés. Se diferentes relés são controlados por diferentes programas, o problema de armazenar o valor agregado se torna extremamente agudo. Nesse caso, você pode usar outros dois comandos:
subCmdRelaysOn - ativa relés selecionados por máscara. Os relés que correspondem aos bits da unidade no argumento
arg1 serão ativados. Os relés que correspondem a zeros na máscara manterão seu estado atual.
O comando
subCmdRelaysOff, espelhando-o , desativará os relés selecionados por máscara. Os relés que correspondem aos bits únicos no argumento
arg1 serão desativados. Os relés que correspondem a zeros na máscara manterão seu estado atual.
Redefinir controladores FTDI e Maxim
Para enviar sinais de redefinição para os microcircuitos FTDI e Maxim, o grupo de comandos
subCmdFT4222_1_Reset ,
subCmdFT4222_2_Reset ,
subCmdFT4222_3_Reset ,
subCmdFT4232_Reset ,
subCmdFT2232_Reset e
subCmdMAX3421_ é usado . A partir de seus nomes, você pode ver quais chips eles controlam através de sinais de reset. As pontes FT4222, como consideramos anteriormente, são duas no circuito (seus índices são 1 e 2), outra ponte FT4222 transfere dados para o chip MAX3421, que consideraremos no próximo artigo.
O parâmetro
arg1 passa um dos seguintes valores:
enum ResetState { resetStateActive =0, resetStateNormalOperation };
Por padrão, todas as pontes estão em condições normais de trabalho. Como já observado em um
artigo anterior , nós mesmos não temos certeza se essa funcionalidade é necessária, mas quando não há acesso direto ao dispositivo, é melhor poder redefinir remotamente tudo e tudo.
Alternando linhas de configuração de chips FT4222
Os chips FT4222 têm quatro modos. É improvável que alguém precise de um modo diferente de "00", mas se você precisar de repente, poderá usar os
comandos subCmdFT4222_1_Cfg ,
subCmdFT4222_2_Cfg e
subCmdFT4222_3_Cfg para
alternar para o primeiro, segundo e terceiro chips. O valor das linhas CFG0 e CFG1 é definido nos dois bits inferiores do parâmetro
arg1 .
Experiência prática na emissão de comandos para o controlador STM32
Para testar o material teórico obtido na prática, tentaremos trocar o cartão SD. Para fazer isso, emita o comando
subCmdSdEnable com o código 0x00 com o argumento
enableModeOutside com o código 0x01. Ótimo. Reescrevemos o programa a partir de experiências passadas da seguinte forma.
Programa reescrito: #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; }
Alteramos o comprimento do comando para dez bytes e removemos o bloco de dados. Bem, eles escreveram o código de comando com argumentos, conforme necessário. Caso contrário, tudo permanece o mesmo. Começamos ... E ... Nada funciona. A função
ioctl () retorna um erro. O motivo está descrito no
documento de comando
SG_IO . O fato é que damos o comando específico do fornecedor
C0h , e o seguinte é dito sobre eles literalmente:
Qualquer outro comando SCSI (opcode) não mencionado para o driver sg precisa de O_RDWR. Qualquer outro comando SCSI (opcode) não mencionado para a camada de bloco SG_IO ioctl precisa de um usuário com o recurso CAP_SYS_RAWIO.
Como o chefe me explicou (estou apenas recontando suas palavras), os valores das
capacidades são atribuídos a um arquivo executável. Por esse motivo, tive que rastrear a partir do ambiente de desenvolvimento efetuando login como
raiz . Não é a melhor solução, mas pelo menos alguma coisa. De fato, no Windows, a solicitação
IOCTL_SCSI_PASS_THROUGH_DIRECT também requer direitos de administrador. Talvez nos comentários alguém dê conselhos sobre como resolver o problema de rastreamento sem essas etapas drásticas, mas você pode executar o programa já escrito sem
raiz , se registrar os
recursos corretos
para ele . Enquanto isso, altere o nome de usuário no ambiente de desenvolvimento e defina um ponto de interrupção na linha:
int res = ioctl(s_fd, SG_IO, &header;);
e antes de chamar a função
ioctl () , examinamos a lista de dispositivos de armazenamento:

Ligue para
ioctl () e veja a lista novamente:

O dispositivo
/ dev / sdb permaneceu (grosso modo, este é o próprio leitor de cartão SD) e
/ dev / sdb1 desapareceu. Este dispositivo corresponde ao sistema de arquivos na mídia. A operadora desconectou do computador - não estava mais visível. Continuamos rastreando. Depois de chamar a segunda função
ioctl () , examinamos novamente a lista de dispositivos:

O cartão SD é reconectado ao sistema, então
/ dev / sdb1 está de volta no lugar. Na verdade, aprendemos como emitir comandos específicos do fornecedor e gerenciar um dispositivo baseado no microcontrolador STM32 no complexo Redd. Outros comandos serão deixados para os leitores para estudo independente. Você pode controlar a operação de alguns deles de maneira semelhante. Se algum chip
ftdi entrar em um estado de redefinição, o dispositivo correspondente desaparecerá do sistema. A operação do relé e o controle das pernas da configuração terão que ser controlados por instrumentos de medição. Bem, você pode verificar o trabalho com uma unidade flash escrevendo páginas com o controle de leitura subsequente.
Conclusão
Examinamos dois grandes tópicos que não estão relacionados aos FPGAs no complexo Redd. O terceiro permaneceu - trabalhando com o chip MAX3421, que permite a implementação de dispositivos USB 2.0 FS. De fato, também existem hosts, mas existem muitos hosts e a placa-mãe. A funcionalidade do dispositivo permitirá ao complexo fingir ser uma unidade flash USB (para enviar atualizações de "firmware"), um teclado USB (para controlar unidades externas) etc. Vamos considerar este tópico no próximo artigo.