Red de conocimiento informático - Conocimiento del nombre de dominio - Cómo escribir un controlador de dispositivo USB en Linux

Cómo escribir un controlador de dispositivo USB en Linux

Las cosas más básicas que se deben hacer al escribir un controlador USB son: el dispositivo que el controlador debe admitir, registrar el controlador USB, detectar y desconectar, enviar y controlar urb (bloque de solicitud USB)

p>

Dispositivos admitidos por el controlador: existe una estructura struct usb_device_id. Esta estructura proporciona una lista de diferentes tipos de dispositivos USB admitidos por el controlador. Para un controlador que solo controla un dispositivo USB específico, la tabla struct usb_device_id es. definido como:

/* Lista de dispositivos admitidos por el controlador*/

estructura estática usb_device_id skel_table [] = {

{ USB_DEVICE(USB_SKEL_VENDOR_ID , USB_SKEL_PRODUCT_ID) } ,

{ } /* Entrada de terminación*/

};

MODULE_DEVICE_TABLE (usb, skel_table);

Para controladores de PC, Se requiere MODULE_DEVICE_TABLE, y usb debe ser el primer valor de esta macro, y USB_SKEL_VENDOR_ID y USB_SKEL_PRODUCT_ID son los ID del fabricante y del producto de este dispositivo especial. Cambiamos los valores definidos en el programa a nuestro Este USB, como por ejemplo:

/*Defina el fabricante y el número de identificación del producto*/

#define USB_SKEL_VENDOR_ID 0x1234

#define USB_SKEL_PRODUCT_ID 0x2345

Estos dos valores ​​Se puede pasar mediante el comando lsusb. Por supuesto, primero debe conectar el dispositivo USB al host. O puede obtenerlo consultando el manual del dispositivo USB del fabricante. Al ejecutar lsusb en mi máquina se obtiene el siguiente resultado:

Bus 004 Dispositivo 001: ID 0000: 0000

Bus 003 Dispositivo 002. : ID 1234: 2345 Abc Corp.

Bus 002 Dispositivo 001: ID 0000:0000

Bus 001 Dispositivo 001: ID 0000:0000

Consigue estos dos Después de establecer el valor, simplemente defínalo en el programa.

Registrar controlador USB: La estructura que deben crear todos los controladores USB es struct usb_driver. Esta estructura debe ser completada por el controlador USB e incluye una serie de funciones de devolución de llamada y variables que describen el controlador USB al código del núcleo USB.

Para crear una estructura struct usb_driver válida, solo necesita inicializar cinco campos en el programa marco, es así:

static struct usb_driver skel_driver = {

.owner = THIS_MODULE. ,

.name = "esqueleto",

.probe = skel_probe,

.disconnect = skel_disconnect,

.id_table = skel_table ,

};

Sondear y desconectar: ​​cuando se monta un dispositivo y el núcleo USB cree que el controlador debería manejarlo, se llama a la función de sondeo y se pasan las comprobaciones de la función de sondeo. eso información del dispositivo para determinar si el controlador es realmente adecuado para el dispositivo. La función de desconexión se llama cuando el controlador no debería controlar el dispositivo por algún motivo y puede realizar algún trabajo de limpieza.

En la función de devolución de llamada de detección, el controlador USB inicializa cualquier estructura local que pueda usarse para controlar el dispositivo USB. También guarda cualquier información requerida relacionada con el dispositivo en una estructura local y envía y controla la urb: cuando el controlador tiene datos para enviar. al dispositivo USB (principalmente en la función de escritura del controlador), debe asignar una urb para transmitir los datos al dispositivo:

/* Crear una urb y asignarle un caché*/

urb = usb_alloc_urb(0, GFP_KERNEL);

if (!urb) {

retval = -ENOMEM;

ir a error

}

Cuando urb se asigna correctamente, se debe crear un búfer DMA para enviar datos al dispositivo de manera eficiente y pasarlos a Los datos del controlador se deben copiar a este búfer:

