Driver USB

Introdução

Drivers de dispositivos são programas que realizam a interface entre o dispositivo e o

sistema operacional. A partir da definição da arquitetura de drivers de um sistema operacional,

para qualquer dispositivo pode ser escrito um driver que se adeque à esta arquitetura, e assim

o dispositivo pode ser manipulado por este sistema operacional.

No Linux os dispositivos são tratados como arquivos, então um driver deverá ler e

escrever informações no dispositivo e passar isso para nivel de usuário a partir de um arquivo.

Requisitos

Um driver deve ter como requisito não afetar o funcionamento do sistema operacional ao

qual vai ser acoplado. Como os drivers rodam em nível de kernel eles têm acesso privilegiado ao

computador, assim podem causar problemas facilmente. Dessa forma é necessário rigor no

tratamento de erros de forma que o driver não vá chegar a uma condição inesperada em que não

será possível prever seu comportamento.

Ferramentas

Para desenvolvimento de um driver é necessário se utilizar uma linguagem de programação

que gere código de máquina para o processador utilizado. Sendo assim as linguagens mais

indicadas, e as mais utilizadas, são C e Assembler.

Assim é necessário se utilizar de ferramentas de software que irão transformar o código

em linguagem de máquina no formato de um driver, como por exemplo o GCC. É necessário também de

utilizar de utilitários do sistema para criar o arquivo que será utilizado na troca de dados

com o nível de usuário, como mknod.

Desenvolvimento

Os drivers utilizam uma estrutra padrão com funções que serão chamadas na inicialização

e finalização do driver, assim como funções que serão chamadas quando se acessa o dispositivo

a partir do nível de usuário utilizando o arquivo que representa o dispositivo, e também uma

funcão de callback que será chamada quando o for terminada a escrita no dispositivo.

Todo driver precisa definir a licensa sobre a qual está sujeito, isto é feito utlizando-se a

macro MODULE_LICENSE.

A definição das funções de inicialização e remoção do driver é feita utilizando-se module_init

e module_exit, informando um ponteiro para a função que deve ser chamada em cada caso.

Para desenvolvimento de um driver para um dispositivo USB, em sua rotina de

inicialização o driver deve se registrar no subsistema USB do Linux. Nesta fase de registro o

driver irá informar identificadores que irão ser verificados nos dispositivos para saber os

dispositivos que são suportados por aquele driver, o identificadores são um ID do produtor e um

ID do produto. Deverá informar um nome para o driver, e ponteiros para funções que serão

chamadas quando um dispositivo aceito pelo driver for conectado, ou quando se quiser

desconectar um dispotivo que estava sendo controlado pelo driver.

No código fonte do kernel do linux há um arquivo criado pelo equipe de desenvolvimento do kernel

que pode ser utilizado como um modelo para criação de um driver USB. Apresento aqui o código:

