Anda di halaman 1dari 13

1

Programacin de dispositivos sobre buses estndares

Bibliografa
La informacin presentada en estas transparencias puede
ser consultada y ampliada en los siguientes libros:

Para consultar informacin sobre el bus PCI: Linux Device Drivers 3 edicin, captulo 12. Understanding Linux Network Internals, captulo 6. Para consultar informacin sobre algunas de las funciones mencionadas en las transparencias: Linux Device Drivers 3 edicin, captulos 8 y 9. NOTA: Las imgenes utilizadas en las transparencias han sido tomadas de Linux
Device Drivers 3 edicin.

El bus PCI
Desarrollado por Intel, convertido a estndar abierto.

Admite espacio de direcciones de memoria y de E/S

Versiones
Datos y direcciones de 32 o 64 bits Frecuencias disponibles: 33, 66 o 133 (PCI-X) MHz

Caractersticas de velocidad superiores a buses anteriores


(ISA, MCA, EISA, etc)

ISA: 16MB/s EISA: 33MB/s PCI 32bits, 66MHz: 264MB/s

Establece mecanismos de configuracin automtica de los


dispositivos PCI

Operacin en el bus PCI


Capacidad de bus mastering por cualquiera de los dispositivos que lo soporten. Puente PCI:

Conecta el procesador/cache/memoria al bus PCI. Permite el acceso directo de la CPU a las direcciones de memoria o E/S de los dispositivos PCI. Permite el acceso directo de los dispositivos PCI maestros a la memoria principal. 4 lneas de interrupcin (de INTA hasta INTD) que pueden redirigirse por el puente PCI a una o varias IRQs. Todos los dispositivos PCI con interrupciones deben soportar interrupciones compartidas.

Interrupciones:

Otros pines indican al puente la presencia de dispositivos conectados al bus y


la potencia elctrica consumida.

Jerarqua del bus PCI


Puente PCI
Dispositivo 0

Hasta 256 buses Hasta 32 dispositivos por bus


Dispositivo 1

Hasta 8 funciones por dispositivo Por ejemplo, una tarjeta puede ser una capturadora de video y una tarjeta grfica VGA

Dispositivo 2

Identificacin de cada funcin de


dispositivo en el bus:

N BUS 8 bits

N DISPOSITIVO 5 bits

N FUNCION 3 bits

Hardware: direccin de 16 bits

Kernel de Linux: La direccin hardware se almacena en variables de tipo struct pci_dev


NOTA: Cada funcin se comporta como un dispositivo independiente dentro de la tarjeta. A partir de ahora utilizaremos el trmino dispositivo para referirnos a las funciones.

Espacios de direcciones PCI:


Direcciones de Memoria y E/S
Existen 3 espacios de direcciones:

Memoria, entrada/salida y configuracin. Existe un espacio de memoria y un espacio de E/S, ambos son compartidos por todos los dispositivos del bus y por la CPU. Cada dispositivo utiliza un rango diferente de direcciones de memoria o de E/S (dos dispositivos no pueden usar la misma direccin de memoria o de E/S). Se accede a los dispositivos usando las direcciones pertenecientes a su rango. El driver puede acceder al espacio de E/S con las funciones vistas en la prctica 6: inb, inw, inl, outb, outw, outl. El driver puede acceder al espacio de memoria con las siguientes funciones, definidas en <asm/io.h>:
unsigned int ioread8(void *direccion); unsigned int ioread16(void *direccion); unsigned int ioread32(void *direccion); void iowrite8(u8 valor, void *direccion); void iowrite16(u16 valor, void *direccion); void iowrite32(u32 valor, void *direccion);

Espacios de direcciones de memoria y E/S


Direcciones de Memoria y E/S Ejemplo (I)


Dispositivo 1 Dispositivo 2 Puente PCI RAM Espacio de Memoria Supongamos que ambos dispositivos utilizan 256 bytes del espacio de direcciones de memoria. 0x0 256 MB RAM 0x0FFFFFFF Durante el arranque del sistema, los dispositivos podran ser mapeados como muestra la figura. Dispositivo 1 Dispositivo 2 0x1000A000 0x1000A0FF 0x1000B000 0x1000B0FF CPU

Espacios de direcciones PCI:

Direcciones de Memoria y E/S Ejemplo (II)