buf = usb_buffer_alloc(dev-gt;udev, count, GFP_KERNEL, amp;urb-gt;transfer_dma);

if (!buf) {

retval = -ENOMEM;

ir a error

}

if (copy_from_user(buf, user_buffer, count)) {

retval = - EFAULT;

goto error;

}

Cuando los datos se copian correctamente desde el espacio del usuario a Después del búfer local, la urb debe inicializarse correctamente antes. se puede enviar al núcleo USB:

/* Inicializar urb */

usb_fill_bulk_urb(urb, dev-gt; udev,

usb_sndbulkpipe(dev-gt udev, dev-gt; Bulk_out_endpointAddr),

buf, count, skel_write_bulk_callback,

urb-gt; = URB_NO_TRANSFER_DMA_MAP;

la urb se puede enviar al núcleo USB para transferirla al dispositivo:

/* Enviar datos desde el puerto de SALIDA masivo*/

retval = usb_submit_urb(urb, GFP_KERNEL);

if (retval) {

err("s - error al enviar la urb de escritura, error d", __FUNCTION__, retval);

ir a error

}

Cuando urb se transfiere exitosamente al dispositivo USB, el núcleo USB llamará a la función de devolución de llamada de urb. En nuestro ejemplo, inicializamos urb, haciéndolo apuntar a la función skel_write_bulk_callback. La siguiente es la función:

static void skel_write_b

ulk_callback(struct urb *urb, struct pt_regs *regs)

{

struct usb_skel *dev;

dev = (struct usb_skel *)urb-gt; contexto;

if (urb-gt; estado amp;

!(urb-gt; estado == -ENOENT ||

urb-gt ; estado == -ECONNRESET ||

urb-gt; status == -ESHUTDOWN)) {

dbg("s - estado de escritura masiva distinta de cero recibido: d",

p>

__FUNCTION__, urb-gt; status);

}

/* Liberar el búfer asignado*/

usb_buffer_free(urb- gt; dev, urb-gt;transfer_buffer_length,

urb-gt;transfer_buffer, urb-gt;transfer_dma);

}

A veces el controlador USB Solo para enviar o recibir algunos datos simples, el controlador también puede transmitir datos sin urb. Esto implica dos funciones de interfaz simples: usb_bulk_msg y usb_control_msg. La operación de lectura en este programa marco USB es una aplicación:

/* Realizar bloqueo. lectura masiva para obtener datos del dispositivo*/

retval = usb_bulk_msg(dev-gt;udev,

usb_rcvbulkpipe(dev -gt; udev, dev-gt; Bulk_in_endpointAddr),

dev-gt; Bulk_in_buffer,

min(dev-gt; Bulk_in_size, recuento),

amp;

/*Si se lee correctamente, cópielo al espacio de usuario*/

if (!retval) {

if ( copy_to_user(buffer, dev-gt; Bulk_in_buffer, count))

retval = -EFAULT;

else

retval = count

}

La función de interfaz usb_bulk_msg se define de la siguiente manera:

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,

void *data, int len, int *actua

l_length, int timeout);

Los parámetros son:

struct usb_device *usb_dev: puntero al dispositivo USB de destino enviado por el mensaje por lotes.

tubería int unsigned: el punto final específico del dispositivo USB al que se envía el mensaje por lotes. Este valor se crea llamando a usb_sndbulkpipe o usb_rcvbulkpipe.

void *data: Si es un punto final OUT, es un puntero a los datos que se enviarán al dispositivo. Si es un punto final IN, es un puntero a la ubicación donde se deben almacenar los datos leídos desde el dispositivo.

int len: el tamaño del búfer al que apunta el parámetro de datos.

int *actual_length: puntero a la ubicación donde se almacena el número real de bytes transferidos. Si se transfiere al dispositivo o se recibe desde el dispositivo depende de la dirección del punto final.

int timeout: El tiempo de espera en Jiffies. Si el valor es 0, la función espera el final del mensaje.

Si la función de interfaz se llama correctamente, el valor de retorno es 0; de lo contrario, se devuelve un valor de error negativo.

La función de la interfaz usb_control_msg se define de la siguiente manera:

int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout)

