Análise da Arquitetura do USBIP no Kernel Linux

Introdução à Arquitetura USBIP

O USBIP permite o acesso a dispositivos USB externos através da rede, tornando o processo transparente para aplicações de usuário. O mecanismo se baseia na abstração do controlador host USB, que normalmente gerancia a comunicação via barramento PCI, e utiliza sockets TCP/IP para transferência de dados entre hosts remotos e locais.

No kernel Linux, a pilha USB é estruturada em três camadas principais: drivers de dispositivo USB (que interagem diretamente com o VFS), o núcleo USB (que gerencia URBs e fornece abstrações), e drivers de controlador host (que lidam com o hardware subjacante). O USBIP estende essa arquitetura introduzindo componentes virtuais para simular a presença de dispositivos USB remotos.

Componentes do USBIP

A implementação do USBIP no kernel é dividida em dois módulos principais: o Stub (servidor) e o VHCI (cliente). O código reside em drivers/usb/usbip/, onde arquivos prefixados com stub tratam do lado do servidor, e arquivos com vhci implementam o cliente.

Estruturas de Dados no Stub

O Stub utiliza estruturas para representar dispositivos e requisições. A estrutura base usbip_common_dev abstrai elementos compartilhados entre Stub e VHCI, incluindo sockets e threads de comunicação.

struct usbip_common_dev {
    enum usbip_endpoint endpoint;
    enum usbip_conn_state state;
    spinlock_t state_lock;
    struct socket *net_sock;
    struct task_struct *rx_thread;
    struct task_struct *tx_thread;
    unsigned long flags;
    struct task_struct *event_thread;
    wait_queue_head_t event_wq;
    struct event_handlers {
        void (*terminate)(struct usbip_common_dev *);
        void (*reinitialize)(struct usbip_common_dev *);
        void (*mark_dead)(struct usbip_common_dev *);
    } ev_handlers;
};

Para gerenciar URBs, o Stub define a estrutura stub_urb_context, que rastreia cada requisição com um número de sequência e filas de status.

struct stub_urb_context {
    unsigned long seq_num;
    struct list_head list;
    struct stub_endpoint *sep;
    struct urb *urb;
    int pending_unlink;
};

A estrutura stub_endpoint representa um dispositivo USB remoto, contendo listas para gerenciar URBs em diferentes estágios: submissão, transmissão e liberação.

Inicialização do Módulo Stub

A inicialização envolve registrar drivers USB e criar arquivos sysfs para controle do usuário. A função stub_host_setup inicializa uma tabela global para mapear IDs de barramento a dispositivos, aloca um cache de memória para contextos de URB, e registra o driver USB do Stub.

static struct table_entry busid_map[MAX_DEVICES];

static int __init stub_module_init(void)
{
    int ret;
    ret = initialize_busid_table();
    if (ret) return ret;
    ret = create_urb_cache();
    if (ret) goto cleanup_table;
    ret = usb_register_driver(&stub_usb_driver, THIS_MODULE, "usbip-host");
    if (ret) goto cleanup_cache;
    ret = create_sysfs_attrs();
    if (ret) goto cleanup_driver;
    return 0;

cleanup_driver:
    usb_deregister_driver(&stub_usb_driver);
cleanup_cache:
    destroy_urb_cache();
cleanup_table:
    clear_busid_table();
    return ret;
}

Driver USB do Stub

O driver USB do Stub lida com a detecção de dispositivos. A função stub_probe é chamada quando um dispositivo USB é conectado, alocando uma estrutura stub_endpoint e iniciando threads de eventos.

static int stub_probe(struct usb_device *udev)
{
    struct stub_endpoint *sep;
    sep = allocate_stub_endpoint(udev);
    if (!sep) return -ENOMEM;
    start_event_handler(&sep->dev_info);
    attach_to_busid_table(sep);
    create_device_attrs(sep);
    return 0;
}

Threads de Comunicação (RX/TX)

O Stub utiliza dois threads principais: um para receber URBs da rede (RX) e outro para enviar resultados (TX). O thread RX processa comandos de submissão e cancelamento de URBs, enquanto o thread TX transmite os resultados de volta ao cliente.

As URBs são gerenciadas através de filas: queue_submitted para URBs recebidas, queue_pending para URBs aguardando envio, e queue_done para URBs completadas. O mecanismo de fila assegura a ordenação e o tratamento adequado das requisições.

Componente VHCI (Cliente)

O VHCI implementa um controlador host USB virtual. Ele intercepta URBs destinadas a dispositivos USB locais e as redireciona pela rede para o Stub remoto. O driver de controlador host (hc_driver) define funções para enfileirar URBs e gerenciar hubs virtuais.

static const struct hc_driver vhci_driver = {
    .description = "vhci-hcd",
    .hcd_priv_size = sizeof(struct vhci_controller),
    .flags = HCD_USB2,
    .start = vhci_start_controller,
    .stop = vhci_stop_controller,
    .urb_enqueue = vhci_enqueue_urb,
    .urb_dequeue = vhci_dequeue_urb,
    .get_frame_number = vhci_get_frame,
    .hub_status_data = vhci_check_hub_status,
    .hub_control = vhci_hub_control,
    .bus_suspend = vhci_suspend_bus,
    .bus_resume = vhci_resume_bus,
};

Assim como o Stub, o VHCI utiliza sysfs para configuração de sockets e listagem de portas virtuais. Os threads de comunicação no VHCI tratam da serialização de URBs e do processamento de respostas do Stub, mantendo a transparência para o núcleo USB.

A arquitetura do USBIP demonstra como extensões de rede podem ser integradas à pilha USB do kernel, permitindo o compartilhamento de dispositivos sem modificações nas aplicações de usuário.

Tags: usbip linux-kernel usb-drivers network-usb kernel-threads

Publicado em 6-14 19:22 por Thomas