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.