Además de permitir que el controlador envíe y reciba mensajes de control USB, la función usb_control_msg funciona de manera similar a la función usb_bulk_msg. Sus parámetros tienen varias diferencias importantes con respecto a los de usb_bulk_msg:

p>

struct usb_device *dev: Puntero al dispositivo USB de destino al que se envía el mensaje de control.

tubería int unsigned: el punto final específico del dispositivo USB de destino al que se envía el mensaje de control. Este valor se crea llamando a usb_sndctrlpipe o usb_rcvctrlpipe.

__u8 request: Valor de solicitud USB del mensaje de control.

__u8 requesttype: Valor del tipo de solicitud USB del mensaje de control.

Valor __u16: Valor del mensaje USB del mensaje de control.

__u16 index: Valor del índice del mensaje USB del mensaje de control.

void *data: Si es un punto final OUT, es un puntero a los datos que están a punto de enviarse al dispositivo. Si es un punto final IN, es un puntero a la ubicación donde se deben almacenar los datos leídos desde el dispositivo.

Tamaño __u16: el tamaño del búfer señalado por el parámetro de datos.

int timeout: El tiempo de espera que se debe esperar en Jiffies. Si es 0, la función esperará el final del mensaje.

Si la llamada a la función de interfaz tiene éxito, devuelve el número de bytes transferidos o leídos desde el dispositivo; si no tiene éxito, devuelve un valor de error negativo.

Ninguna de estas dos funciones de interfaz se puede llamar en un contexto de interrupción o mientras se mantiene un bloqueo de giro. De manera similar, esta función no puede ser cancelada por ninguna otra función, así que tenga cuidado al usarla.

Queremos escribir un controlador para un dispositivo USB desconocido. Solo necesitamos modificar ligeramente este programa marco para usarlo. Ya hemos mencionado que necesitamos modificar los números de identificación del fabricante y del producto, y cambiarlos. el 0xfff0 cambia ambos valores al número de ID del USB desconocido.

#define USB_SKEL_VENDOR_ID 0xfff0

#define USB_SKEL_PRODUCT_ID 0xfff0

La otra cosa es escribir el tipo de punto final de la interfaz que debe detectarse en la función de detección. este programa marco Solo se detectan los puntos finales de entrada y salida masivos (USB_ENDPOINT_XFER_BULK). Puede usar la máscara (USB_ENDPOINT_XFERTYPE_MASK) aquí para permitirle detectar otros tipos de puntos finales. El controlador detectará cada interfaz del dispositivo USB una vez. exitoso, el controlador está vinculado a esta interfaz. Luego está el problema de la inicialización de urb. Si solo escribe un controlador USB simple, no necesita pensar demasiado en ello. Aquí presentamos brevemente tres funciones auxiliares para inicializar urb.

usb_fill_int_urb: Su prototipo de función es así:

void usb_fill_int_urb(struct urb *urb, struct usb_device *dev,

unsigned int pipe, void *transfer_buff ,

int buffer_length, usb_complete_t complete,

void *context, int intervalo);

Esta función se utiliza para inicializar correctamente la urb que será enviada a el punto final de interrupción del dispositivo USB.

usb_fill_bulk_urb: Su prototipo de función es así:

void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev,

unsigned int pipe, void * transfer_buffer ,

int buffer_length, usb_complete_t complete)

Esta función se utiliza para inicializar correctamente el punto final urb por lotes.

usb_fill_control_urb: Su prototipo de función es así:

void usb_fill_control_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, unsigned char *setup_packet, void *transfer_buffer, int buffer_length, usb_complete_t complete, void *context);

Esta función se utiliza para inicializar correctamente el punto final de la urb de control.

También hay una urb isócrona de inicialización, que aún no tiene una función de inicialización, por lo que deben inicializarse manualmente en el controlador antes de enviarse al núcleo USB. Puede consultar el árbol de código fuente del kernel. .konicawc.c en /usr/src/~/drivers/usb/media.