Como se recuperar de erros de pipe de USB
Observação
Este artigo destina-se a desenvolvedores de driver de dispositivo. Se você tiver dificuldades com um dispositivo USB, consulte Corrigir problemas de USB-C no Windows
Este artigo fornece informações sobre as etapas a serem seguidas quando uma transferência de dados para um pipe USB falhar. Os mecanismos descritos neste artigo abrangem operações de porta de anulação, redefinição e ciclo em pipes em massa, de interrupção e isócronos.
Um driver USB cliente se comunica com o dispositivo enviando transferências de controle para o ponto de extremidade padrão; transferências de dados para pontos de extremidade em massa, de interrupção e isócronos do dispositivo. Às vezes, essas transferências podem falhar devido a diversos motivos, como uma condição de paralisação no ponto de extremidade. Se a transferência falhar, o pipe associado não poderá processar solicitações até que a condição de erro seja apagada.
Para transferências de controle, a pilha de drivers USB apaga as condições de erro automaticamente. Para transferências de dados, o cliente deve tomar as medidas apropriadas para se recuperar da condição de erro. Quando uma transferência de dados falha, a pilha de drivers USB relata o erro ao driver cliente por meio de códigos de status USBD com falha. Com base no código de status, o driver pode fornecer um mecanismo de recuperação de erros.
Este artigo fornece diretrizes sobre a recuperação de erros por meio dessas operações.
- Redefinir o pipe USB
- Redefinir a porta USB à qual o dispositivo está conectado
- Alternar a porta USB para enumerar novamente a pilha de dispositivos para o driver cliente
Para apagar uma condição de erro, comece com a operação de redefinição de pipe e execute operações mais complexas, como reset-port e cycle-port, somente se for necessário.
Sobre a coordenação de vários mecanismos de recuperação:
O driver cliente deve coordenar as diferentes operações de recuperação e garantir que apenas um método seja usado em um determinado momento. Por exemplo, considere um dispositivo com dois pontos de extremidade: um em massa e um de interrupção. Depois de enviar algumas solicitações de transferência de dados para o dispositivo, o driver percebe que as solicitações falham no pipe em massa. Para se recuperar desses erros, o driver redefine o pipe em massa. No entanto, essa operação não resolve os erros de transferência e as transferências em massa continuam falhando. Portanto, o driver emite uma solicitação para redefinir a porta USB. Enquanto isso, as transferências começam a falhar no pipe de interrupção e, em seguida, uma solicitação de reinicialização de dispositivo. Para se recuperar das falhas de transferência de interrupção, o driver emite uma solicitação de redefinição no pipe de interrupção. Se essas duas operações não forem coordenadas, o driver poderá iniciar duas operações de redefinição de dispositivo simultaneamente, devido a falhas nos dois pipes. Essas operações simultâneas podem ser problemáticas.
O driver cliente deve garantir que, em um determinado momento, o driver execute apenas uma operação de redefinição ou alternância de porta. Durante essas operações, uma operação de redefinição de pipe não deve estar em andamento em nenhum pipe e o driver não deve emitir uma nova solicitação de redefinição de pipe.
O que você precisa saber
Este artigo usa o Kernel-Mode Driver Framework (KMDF).
Pré-requisitos
O driver cliente deve ter criado o objeto de dispositivo de destino USB da estrutura.
Se você estiver usando os modelos USB fornecidos com o Microsoft Visual Studio Professional 2012, o código do modelo executará essas tarefas. O código do modelo obtém o identificador para o objeto de dispositivo de destino e armazena no contexto do dispositivo.
Um driver cliente KMDF deve obter um identificador WDFUSBDEVICE chamando o método WdfUsbTargetDeviceCreateWithParameters. Para obter mais informações, consulte "Código-fonte do dispositivo" em Noções básicas sobre a estrutura de código do driver do cliente USB (KMDF).
O driver cliente deve ter um identificador para o objeto de pipe de destino da estrutura. Para obter mais informações, consulte Como enumerar pipes USB.
Etapa 1: Determinar a causa da condição de erro
O driver cliente inicia uma transferência de dados usando um Bloco de Solicitação USB (URB). Após a conclusão da solicitação, a pilha de drivers USB retorna um código de status USBD que indica se a transferência foi bem-sucedida ou falhou. Em uma falha, o código USBD indica o motivo da falha.
- Se você enviou URB chamando o método WdfUsbTargetDeviceSendUrbSynchronously, verifique o membro Hdr.Status da estrutura URB depois que o método retornar.
- Se você enviou o URB de forma assíncrona chamando o método WdfRequestSend, verifique o status do URB em EVT_WDF_REQUEST_COMPLETION_ROUTINE. O parâmetro Params aponta para uma estrutura WDF_REQUEST_COMPLETION_PARAMS. Para verificar o código de status USBD, inspecione o membro Usb->UsbdStatus. Para obter informações sobre o código, consulte USBD_STATUS.
As falhas de transferência podem resultar de um erro do dispositivo, como USBD_STATUS_STALL_PID ou USBD_STATUS_BABBLE_DETECTED. Eles também podem resultar de um erro relatado pelo controlador host, como USBD_STATUS_XACT_ERROR.
Etapa 2: Determinar se o dispositivo está conectado à porta
Antes de emitir qualquer solicitação que redefina o pipe ou o dispositivo, verifique se o dispositivo está conectado. Você pode determinar o estado conectado do dispositivo chamando o método WdfUsbTargetDeviceIsConnectedSynchronous.
Etapa 3: Cancelar todas as transferências pendentes para o pipe
Antes de enviar qualquer solicitação que redefina o pipe ou a porta, cancele todas as solicitações de transferência pendentes para o pipe, que a pilha de drivers USB ainda não concluiu. Você pode cancelar solicitações assim:
Pare o destino de E/S chamando o método WdfIoTargetStop.
Para interromper o destino de E/S, primeiro, obtenha o identificador WDFIOTARGET associado ao objeto de pipe da estrutura chamando o método WdfUsbTargetPipeGetIoTarget. Usando o identificador, chame WdfIoTargetStop. Na chamada, defina a ação como WdfIoTargetCancelSentIo (consulte WDF_IO_TARGET_SENT_IO_ACTION)** para instruir a estrutura a cancelar todas as solicitações que a pilha de drivers USB não concluiu. Para solicitações que foram concluídas, o driver cliente deve aguardar o retorno de chamada de conclusão para ser invocado pela estrutura.
Envie uma solicitação de interrupção de pipe. Você pode enviar a solicitação chamando um destes métodos:
Chame o método WdfUsbTargetPipeAbortSynchronously.
A chamada é síncrona e retorna somente depois que todas as solicitações pendentes são canceladas. WdfUsbTargetPipeAbortSynchronously usa um parâmetro Request opcional. Recomendamos que você transmita um identificador WDFREQUEST para um objeto de solicitação de estrutura pré-alocado. O parâmetro habilita a estrutura a usar o objeto de solicitação especificado em vez de um objeto de solicitação interno que o driver não pode acessar. Esse valor de parâmetro garante que WdfUsbTargetPipeAbortSynchronously não falhe devido à memória insuficiente.
Chame o método WdfUsbTargetPipeFormatRequestForAbort para formatar um objeto de solicitação para uma solicitação de interrupção de pipe e envie a solicitação chamando o método WdfRequestSend.
Se o driver enviar a solicitação de forma assíncrona, deverá especificar um ponteiro para a EVT_WDF_REQUEST_COMPLETION_ROUTINE que o driver implementa. Para especificar o ponteiro, chame o método WdfRequestSetCompletionRoutine.
O driver pode enviar a solicitação de forma síncrona especificando WDF_REQUEST_SEND_OPTION_SYNCHRONOUS como uma das opções de solicitação em WdfRequestSend. Se você enviar a solicitação de forma síncrona, chame WdfUsbTargetPipeAbortSynchronously.
Etapa 4: Redefinir o pipe USB
Inicie a recuperação de erros redefinindo o pipe. Você pode enviar uma solicitação de redefinição de pipe chamando um destes métodos:
Chame WdfUsbTargetPipeResetSynchronously para enviar uma solicitação de redefinição de pipe de forma síncrona.
Chame o método WdfUsbTargetPipeFormatRequestForReset para formatar um objeto de solicitação para uma solicitação de redefinição de pipe e envie a solicitação chamando o método WdfRequestSend. Essas chamadas são semelhantes às da solicitação de interrupção de pipe, conforme descrito na etapa 3.
Observação
Não envie novas solicitações de transferência até que a operação de redefinição de pipe seja concluída.
A solicitação de redefinição de pipe apaga a condição de erro no dispositivo e no hardware do controlador de host. Para apagar o erro do dispositivo, a pilha de drivers USB envia uma solicitação de controle CLEAR_FEATURE para o dispositivo usando o seletor de recursos ENDPOINT_HALT. O destinatário da solicitação é o ponto de extremidade associado ao pipe. Se a condição de erro ocorreu em um pipe isócrono, a pilha de drivers não executará nenhuma ação para apagar o dispositivo porque, em caso de erros, os pontos de extremidade isócronos serão apagados automaticamente.
Para apagar o erro do controlador de host, a pilha de drivers apaga o estado HALT do pipe e redefine a alternância de dados do pipe para 0.
Etapa 5: Redefinir a porta USB
Se uma operação de redefinição de pipe não apagar a condição de erro e as transferências de dados continuarem falhando, envie uma solicitação de redefinição de porta.
Cancele todas as transferências para o dispositivo. Para fazer isso, enumere todos os pipes na configuração atual e cancele as solicitações pendentes agendadas para cada pipe.
Pare o destino de E/S do dispositivo.
Chame o método WdfUsbTargetDeviceGetIoTarget para obter um identificador WDFIOTARGET associado ao objeto de dispositivo de destino da estrutura. Em seguida, chame WdfIoTargetStop e especifique o identificador WDFIOTARGET. Na chamada, defina a ação como WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION).
Envie uma solicitação de redefinição de porta chamando o método WdfUsbTargetDeviceResetPortSynchronously.
Uma operação de redefinição de porta faz com que o dispositivo seja reenumerado no barramento USB. A pilha de drivers USB preserva a configuração do dispositivo após a enumeração. O driver cliente pode usar os identificadores de pipe obtidos anteriormente porque a pilha de drivers garante que os identificadores de pipe existentes permaneçam válidos.
Não é possível redefinir uma função individual de um dispositivo composto. Para um dispositivo composto, quando o driver cliente de uma função específica envia uma solicitação de redefinição de porta, todo o dispositivo é redefinido. Se o dispositivo USB mantiver o estado, essa solicitação de redefinição de porta poderá afetar os drivers cliente de outras funções. Portanto, é importante que o driver cliente tente redefinir o pipe antes de redefinir a porta.
Etapa 6: Alternar a porta USB
Uma operação de alternância de porta é semelhante ao dispositivo que é desconectado e conectado de volta à porta, mas o dispositivo não é desconectado eletricamente. O dispositivo é desconectado e reconectado no software. Essa operação leva à redefinição e à enumeração do dispositivo. Como resultado, o Gerenciador de PnP recria o nó do dispositivo.
Se uma operação de redefinição de porta não apagar a condição de erro e as transferências de dados continuarem falhando, envie uma solicitação de alternância de porta.
Cancele todas as transferências para o dispositivo. Cancele a solicitação pendente agendada para cada pipe na configuração atual (consulte a etapa 3).
Pare o destino de E/S do dispositivo.
Chame o método WdfUsbTargetDeviceGetIoTarget para obter um identificador WDFIOTARGET associado ao objeto de dispositivo de destino da estrutura. Em seguida, chame WdfIoTargetStop e especifique o identificador WDFIOTARGET. Na chamada, defina a ação como WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION).
Envie uma solicitação de alternância de porta chamando um destes métodos:
- Chame WdfUsbTargetDeviceCyclePortSynchronously para enviar uma solicitação de alternância de porta de forma síncrona.
- Chame o método WdfUsbTargetDeviceFormatRequestForCyclePort para formatar um objeto de solicitação para uma solicitação de alternância de porta e envie a solicitação chamando o método WdfRequestSend. Essas chamadas são semelhantes às da solicitação de interrupção de pipe, conforme descrito na etapa 3.
O driver cliente pode enviar solicitações de transferência para o dispositivo somente após a conclusão da solicitação de alternância de porta. Isso ocorre porque o nó do dispositivo é removido enquanto a pilha de drivers USB processa a solicitação de alternância de porta.
A solicitação de alternância de porta faz com que o dispositivo seja enumerado novamente. A pilha de drivers USB informa ao Gerenciador PnP que o dispositivo foi desconectado. O Gerenciador PnP destrói a pilha de dispositivos associada ao driver cliente. A pilha de drivers redefine o dispositivo, enumera-o novamente no barramento USB e informa ao Gerenciador PnP que um dispositivo foi conectado. Em seguida, o Gerenciador PnP recria a pilha de dispositivos para o dispositivo USB.
Como resultado da operação de alternância de porta, qualquer aplicativo que tenha um identificador aberto para o dispositivo recebe uma notificação de remoção do dispositivo (se o aplicativo se registrou para essa notificação). Em resposta, o aplicativo pode relatar uma mensagem de dispositivo desconectado ao usuário. Como isso afeta a experiência do usuário, o driver cliente deverá optar por uma solicitação de alternância de porta somente se outros mecanismos de recuperação não resolverem a condição de erro.
Semelhante à operação de redefinição de porta (descrita na etapa 6), para um dispositivo composto, a operação de alternância de porta afeta todo o dispositivo e não as funções individuais do dispositivo.