1 /*
  2  * USB Skeleton driver - 2.0
  3  *
  4  * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
  5  *
  6  *      This program is free software; you can redistribute it and/or
  7  *      modify it under the terms of the GNU General Public License as
  8  *      published by the Free Software Foundation, version 2.
  9  *
 10  * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c 
 11  * but has been rewritten to be easy to read and use, as no locks are now
 12  * needed anymore.
 13  *
 14  */
 15 
 16 #include <linux/config.h>
 17 #include <linux/kernel.h>
 18 #include <linux/errno.h>
 19 #include <linux/init.h>
 20 #include <linux/slab.h>
 21 #include <linux/module.h>
 22 #include <linux/kref.h>
 23 #include <asm/uaccess.h>
 24 #include <linux/usb.h>
 25 
 26 
 27 /* Define these values to match your devices */
 28 #define USB_SKEL_VENDOR_ID      0xfff0
 29 #define USB_SKEL_PRODUCT_ID     0xfff0
 30 
 31 /* table of devices that work with this driver */
 32 static struct usb_device_id skel_table [] = {
 33         { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
 34         { }                                     /* Terminating entry */
 35 };
 36 MODULE_DEVICE_TABLE (usb, skel_table);
 37 
 38 
 39 /* Get a minor range for your devices from the usb maintainer */
 40 #define USB_SKEL_MINOR_BASE     192
 41 
 42 /* our private defines. if this grows any larger, use your own .h file */
 43 #define MAX_TRANSFER            ( PAGE_SIZE - 512 )
 44 #define WRITES_IN_FLIGHT        8
 45 
 46 /* Structure to hold all of our device specific stuff */
 47 struct usb_skel {
 48         struct usb_device *     udev;                   /* the usb device for this device */
 49         struct usb_interface *  interface;              /* the interface for this device */
 50         struct semaphore        limit_sem;              /* limiting the number of writes in progress */
 51         unsigned char *         bulk_in_buffer;         /* the buffer to receive data */
 52         size_t                  bulk_in_size;           /* the size of the receive buffer */
 53         __u8                    bulk_in_endpointAddr;   /* the address of the bulk in endpoint */
 54         __u8                    bulk_out_endpointAddr;  /* the address of the bulk out endpoint */
 55         struct kref             kref;
 56 };
 57 #define to_skel_dev(d) container_of(d, struct usb_skel, kref)
 58 
 59 static struct usb_driver skel_driver;
 60 
 61 static void skel_delete(struct kref *kref)
 62 {       
 63         struct usb_skel *dev = to_skel_dev(kref);
 64 
 65         usb_put_dev(dev->udev);
 66         kfree (dev->bulk_in_buffer);
 67         kfree (dev);
 68 }
 69 
 70 static int skel_open(struct inode *inode, struct file *file)
 71 {
 72         struct usb_skel *dev;
 73         struct usb_interface *interface;
 74         int subminor;
 75         int retval = 0;
 76 
 77         subminor = iminor(inode);
 78 
 79         interface = usb_find_interface(&skel_driver, subminor);
 80         if (!interface) {
 81                 err ("%s - error, can't find device for minor %d",
 82                      __FUNCTION__, subminor);
 83                 retval = -ENODEV;
 84                 goto exit;
 85         }
 86 
 87         dev = usb_get_intfdata(interface);
 88         if (!dev) {
 89                 retval = -ENODEV;
 90                 goto exit;
 91         }
 92 
 93         /* increment our usage count for the device */
 94         kref_get(&dev->kref);
 95 
 96         /* save our object in the file's private structure */
 97         file->private_data = dev;
 98 
 99 exit:
100         return retval;
101 }
102 
103 static int skel_release(struct inode *inode, struct file *file)
104 {
105         struct usb_skel *dev;
106 
107         dev = (struct usb_skel *)file->private_data;
108         if (dev == NULL)
109                 return -ENODEV;
110 
111         /* decrement the count on our device */
112         kref_put(&dev->kref, skel_delete);
113         return 0;
114 }
115 
116 static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
117 {
118         struct usb_skel *dev;
119         int retval = 0;
120         int bytes_read;
121 
122         dev = (struct usb_skel *)file->private_data;
123         
124         /* do a blocking bulk read to get data from the device */
125         retval = usb_bulk_msg(dev->udev,
126                               usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
127                               dev->bulk_in_buffer,
128                               min(dev->bulk_in_size, count),
129                               &bytes_read, 10000);
130 
131         /* if the read was successful, copy the data to userspace */
132         if (!retval) {
133                 if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))
134                         retval = -EFAULT;
135                 else
136                         retval = bytes_read;
137         }
138 
139         return retval;
140 }
141 
142 static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
143 {
144         struct usb_skel *dev;
145 
146         dev = (struct usb_skel *)urb->context;
147 
148         /* sync/async unlink faults aren't errors */
149         if (urb->status && 
150             !(urb->status == -ENOENT || 
151               urb->status == -ECONNRESET ||
152               urb->status == -ESHUTDOWN)) {
153                 dbg("%s - nonzero write bulk status received: %d",
154                     __FUNCTION__, urb->status);
155         }
156 
157         /* free up our allocated buffer */
158         usb_buffer_free(urb->dev, urb->transfer_buffer_length, 
159                         urb->transfer_buffer, urb->transfer_dma);
160         up(&dev->limit_sem);
161 }
162 
163 static ssize_t skel_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos)
164 {
165         struct usb_skel *dev;
166         int retval = 0;
167         struct urb *urb = NULL;
168         char *buf = NULL;
169         size_t writesize = min(count, (size_t)MAX_TRANSFER);
170 
171         dev = (struct usb_skel *)file->private_data;
172 
173         /* verify that we actually have some data to write */
174         if (count == 0)
175                 goto exit;
176 
177         /* limit the number of URBs in flight to stop a user from using up all RAM */
178         if (down_interruptible(&dev->limit_sem)) {
179                 retval = -ERESTARTSYS;
180                 goto exit;
181         }
182 
183         /* create a urb, and a buffer for it, and copy the data to the urb */
184         urb = usb_alloc_urb(0, GFP_KERNEL);
185         if (!urb) {
186                 retval = -ENOMEM;
187                 goto error;
188         }
189 
190         buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma);
191         if (!buf) {
192                 retval = -ENOMEM;
193                 goto error;
194         }
195 
196         if (copy_from_user(buf, user_buffer, writesize)) {
197                 retval = -EFAULT;
198                 goto error;
199         }
200 
201         /* initialize the urb properly */
202         usb_fill_bulk_urb(urb, dev->udev,
203                           usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
204                           buf, writesize, skel_write_bulk_callback, dev);
205         urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
206 
207         /* send the data out the bulk port */
208         retval = usb_submit_urb(urb, GFP_KERNEL);
209         if (retval) {
210                 err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
211                 goto error;
212         }
213 
214         /* release our reference to this urb, the USB core will eventually free it entirely */
215         usb_free_urb(urb);
216 
217 exit:
218         return writesize;
219 
220 error:
221         usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
222         usb_free_urb(urb);
223         up(&dev->limit_sem);
224         return retval;
225 }
226 
227 static struct file_operations skel_fops = {
228         .owner =        THIS_MODULE,
229         .read =         skel_read,
230         .write =        skel_write,
231         .open =         skel_open,
232         .release =      skel_release,
233 };
234 
235 /* 
236  * usb class driver info in order to get a minor number from the usb core,
237  * and to have the device registered with the driver core
238  */
239 static struct usb_class_driver skel_class = {
240         .name =         "skel%d",
241         .fops =         &skel_fops,
242         .minor_base =   USB_SKEL_MINOR_BASE,
243 };
244 
245 static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id)
246 {
247         struct usb_skel *dev = NULL;
248         struct usb_host_interface *iface_desc;
249         struct usb_endpoint_descriptor *endpoint;
250         size_t buffer_size;
251         int i;
252         int retval = -ENOMEM;
253 
254         /* allocate memory for our device state and initialize it */
255         dev = kzalloc(sizeof(*dev), GFP_KERNEL);
256         if (dev == NULL) {
257                 err("Out of memory");
258                 goto error;
259         }
260         kref_init(&dev->kref);
261         sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
262 
263         dev->udev = usb_get_dev(interface_to_usbdev(interface));
264         dev->interface = interface;
265 
266         /* set up the endpoint information */
267         /* use only the first bulk-in and bulk-out endpoints */
268         iface_desc = interface->cur_altsetting;
269         for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
270                 endpoint = &iface_desc->endpoint[i].desc;
271 
272                 if (!dev->bulk_in_endpointAddr &&
273                     ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
274                                         == USB_DIR_IN) &&
275                     ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
276                                         == USB_ENDPOINT_XFER_BULK)) {
277                         /* we found a bulk in endpoint */
278                         buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
279                         dev->bulk_in_size = buffer_size;
280                         dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
281                         dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
282                         if (!dev->bulk_in_buffer) {
283                                 err("Could not allocate bulk_in_buffer");
284                                 goto error;
285                         }
286                 }
287 
288                 if (!dev->bulk_out_endpointAddr &&
289                     ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
290                                         == USB_DIR_OUT) &&
291                     ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
292                                         == USB_ENDPOINT_XFER_BULK)) {
293                         /* we found a bulk out endpoint */
294                         dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
295                 }
296         }
297         if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
298                 err("Could not find both bulk-in and bulk-out endpoints");
299                 goto error;
300         }
301 
302         /* save our data pointer in this interface device */
303         usb_set_intfdata(interface, dev);
304 
305         /* we can register the device now, as it is ready */
306         retval = usb_register_dev(interface, &skel_class);
307         if (retval) {
308                 /* something prevented us from registering this driver */
309                 err("Not able to get a minor for this device.");
310                 usb_set_intfdata(interface, NULL);
311                 goto error;
312         }
313 
314         /* let the user know what node this device is now attached to */
315         info("USB Skeleton device now attached to USBSkel-%d", interface->minor);
316         return 0;
317 
318 error:
319         if (dev)
320                 kref_put(&dev->kref, skel_delete);
321         return retval;
322 }
323 
324 static void skel_disconnect(struct usb_interface *interface)
325 {
326         struct usb_skel *dev;
327         int minor = interface->minor;
328 
329         /* prevent skel_open() from racing skel_disconnect() */
330         lock_kernel();
331 
332         dev = usb_get_intfdata(interface);
333         usb_set_intfdata(interface, NULL);
334 
335         /* give back our minor */
336         usb_deregister_dev(interface, &skel_class);
337 
338         unlock_kernel();
339 
340         /* decrement our usage count */
341         kref_put(&dev->kref, skel_delete);
342 
343         info("USB Skeleton #%d now disconnected", minor);
344 }
345 
346 static struct usb_driver skel_driver = {
347         .name =         "skeleton",
348         .probe =        skel_probe,
349         .disconnect =   skel_disconnect,
350         .id_table =     skel_table,
351 };
352 
353 static int __init usb_skel_init(void)
354 {
355         int result;
356 
357         /* register this driver with the USB subsystem */
358         result = usb_register(&skel_driver);
359         if (result)
360                 err("usb_register failed. Error number %d", result);
361 
362         return result;
363 }
364 
365 static void __exit usb_skel_exit(void)
366 {
367         /* deregister this driver with the USB subsystem */
368         usb_deregister(&skel_driver);
369 }
370 
371 module_init (usb_skel_init);
372 module_exit (usb_skel_exit);
373 
374 MODULE_LICENSE("GPL");
375

Instalação

Para utilização do driver este deve ser instalado junto ao sistema operacional. No

Linux os drivers são considerados como módulos do kernel, assim o utilitário insmod permite que

instalemos manualmente o driver utilizando o comando insmod <driver>.ko

Testes

Para realização de testes do driver devemos escrever um programa que rode a nível de

usuário e faça uso do arquivo que representa o dispositivo. Assim com uma rotina definida

podemos avaliar se o dispositivo responde como esperado, validando a implementação do driver,

assim como utilizar uma rotina que gere erros e analisando os recursos do sistema verificar se

o driver se comporta estávelmente.

Referências

http://www.freesoftwaremagazine.com/articles/drivers_linux
http://www.linuxjournal.com/article/4786
http://www.gelato.unsw.edu.au/lxr/source/drivers/usb/usb-skeleton.c?a=i386

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License