Introdução aos Sistemas Operacionais/Exemplo de Drivers USB em Linux: Multilaser Mouse USB

Objetivos

editar

Este material é fruto de um estudo de caso de um driver usb(Multilaser Mouse USB) para linux e tem como intuito principal apresentar as características deste driver. Visamos com este material, tornar fácil a compreensão do código fonte, através do detalhamento do código e explicações baseadas em nossos conhecimentos e comentários feitos pelo autor.

Introdução

editar

Aqui será apresentado de uma forma clara, o funcionamento do driver Multilaser Mouse USB(usbmouse.c), como: Detalhamento do driver escolhido, código-fonte para download, quais módulos são carregados e como são carregados, dispositivos que podem ser reconhecidos e os que não podem ser reconhecidos com este driver, estruturas utilizadas, registro do driver, comunicação, etc.

Será apresentado também trechos de códigos relativos aos tópicos citados, como forma de exemplificar a funcionalidade descrita.

O módulo usbmouse.c ativa um suporte genérico a mouses usb.

Informações gerais

editar
  • Versão do kernel:
  • Identificação do driver analisado:
    • Nome: USB HID Boot Protocol mouse driver
  • Outras informações relevantes:

 


Carga (load ou init)

editar
  • Módulos Carregados:

MODULE_AUTHOR ( "Vojtech Pavlik <vojtech@ucw.cz>" )

MODULE_DESCRIPTION ( "USB HID Boot Protocol mouse driver" )

MODULE_LICENSE ( "GPL" )

MODULE_DEVICE_TABLE (usb, usb_mouse_id_table)

module_init(usb_mouse_init)

module_exit(usb_mouse_exit)


  • Processo de carga do módulo

A carga do módulo é feita pela função module_init

  • Parâmetros que podem ser passados na carga do módulo
  • Como passar uma parâmetro para o módulo, etc;

Reconhecimento ou detecção (probe)

editar
  • Dispositivos que podem ser reconhecidos segundo o fabricante:


  • Dispositivos que não podem ser reconhecidos:


  • Como se processa a identificação do dispositivo
static struct usb_driver usb_mouse_driver = {
	.name		= "usbmouse",
	.probe		= usb_mouse_probe,
	.disconnect	= usb_mouse_disconnect,
	.id_table	= usb_mouse_id_table,
};


Em caso de reconhecimento do dispositivo, ou seja, se o dispositivo puder ser controlado por este driver, chamará a funão probe. Em outras palavras, o campo probe define usb_mouse_probe como a função a ser chamada quando o USB core desejar testar se o driver pode suportar determinado dispositivo.

Segue sintaxe da função usb_mouse_probe:


static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev(intf);
	struct usb_host_interface *interface;
	struct usb_endpoint_descriptor *endpoint;
	struct usb_mouse *mouse;
	struct input_dev *input_dev;
	int pipe, maxp;
	int error = -ENOMEM;

	interface = intf->cur_altsetting;

	if (interface->desc.bNumEndpoints != 1)
		return -ENODEV;

	endpoint = &interface->endpoint[0].desc;
	if (!usb_endpoint_is_int_in(endpoint))
		return -ENODEV;

        #ifdef CONFIG_USB_HID
	if (usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
				le16_to_cpu(dev->descriptor.idProduct))
			& (HID_QUIRK_IGNORE|HID_QUIRK_IGNORE_MOUSE)) {
		return -ENODEV;
	}
        #endif

        //segue com a inicialização dos dados como descrito no próximo tópico.
  • O que ocorre quando o driver não é reconhecido (erro), etc;

Inicialização de estruturas

editar

Após a identificação do dispositivo, a inicialização de algumas estruturas se torna necessária para estabelecer a comunicação com ele.

  • Quais estruturas são inicializadas


struct usb_mouse {
	   
	//nome do dispositivo
	char name[128];	
	
	char phys[64];
	
	//dispositivo USB
	struct usb_device *usbdev;
	
	//Dispositivo de entrada
	struct input_dev *dev;
	
	//Pedido USB de bloco
	struct urb *irq;

	// interrupção de dados USB
	signed char *data;
	
	dma_addr_t data_dma;
};


  • Tipo de estruturas inicializadas (buffer, por exemplo)


        pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

        //inicializando a estutura mouse
        mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
	input_dev = input_allocate_device();
	if (!mouse || !input_dev)
		goto fail1;

        //inicializando o buffer
	mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma);
	if (!mouse->data)
		goto fail1;

        //inicializando os URBs
	mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
	if (!mouse->irq)
		goto fail2;

        //Condições de controle
        if (dev->manufacturer)
		strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));

	if (dev->product) {
		if (dev->manufacturer)
			strlcat(mouse->name, " ", sizeof(mouse->name));
		strlcat(mouse->name, dev->product, sizeof(mouse->name));
	}

	if (!strlen(mouse->name))
		snprintf(mouse->name, sizeof(mouse->name),
			 "USB HIDBP Mouse %04x:%04x",
			 le16_to_cpu(dev->descriptor.idVendor),
			 le16_to_cpu(dev->descriptor.idProduct));



        //copiam informações para uma posterior utilização
	mouse->usbdev = dev;
	mouse->dev = input_dev;

        //define um caminho estável na árvore de dispositivos USB
	usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
	strlcat(mouse->phys, "/input0", sizeof(mouse->phys));



  • Registro de URB;