Supongamos que un dispositivo PCI tiene dos registros de 16 bits en las
direcciones de E/S (o puertos de E/S) DIR1 y DIR2:
u16 valor = inw( DIR1 ); // Leemos un valor del puerto DIR1 outw( 40, DIR2 ); // Escribimos en el puerto DIR2

Espacios de direcciones PCI:

Supongamos que un dispositivo PCI utiliza un rango de direcciones de memoria


que va desde la direccin BASE hasta BASE+50.
u8 *pbase = BASE; u8 valor = ioread8(BASE); // Leemos un byte de BASE u8 valor2 = ioread8(pbase+3); // Leemos un byte de BASE+3 iowrite8( 0, BASE+5 ); // Escribimos 0 en BASE+5 iowrite8( 1, pbase+6 ); // Escribimos 1 en BASE+6 *pbase = valor;
Esta operacin es vlida en x86 pero no es portable a otras arquitecturas. Adems, en el caso de E/S mapeada en memoria, hay que evitar que el compilador modifique el orden en el que se realizan las lecturas y escrituras en memoria.

Espacios de direcciones PCI:


Espacio de Configuracin
Espacio de configuracin:

Contiene los registros de configuracin del dispositivo. Cada dispositivo tiene su propio espacio de configuracin. Para acceder al espacio de configuracin de un dispositivo, es necesario especificar el dispositivo (n bus, n dispositivo, n funcin) y la direccin del registro de configuracin.

Durante el arranque, la BIOS o el kernel de Linux configura todos los


dispositivos PCI accediendo a sus espacios de configuracin:

Los rangos de direcciones de memoria y de E/S de cada dispositivo se mapean en los espacios de direcciones de memoria y E/S de la CPU. Se asigna un nmero de interrupcin a cada dispositivo. El kernel crea una especie de base de datos con los dispositivos encontrados.

Cuando el driver accede al dispositivo, este ya ha sido debidamente

configurado: tan slo tiene que consultar la configuracin leyendo los registros de configuracin.

10

Espacio de configuracin
Registros obligatorios

El espacio de configuracin (256 bytes) se dividide en dos regiones:


Cabecera de 64 bytes

Los primeros 16 bytes son iguales para todos los tipos de dispositivos. El resto depende del campo Header Type (en la figura se muestra el tipo 00h).

192 bytes dependientes del dispositivo

Espacio de configuracin:
Acceso desde el driver
Funciones para leer del espacio de configuracin de un dispositivo:
int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val); int pci_read_config_word(struct pci_dev *dev, int where, u16 *val); int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);

11

Funciones para escribir en el espacio de configuracin de un dispositivo:


int pci_write_config_byte(struct pci_dev *dev, int where, u8 val); int pci_write_config_word(struct pci_dev *dev, int where, u16 val); int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);

Parmetros:

dev: dispositivo a cuyo espacio de configuracin se va acceder. where: direccin de configuracin a la que se quiere acceder (vase la figura de la transparencia anterior). Existen macros definidas para cada registro de configuracin. val: direccin donde se guardar el valor ledo o valor que se quiere escribir. Devuelven 0 si la operacin se ha realizado correctamente; sino, devuelven un cdigo de error.

Valor devuelto: Fichero de cabecera: <linux/pci.h> Este fichero contiene todas las
funciones relacionadas con el PCI que veremos en las transparencias.

Acceso desde el driver - Ejemplos


Supongamos que la variable struct pci_dev *dispositivo contiene un valor que
identifica a un dispositivo PCI concreto del bus (el kernel proporciona dicho valor al driver a travs de la funcin probe, que se estudiar ms adelante).

Espacio de configuracin:

12

Para leer el registro Cache Line (direccin 0xc) debemos escribir:


if (pci_read_config_byte( dispositivo, 0xC, &cache_line )) { /* Error en el acceso al espacio de configuracion */ }

Para leer el registro Command (direccin 0x4) debemos escribir:


if (pci_read_config_word( dispositivo, 0x4, &command )) { /* Error en la lectura del espacio de configuracion */ }

Para escribir el valor 3 en el registro IRQ Line (direccin 0x3c):


if (pci_write_config_byte( dispositivo, 0x3c, 3 )) { /* Error en la escritura del espacio de configuracion */ } El fichero <linux/pci.h> define macros con las direcciones de cada uno de los registros del espacio de configuracin.

Identificacin del tipo de dispositivo


Vendor ID: Identificador del fabricante

Espacio de configuracin:

13

Los valores son establecidos por el PCI SIG (Special Interest Group). Por ejemplo, el valor 0x8086 corresponde a Intel 0xFFFF indica que no hay dispositivo conectado Su valor lo establece el fabricante (no necesita ser registrado por el PCI SIG) El par (VendorID, DeviceID) identifica al dispositivo hardware Sus valores son definidos por el PCI SIG. Se divide en tres campos de un byte:

Device ID: Identificador del dispositivo Class Code: Indica la clase a la que pertenece el dispositivo segn su funcin
Clase base (byte alto): Indica la clase genrica a la que pertenece el dispositivo.
Ejemplos: sin clase (00h), almacenamiento (01h), redes (02h), vdeo (03h) Subclase (byte medio): Indica la funcin especfica del dispositivo. Ejemplos: controlador SCSI (0100h), controlador IDE (0101h), adaptador token ring (0201h), adaptador VGA (0300h) Interface de programacin (byte bajo): Define la interface de programacin a nivel de registro.

Macros definidas para el acceso a los registros anteriores: PCI_VENDOR_ID,

PCI_DEVICE_ID, PCI_CLASS_PROG, PCI_CLASS_DEVICE (incluye la Clase base y la Subclase)

Espacio de configuracin:
Registros de direccin base (I)
Entrada/Salida La direccin base de cada regin est definida por un registro BAR (Base Address Register)
Memoria
Direccin Base

14

Un dispositivo PCI puede tener hasta 6 regiones de direcciones de memoria o de

31/63

3 X

2 1 X X

0 0

31

E/S
Direccin Base

1 0

0 1

Tipo: 00 : Direccin de 32 bits 01 : Reservado 10 : Direccin de 64 bits 11 : Reservado Prefetchable (admite prebsqueda): 0 : nonprefetchable 1 : prefetchable

En el caso de que la direccin de memoria sea de 64 bits, se usan dos registros BAR consecutivos para especificar el rango. Si la memoria admite prebsqueda la CPU puede guardarla en la cach y el acceso desde el driver se puede realizar como si fuera memoria RAM. En caso contrario, se comporta como E/S mapeada en memoria. El tamao de cada regin tambin puede consultarse con los registros BAR

Espacio de configuracin:
Registros de direccin base (II)
Toda la informacin relativa a las diferentes regiones de direcciones de memoria
o E/S se obtiene con las siguientes funciones:
unsigned long pci_resource_start(struct pci_dev *dev, int bar); Devuelve la primera direccin de la regin indicada por el parmetro bar unsigned long pci_resource_end(struct pci_dev *dev, int bar); Devuelve la ltima direccin de la regin indicada por el parmetro bar unsigned long pci_resource_flags(struct pci_dev *dev, int bar); Devuelve los flags asociados a la regin indicada por el parmetro bar:
IORESOURCE_IO : Es una regin de direcciones de E/S IORESOURCE_MEM: Es una regin de direcciones de memoria IORESOURCE_PREFETCH: La regin de memoria admite prebsqueda

15

Parmetros:

dev: dispositivo a cuyo espacio de configuracin se quiere acceder. bar: indica el nmero de registro BAR que define la regin. Puede ser un valor del 0 al 5, o las macros PCI_BASE_ADDRESS_0 hasta PCI_BASE_ADDRESS_5.

Fichero de cabecera para los flags: <linux/ioports.h>

Registros de direccin base Ejemplo