//Alocando os URBs
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!mouse->irq)
    goto fail2;

//...
//Após a utilização é feito a desalocação dos URBs
usb_free_urb(mouse->irq);


  • Tipos de URBs
  • INTERRUPT


Estrutura:

/**mouse->irq: Um ponteiro para o URB a ser inicializado.

dev: Indica o dispositivo USB para o qual o URB será enviado.

pipe: India para qual endpoint do dispositivo URB será enviado.

mouse->data: Um ponteiro para o buffer de enviou ou recepção de dados.

(maxp > 8 ? 8 : maxp): Indica o tamanho do buffer apontado por mouse->data.

usb_mouse_irq: É um apontador para uma função do tipo callback que deverá ser chamada quando o URB for completado.

mouse: Equivale ao campo void *mouse da estrutura struct mouse->irq, comentada anteriormente.

endpoint->bInterval: Indica o intervalo entre as interrupções, conforme definido na estrutura mouse->irq.
*/

usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
			 (maxp > 8 ? 8 : maxp),
			 usb_mouse_irq, mouse, endpoint->bInterval);


  • BULK

Não possui.

  • CONTROL

Não possui.

  • ISOCHRONOUS

Não possui.

Registro do driver (register)

editar
  • Como o driver é reconhecido

Uma vez que a estrutura usb_driver estiver definida, o dispositivo poderá ser registrado no sistema através da chamada à função usb_register, como segue no código:

static int __init usb_mouse_init(void)
{
	int retval = usb_register(&usb_mouse_driver);
	if (retval == 0)
		info(DRIVER_VERSION ":" DRIVER_DESC);
	return retval;
}
  • Quais procedimentos (comandos) para verificar qual dispositivo específico que foi reconhecido


A estrutura struct usb_device_id *id_table: aponta para a lista de estruturas, na qual cada item identifica um dispositivo suportado para que, no momento da inserção de um novo dispositivo, o USB core possa verificar se o driver pode ou não controlá-lo, comparando suas características com os dados desta tabela.

static struct usb_device_id usb_mouse_id_table [] = {
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
		USB_INTERFACE_PROTOCOL_MOUSE) },
	{ }	/* Terminating entry */
};



  • Quais drivers que foram carregados para o dispositivo, etc;

Comunicação

editar
  • Arquivos especiais de dispositivo (/proc, /dev e /sys)



  • Criação de interface


O USB core disponibiliza para ca driver a estrutura struct usb_interface, e a partir desta estrutura que o driver gerencia o dispositivo.

static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev(intf);
	struct usb_host_interface *interface;
//...
        interface = intf->cur_altsetting;
}



  • Criação de endpoint


Toda a comunicação entre um dispositivo USB e o USB host começa ou termina em um endpoint.

No kernel do Linux, os endpoints são acessados através da estrutura struct usb_host_endpoint(definida em usb.h), na qual contém uma subestrutura chamada struct usb_endpoint_descriptor que aponta para as informações reais do endpoint, exatamente no formato especificado pelo dispositivo.

Neste driver esta estrutura é definida na função usb_mouse_probe, como segue no exemplo:

static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    struct usb_endpoint_descriptor *endpoint;
//...
    endpoint = &interface->endpoint[0].desc;
	if (!usb_endpoint_is_int_in(endpoint))
		return -ENODEV;
//...
}


  • Criação de configuração (configuration)


As configurações são utilizadas para permitir que um mesmo dispositivo possa operar de formas diferentes, conforme apropriado.
O linux permite o acesso às configurações de um dispositivo através da estrutura struct usb_host_config e ao dispositivo como um todo por meio da estrutura struct usb_device. O USB core exige que as informações sobre o dispositivo lhe sejam passadas no forma da estrutura usb_device.

Neste driver ela é especificada na função usb_mouse_probe.


static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev(intf);
//...
}


  • Tipos de comunicação utilizados pelo driver (control, bulk, etc);

O driver usbmouse.c utiliza somente a comunicação do tipo INTERRUPT, para transmissão de pequenas quantidade de dados mediante a solicitação do computador ou do dispositivo em uma taxa fixa. Método também utilizado por teclados.

O nome deste tipo de transmissão se deve ao fato de ocorrer em intervalos pré-definidos. Em alguns caso, é utilizada para controlar o dispositivo.

Finalização

editar
  • Mensagens de remoção do dispositivo
  • Desalocação/liberação de estruturas;

Qualquer funcionalidade registrada deve ser desalocada da memória quando não for mais necessária. Neste caso o driver é desalocado através da função usb_deregister invocada na função de saída do módulo.


static void __exit usb_mouse_exit(void)
{
	usb_deregister(&usb_mouse_driver);
}

Considerações finais

editar
  • Informações gerais do driver
  • Conclusões
  • Mapa conceitual sobre o driver estudado;

Bibliografia/Referências

editar