Supongamos que tenemos la variable disp debidamente inicializada para identificar a un
dispositivo PCI del bus: struct pci_dev *disp; ... inicio = pci_resource_start( disp, PCI_BASE_ADDRESS_0 ); tam = pci_resource_end( disp, PCI_BASE_ADDRESS_0 ) inicio + 1; if (pci_resource_flags(disp,PCI_BASE_ADDRESS_0) & IORESOURCE_MEM) { Espacio de if (!request_mem_region( inicio, tam, midispositivo)) { direcciones /* Error */ de memoria } if ((base = ioremap( inicio, tam )) == NULL) { Esta operacin es /* Error */ vlida en x86 pero } no es portable a iowrite8( 0x3f, base ); // *base = 0x3f; otras arquitecturas estado = ioread16( base + 2 ); ... }else { if (request_region(inicio, tam, midispositivo) != NULL) { Espacio de direcciones outb(0x3f,inicio); de E/S ...

Espacio de configuracin:

16

Preparando el acceso a memoria o E/S


Las siguientes funciones utilizadas en el ejemplo anterior pueden ser
consultadas en los captulos 8 y 9 de Linux Device Drivers 3 edicin. struct resource *request_region(unsigned long start, unsigned long n, char *name); Solicita al kernel el uso de n puertos de E/S a partir de la direccin start para el dispositivo name. Devuelve NULL en caso de que los puertos solicitados no estn disponibles. <linux/ioport.h> struct resource *request_mem_region(unsigned long start, unsigned long len, char *name); Solicita al kernel la asignacin de len bytes de memoria a partir de la direccin start para el dispositivo name. Devuelve NULL en caso de error. <linux/ioport.h> void *ioremap(unsigned long phys_addr, unsigned long size); Asigna un rango de direcciones de memoria virtuales al rango de direcciones fsicas [phys_addr, phys_addr+size]. Devuelve la direccin base virtual. <asm/io>

Espacio de configuracin:

17

Preparando el acceso a memoria o E/S Ejemplo


CPU Dispositivo 1 Dispositivo 2 Puente PCI RAM

Espacio de configuracin:

18

Direcciones virtuales 0x0 256 MB RAM 0x0FFFFFFF 0x90000000 0x900000FF 0xA0000000 0xA00000FF Direcciones virtuales asignadas por ioremap a las direcciones fsicas

Direcciones fsicas 0x0 256 MB RAM 0x0FFFFFFF

Dispositivo 2 Dispositivo 1

Sistema de paginacin

Dispositivo 1 Dispositivo 2

0x1000A000 0x1000A0FF 0x1000B000 0x1000B0FF

Liberando los rangos de direcciones


Las siguientes funciones son complementarias a las mencionadas en la
transparencia 17 (ser consultadas en los captulos 8 y 9 de Linux Device Drivers 3 edicin). void release_region(unsigned long start, unsigned long n); Libera la regin de E/S especificada void release_mem_region(unsigned long start, unsigned long len); Libera la regin de memoria indicada. void iounmap(void * virtual_addr); Libera el rango de direcciones de memoria virtuales asignadas con ioremap.

Espacio de configuracin:

19

Espacio de configuracin:
Interrupciones PCI

20

Registros:

IRQ Line: Indica el nmero de interrupcin asociada al dispositivo. IRQ Pin: Indica que lnea de interrupcin (INTA, INTB, INTC, INTD) est conectada al dispositivo (el valor 0 indica que el dispositivo no usa interrupciones). Accede a los registros utilizando las funciones vistas anteriormente.

Driver:

PCI_INTERRUPT_LINE: direccin del registro IRQ Line PCI_INTERRUPT_PIN: direccin del registro IRQ Pin

Ejemplo:
resultado = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &mi_irq); if (resultado) { /* error en la lectura del espacio de configuracin */ }

Registrando el driver (I)


En el proceso de registro, el driver PCI debe indicar al kernel qu tipo de dispositivos
maneja. Para especificar el tipo de dispositivo PCI se utiliza la estructura struct pci_device_id, con los siguientes campos:

Driver PCI:

21

__u32 vendor; __u32 device; Indica el Vendor ID y el Device ID del tipo de dispositivo. Para indicar cualquier tipo de identificador se usa el valor PCI_ANY_ID. __u32 class; __u32 class_mask; Se utilizan para indicar la clase de dispositivo PCI. El campo class_mask se utiliza para seleccionar parte de los bytes en los que se divide el campo Class Code. __u32 subvendor; (SIN COMENTAR) __u32 subdevice; (SIN COMENTAR) kernel_ulong_t driver_data; (SIN COMENTAR)

Ejemplo:
struct pci_device_id scsi_id = {.vendor = PCI_ANY_ID, .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .class = 0x020100, .class_mask = ~0}; Adaptador Token Ring

Registrando el driver (II)


La estructura struct pci_driver contiene toda la informacin que el kernel necesita
para registrar el driver. Consta de los siguientes campos (entre otros):

Driver PCI:

22

const char *name; Nombre del driver (debe ser nico). const struct pci_device_id *id_table; Puntero a un vector de estructuras pci_device_id que indica los tipos de dispositivos que maneja el driver. El ltimo elemento del vector debe tener todos los campos a cero. int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); Puntero a la funcin probe del driver. El kernel llama a esta funcin cuando encuentra un dispositivo en el bus que debe ser controlado por el driver. El kernel le pasa a la funcin el dispositivo encontrado y su tipo en los parmetros dev e id, respectivamente.

void (*remove) (struct pci_dev *dev); Puntero a la funcin remove del driver. El kernel llama a esta funcin cuando el dispositivo es eliminado del sistema.

Para registrar el driver desde el mdulo de linux, se utiliza la siguiente funcin:


int pci_register_driver( struct pci_driver *drv );

En el momento de descargar el driver, es necesario deshacer el registro:


void pci_unregister_driver( struct pci_driver *drv );

Registrando el driver: una visin general


Durante el arranque del sistema, el kernel configura todos los dispositivos PCI accediendo a
sus registros de configuracin y guarda una base de datos con todos los dispositivos del sistema.

Driver PCI:

23

Un mdulo que implemente un driver para un dispositivo PCI debe registrar el driver PCI en
la funcin de inicializacin del mdulo. El mdulo le indica al kernel qu tipo de dispositivos PCI maneja (p.e. mediante el VendorID, DeviceID)

El kernel busca dispositivos libres que puedan ser manejados por el mdulo y llama a la

funcin probe del mdulo tantas veces como dispositivos encuentre (una vez por cada dispositivo). En cada llamada le pasa a la funcin probe un valor de tipo struct pci_dev* para que la funcin pueda acceder al espacio de configuracin del dispositivo.

La funcin probe debe habilitar el dispositivo y leer su configuracin: nmero de

interrupcin, rango de direcciones de memoria o de E/S que utiliza (registros BAR), etc. accediendo a sus direcciones de E/S (inb, outb, etc.) o a sus direcciones de memoria (ioread8, iowrite8, etc.)

Despus de ejecutar la funcin probe, el mdulo ya puede manejar los dispositivos PCI

Registrando el driver Ejemplo (I)


Definimos los tipos de dispositivos que static const struct pci_device_id ej_ids [] = { maneja el driver. La macro PCI_DEVICE {PCI_DEVICE( PCI_VENDOR_ID_INTEL, sirve para inicializar un struct pci_device_id. PCI_DEVICE_ID_INTEL_82810_IG1 )}, { /*fin*/ } El ltimo elemento del array debe ser rellenado con ceros }; static int ej_probe( struct pci_dev *dev, const struct pci_device_id *id ); static void ej_remove( struct pci_dev *dev ); static struct pci_driver ej_pci_driver = { .name = midriver", .id_table = ej_ids, .probe = ej_probe, .remove = ej_remove, }; static int __init ej_inicio_modulo(void) { ... return pci_register_driver( &ej_pci_driver ); } static void __exit ej_fin_modulo(void) { ... pci_unregister_driver( &ej_pci_driver ); } Cuando se carga el mdulo (por ejemplo, con insmod) registramos el driver en el kernel. Cuando se descarga el mdulo (por ejemplo, con rmmod) deshacemos el registro del driver. Contina #include <linux/pci.h>

Driver PCI:

24

Registrando el driver Ejemplo (II)


Si hay dispositivos del tipo que maneja el driver, el kernel llama a la funcin probe para cada uno de ellos. El kernel le pasa el dispositivo y el elemento del campo id_tables static int ej_probe( struct pci_dev *dev, const struct pci_device_id *id ) { ... La funcin pci_enable_device se encarga de if (pci_enable_device( dev )) { /* error */ } despertar al dispositivo, entre otras cosas. ... Devuelve un cdigo de error. resultado = pci_read_config_byte( dev, PCI_INTERRUPT_LINE, &mi_irq ); ... La funcin probe se dir_inicio = pci_resource_start( dev, PCI_BASE_ADDRESS_0 ); encarga de inicializar el ... hardware del dispositivo, numero_mayor = register_chrdev(0,midriver,&operaciones); activar interrupciones, ... registrar el dispositivo, etc. } static void ej_remove( struct pci_dev *dev ) { ... pci_disable_device(dev); } ... module_init( ej_inicio_modulo ); module_exit( ej_fin_modulo ); Si un dispositivo se elimina del sistema, el kernel llama a la funcin remove correspondiente, indicndole el dispositivo.

Driver PCI:

25

Anda mungkin juga menyukai