|
G A C E T A D E L I N U X
...haciendo a Linux un poco más divertido! |
|
Tarjetas sintonizadoras: Aprender mirando Por Cherry George Mathew Traducción al español por Pepa Suárez Poch
|
Es increible el trabajo hoy en día. Uno tiene cientos de mensajes a los que responder, el informe de un análisis de calidad que entregar esta tarde, una presentación de negocios que preparar y un montón de códigos que estudiar para eliminar errores. Y no sólo eso, además echan en la televisión tu programa favorito, ese que no te perderías por nada del mundo. ¿Cómo solucionarlo? Encendiendo la tarjeta de TV, está claro, para así poder ver el programa en una ventana independiente en la parte superior izquierda de la pantalla del ordenador. ¡Es trabajo, no placer, por supuesto! Además, es possible minimizar la ventana de video si dejar rastro de ella siembre que al jefe le de por echar un vistazo por encima de tus hombros. Incluso podrías ejecutarla a pantalla completa y hacerle una señal para que viniese, si es que también es seguidor del programa. ;-) ¡Ah, las maravillas de la tecnología!
La plataforma Linux soporta un gran número de tarjetas sintonizadoras, así como cámaras web y una amplia gama de otros dispositivos multimedia. Además, al igual que ocurre con otros sistemas operativos, las áreas de los programas de aplicación y el kernel proper estan bien delimitados y diseñados con claridad. Video4Linux, o V4L, nombre que recibe la tecnología, sigue estando en proceso de desarrollo de la version 1, todavía un borrador, a la versión 2, mucho más robusta. Al mismo tiempo, se están creando una gran cantidad de drivers, en principio relacionados con el conjunto de chips, pero cada vez más en lo que se refiere a otro tipo de modelos. Los programas de aplicación se centran en desarrollar interfaces basadas en GUI sencillas para el usuario, tanto para ver televisión, como para grabar al disco, decodificar y leer el teletexto y demás. En lo que se refiere a la visualización de la televisión, el programador de aplicaciones tiene que realizar tareas tales como abrir una ventana del tamaño justo en la pantalla, ordenar al driver adecuado que proyecte la imagen dentro de dicha ventana (overlay), cambiar el tamaño del área de visualización y conseguir que el tamaño de la imagen se ajuste al mismo tiempo, así como sintonizar, a petición del usuario, un determinado canal o cambiar la entrada del sintonizador a modo AV, o simplemente desactivar el sonido. Por tanto, la aplicación se encuentra en el lado opuesto al driver de sintonización y pasa, a petición del usuario, al diver siguendo un procedimiento previaente establecido que recibe el nombre de Application Programer Interface (API).
Esto se explicará en detalle más adelante.Por otro lado, los programadores de drivers de dispositivo se centran en traducir las peticiones del usuario, mencionadas con anterioridad, en intrucciones del hardware a la tarjeta sintonizadora. También se aseguran de que exista comunicación con las aplicaciones por medio de la API del V4L . Por lo tanto los drivers de dispositivo están situados entre el hardware y la aplicación, reciben órdentes de estos, las traducen y las envían al hardware subyacente, y todo esto en la jerga propia de las máquinas.
En las próximas páginas pondremos a prueba
nuestra paciencia. Mostraremos, el uno al otro, entre otras cosas, cómo
funcionan las tarjetas sintonizadoras de televisión, de qué están
hechas, qué tipos existen, cómo hacerlas funcionar a través
en Linux, etc. Utilizo la expresión «mostrar el uno al otro»
ya que, al intentar redactar este artículo, tuve que realizar algunas
investigaciones. ¡Y todo por ti, querido lector! Así que es mutuo.
Por lo tanto, coge lapiz y papel, pónte cómodo y a leer.
Aviso: No dormirse en los laureles. Se realizará una prueba al final
Palabras clave: bus PCI , bus I2C , IF (Frecuencia Intermedia), Procesador de
video, memoria intermedia, DMA y IRQ.
Muy bien, ahora veamos qué aspecto tiene una tarjeta sintonizadora de televisión. Lo más normal es que en ella localicemos al menos tres chips funcionales.
El «chip» sintonizador es en realidad un panel con todos los componentes de radio frecuencia, muy bien recubierto por una capa plateada, es decir, por una capa protectora. Echa un vistazo al diagrama. Los módulos de sintonización aparecen en paquetes distintos, los cuales presentan a menudo un aspecto muy similar. El cable de antena se conecta directamente en la ranura que se encuentra en un extremo del módulo de sintonización. La función del módulo de sintonización es realizar trucos con el mezclador de radio frecuencia, con el que se sintonizan los distintos programas de televisión. Sea cual sea la frecuencia que utilice el canal de televisión, esta se convierte en una frecuencia intermedia (IF) predeterminada. En verdad esta frecuencia «predeterminada» es desastrosa por motivos históricos (¿políticos?). Cada señal de televisión (por ejemplo PAL, SECAM, NTSC, etc) utiliza una IF propia. Sea cual sea ésta, el sintonizador tiene un sólo cometido: reconocer los miles de frecuencias de ondas de radio existentes en el universo y, a petición tuya, filtrar para ti sólo la cadena adecuada. En la «sección I2C»5, descubriremos como «a petición tuya» el módulo sintonizador encuentra tu canal deportivo favorito.
La IF que llega del módulo sintonizador tiene que ser descodificada y transformada a un formato visionable. En esto consiste el trabajo del Procesador de Video. Los formatos visionables, por razones históricas, de nuevo, pueden presentar varias formas y tamaños. Cuentas con el antiguo formato plano de mapa de bits, formato VGA, paletizado y planarizado (sea cual sea su significado), formato RGB (siglas inglesas de Red Green Blue, rojo, verde y azul), formato YUV (y todas sus ligeras variantes) y, por supuesto, unos cuantos formatos registrados. Si tienes habilidad para leer entre líneas, tendrías que haver adivinado que la «transformación» a la que me refería antes incluye demodulación y conversión de señal análoga a digital, que, al fin y al cabo, es de lo que se tratan las tarjetas sintonizadoras. Cuando estás viendo la televisión en tu ordenador, lo que realmente estás presenciando es cómo el adaptador VGA muestra los datos de video digitalizados del procesador de video. Bien, ya podemos dividir el proceso en dos pasos:
Cualquier tipo de dato que se encuentre en la memoria intermedia es visualizada de forma inmediata en la pantalla. De esto se ocupa el controlador VGA. Si lo que quieres es mostrar algo en la pantalla, lo único que tienes que hacer es volcar los datos a la memoria intermedia y ¡voila! al momento aparecerán en la pantalla. En la mayoría de las plataformas, para copiar datos de la memoria sólo es necesario la memoria plana ya que la memoria intermedia, al igual que cualquier otra memoria RAM, viene mapeada en una dirección de memoria física. Sin embargo, en un sistema que utiliza algún tipo de protección de memoria, el acceso directo de las aplicaciones al sistema RAM no está permitido. En Linux, esto se controla por medio del sistema mmap() que opera junto con el nodo de dispositivo /dev/ram o el driver de la memoria intermedia. Para obtener más información a cerca de mmap(), echa un vistazo al manual. Está claro que, para conseguir que esto funcione de forma correcta, tienes que dejar bien claro al controlador qué es lo que quieres que aparezca en la pantalla, qué información has enviado a la memoria intermedia y dónde se encuentra. Esto se consigue «activando el modo VGA», con lo que cada bit de datos que se encuentre en la memoria intermedia pasa a ser reconocida por el controlador VGA. Por ejemplo, en el caso de que el modo VGA se configure a "640x480" y 8 bpp, el controlador VGA sabrá dos cosas a cerca de lo que va a ser mostrado:
Imaginemonos por un momento la típica tarjeta sintonizadora. Ésta ha sido programada para sintonizar un canal en concreto, capturar datos de video del mismo píxel a píxel en formato digital (por ejemplo bpp o YUV), para después volcarlos a la memoria RAM. Este procedimiento recibe el nombre de «captura de video». A continuación están enumeradas algunas de las posibilidades para capturar datos de video:
Por lo general, las tarjetas sintonizadoras procesan el sonido de dos formas distintas. El primer método utiliza el procesador de audio para demodular el sonido en frecuencia intermedia (IF contiene tanto información de audio como de video). Las señales de audio obtenidas de esta foma tienen que ser dirigidas a un jack de audio externo, desde el cual sería necesario redirigirlas a la entrada de línea de una tarjeta de sonido independiente a través de un cable externo adecuado. Si no te puedes permitir el lujo de comprar una tarjeta de sonido, la entrada de línea de tu aparato de música valdrá :-).
Por otro lado, el procesador de audio tiene que demodular el sonido en frecuencia intermedia, convertirlo en muestras digitales para después utilizar técnicas como el DMA, de la que se hablará en el apartado sobre «buses PCI», para enviar dichas muestras a la tarjeta de sonido, a través del bus interno de sistema (por ejemplo el bus PCI). A continuación utilizará la tarjeta de sonido para volver a convertir las muestras digitales en señal de audio. Este proceso es más complejo pero más flexible, ya que los niveles de sonido de televisión se pueden controlar desde la propia tarjeta sintonizadora. Mediante el primer método, sólo nos podríamos permitir este lujo con el driver de sonido de una tarjeta de sonido independiente. Sea como sea, resumamos ahora lo que se nos exige y aquello que debemos exigir como buenos programadores de drivers de tarjetas sintonizadoras.
Alan Cox escribió un magnífico artículo sobre la API de Video4Linux para tarjetas de captura en Linux. Este artículo se encuentra, junto con la documentación sobre el kernel, en (Documentation/DocBook/videobook.tmpl)2 y abarca gran cantidad de temas relacionados con la API de Video4Linux. A lo que no hace referencia es a los detalles sobre los procesos de captura de las tarjetas sintonizadoras. Intentar referirse a los detalles sobre todos los tipos de dispositivos de captura de televisión en un mismo artículo resultaría imposible. Sin embargo una relación de las tarjetas sintonizadoras disponibles hoy en día (no me hago cargo de cámaras web u otros dispositivos que hay que enchufar en el puerto USB) se ajustaría a la información que se presenta aquí.
linux/videodev.h3 es la referencia autorizada para la API de V4L. Por lo tanto evitaremos realizar descripciones detalladas de la API de V4L. Cualquier tipo de detalle conceptual sobre el mismo se puede obtener en el artículo de Alan Cox al que nos hemos referido antes. Por otra parte la API de V4L está en proceso de desarrollo. Lo que es aplicable hoy, puede no serlo mañana.
Antes de nada, echemos un vistazo al mecanismo se pone en marcha durante la comunicación entre una aplicación y el driver. Si ya conoces los dispositivos de carácter, esto te resultará repetitivo y podrás saltarte esta parte sin problema.
En todo sistema de Unix, el subdirectorio /dev contiene unos archivos especiales llamados nodos de dispositivo. Cada uno de ellos está asociado a un número específico de dispositivo registrado en el kernel. En Linux, al driver del Video4Linux le corresponde el número de dispositivo 81. Por convención, el nombre del nodo asociado con este número es /dev/video0. Para más detalles sobre numeración de nodos de dispositivo, ver Documentation/devices.txt. En el caso de que el nodo /dev/video0 no existiera, se podría crear con el comando mknod desde la estructura raíz tal y como se muestra a continuación:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main(){
char *buffer;
/* Asignar un espacio de memoria lo más amplio posible. */
buffer = malloc(65535);
/* Abrir el nodo del dispositivo para su lectura. */
if((fd = open("/dev/video0", O_RDONLY))<0)
fprintf(stderr, "Sorry, error opening device /dev/video0\n");
exit(-1);
}
/* Leer hasta que el programa esté acabado o el dispositivo se quede sin datos (poco probable).*/
while( read(fd, buffer, 65535)) write(0, buffer, 65535);
free(buffer);
}
¿Quieres abrir la ventana para ver la televisión?
ioctl(fd, VIDIOCSAUDIO, &v);
int video_exclusive_release(struct inode *inode, struct file *file);
int video_usercopy(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg, int (*func)(struct inode *inode, struct file *file, unsigned int cmd, void *arg));
Llegados a este punto, lo que podemos hacer es centrar todas nuestras energías a escribir el código para programar el hardware sintonizador para que realice funciones tales como comenzar captura, activar el sonido, copiar datos de un sitio a otro, etc. La mayoría de los ioctls del V4L se reducen a resolver este tipo de problemas. Finalmente, cuando todo está listo, podremos centrarnos en enlazar la última API de V4L con nuestro código subyacente. Esto es una práctica común en ingeniería.
--------------- Fragmento-------------------
General de brigada: «Teniente, quiero que al anochecer ese puente esté terminado.»
Ingeniero: «Vaya, creo que será imposible, señor. Es necesario tomar medidas en el terreno y pedir el material a los proveedores antes de poder pensar en comenzar a construir. Eso no llevará por lo menos un par de semanas, ¡señor!»
General de brigada: «Me está diciendo que no tienen puntales, tuercas o medidores de ángulos.¡Nada de nada con lo que puedan comenzar a trabajar inmediatamente!»
Ingeniero: «Pues, no, señor. No había pensado que necesitaríamos repuestos en un plazo de tiempo tan corto.»
Se escucha un disparo.
Fin de la primera toma.
--------------- Fin del fragmento ----------------Comencemos a construir las partes.
Una de las funciones del driver tiene que ver con la adquisición de datos de video: asegurarse de que el módulo sintonizador está correctamente sintonizado, de que el procesador de video decodifica la señal correcta (ejemplo: PAL, NTSC, etc.), y de que las propiedades de la imagen, como brillo, tono, saturación y otras responsabilidad del hardware del procesador de video estén ajustadas, correctamente sintonizadas o establecidas en sus valores por defecto. La adquisición de audio puede ser también tarea del driver. Este aspecto está explicado en detalle en el apartado sobre I2C.
La otra parte del driver se encarga de asegurarse de que los datos adquiridos aparecen en la pantalla de forma correcta. Esta parte del driver tiene que asegurarse del correcto funcionamiento del sistema en el caso de que el video se visualice en una ventana al tiempo que otras ventanas, correspondientes a otras aplicaciones, están abiertas. Aspectos relacionados con los parámetros que se ven afectados cuando la ventana de video es restaurada o arrastrada a otro lugar, tales como el área de visualización de la ventana, el número de líneas o píxels adquiridos, etc., son responsabilidad de esta parte del driver. Profundicemos un poco más en el problema de la superposición de ventanas. En un sistema basado en ventanas, como Xwindows, la visualización del video se lleva a cabo en una ventana. El problema de la superposición comienza cuando una esquina de la ventana de otra aplicación se superpone a la ventana de video.
Existen dos posibilidades:
Lo que podemos hacer es comenzar programando rutinas
que se ocupen de pequeñeces, tales como establecer el chroma key
o el tamaño de la ventana de video, colocar la ventana correctamente,
etc. La mejor forma de aprender este tipo de cosas es fijándose en un
modelo. Centraremos nuestro estudio en algunos fragmentos de código de
un driver no oficial que yo mismo estoy desarrollando para el Pixelview Combo
TV plus. Esta es una tarjeta muy simple, tan simple como las tarjetas sintonizadoras
pueden llegar a ser. El módulo sintonizador, el procesador de video y
el controlador VGA se encuentran en la misma tarjeta. Ésta se conecta
en la ranura PCI y funciona tanto de tarjeta sintonizadora como de tarjeta VGA.
Descripción de la tarjeta:
El buffer de video puede estar situado en cualquier
parte de la memoria intermedia, pero normalmente está al final de ésta.
Esto previene que las muestras de datos de video capturados borren muestras
de gráficos que ya estaban en la memoria intermedia y viceversa.
Pongamos un ejemplo:
| Tamaño de la memoria intermedia | = | 2MB |
| Modo de visualización | = | 640x480 @ 16bpp. |
| Memoria necesaria para la visualización de VGA | = | 640 x 480 x 2 bytes |
| = | 614400 bytes | |
| = | 0.59 MB |
| Memoria sin utilizar al final de la memoria intermedia | = | 2MB - 0.59MB |
| = | 1.41 MB |
La ventana del hardware interpreta y muestra los datos que son de su competencia de una forma completamente distinta a como dicta el modo VGA. El tamaño y la situación de la ventana de video pueden ser modificados si se programan los registros VGA adecuados. El GD-5446 cuenta con tres grupos de registros llamados: registros de control, registros de gráficos y registros de secuencia. A cada uno de estos registros VGA se puede acceder por medio de múltiples lecturas y escrituras a los puertos del hardware, y por lo tanto están rodeados por funciones especializadas. Los he denominado gd_read_cr(), gd_write_cr() Esto mejora la legibilidad del código a la vez que reduce las probabilidades de error. A continuación aparecen algunas de las rutinas del driver que estoy creando. Para ahorrar tiempo las he descompuesto en partes:
#define GD_SR_OFFSET 0x3c4
#define GD_GR_OFFSET 0x3ce
#define GD_CR_OFFSET 0x3d4
/* Adaptador - funciones de nivel bajo */
unsigned value;
io_writeb(reg, gd_io_base + GD_CR_OFFSET);
value = io_readb(gd_io_base + GD_CR_OFFSET + 1);
return value;
}
Éstas son algunas funciones de nivel más alto:
void gd_enable_window();
static void gd_set_window(,,,);
void gd_set_pitch(
{
CR3C = gd_read_cr(card_p, 0x3c);
CR3D = gd_read_cr(card_p, 0x3d);
/* CR3C[5] = offset[11], CR3D = offset[10:3]*/
gd_bit_copy(&CR3C, 5, &offset, 11, 11);
gd_bit_copy(&CR3D, 0, &offset, 3, 10);
gd_write_cr(card_p, CR3C, 0x3c);
gd_write_cr(card_p, CR3D, 0x3d);
gd_write_cr() se utiliza para escribir un valor en un registro VGA específico. Por ahora ignoraremos la variable card_p. Consiste en una estructura en la que se almacena información global sobre el estado del driver. card_p es utilizado por gd_write_cr únicamente con fines de contabilidad. gd_write_cr(card_p, CR3C, 0x3c) se ocupará de escribir el contenido de la variable CR3C dentro del registro de control 0x3c. (que no te confunda la designación CR3C, se trata de una variable).
En el caso general de una tarjeta sintonizadora, cuyo controlador VGA no ofrezca una ventana de video del hardware independiente, el procesador de video tendrá que volcar las imágenes justo en medio de los datos de gráficos. Este proceso se tendrá que llevar a cabo de tal manera que, cuando el controlador VGA muestre el contenido nuevo de la memoria intermedia, el video aparezca de forma correcta, sin distorsiones. Para ello es necesario que los datos de video se alineen de acuerdo con los límites de resolución (cada bate a 8bpp, uno de cada dos bytes a 16bpp, uno de cada cuatro bytes a 32bpp, etc.). Además de esto, la representación de los píxels dentro del procesador de video debe coincidir con la del modo actual del controlador VGA. El procesador de video no puede adquirir video a 32bpp y volcarlo a una memoria intermedia de 16bpp. Es más, los datos de video no se pueden presentar de una manera lineal continuada. Los recursos del buffer de cada línea deberán ser calculados como se indica en el gráfico que aparece a continuación:
Video Buffer Offset = Video Buffer Offset + Video Window Pitch x Line No.
Dicho de otra manera, todas las precauciones y cálculos que realiza el Xserver cuando dibuja una ventana de aplicación, tienen que ser tomadas por el procesador de video. En este caso, el procesador de datos modifica únicamente el buffer de gráficos y no realiza distinción alguna entre datos de video y de gráficos.
Sin embargo, en el caso del GD-5446, el procesador de video no escribe en el área de gráficos ni se preocupa de temas de alineación. De lo que se tienen que asegurar las rutinas de un procesador de video es de que el video sea capturado dentro del recurso correcto de la memoria intermedia, donde comienza el buffer de video. La rutina gd_set_vbuf1() se ocupa de eso por nosotros. Por lo tanto, el hardware GD-5446 se ocupa de lo relacionado con la actividad de las ventanas.
Para más detalles sobre la descripción de los registros GD5446 del hardware, echa un vistazo al manual de referencia técnica de GD-5446.
Ha llegado el momento de realizar una visita guiada por el camino que recorre una llamada del IOCT. Partamos del instante en el que una aplicación del video4linux, como por ejemplo xawtv (ver: http://bytesex.org), conecta con el ioctl() para que active la ventana de televisión.
La aplicación solicita información al driver de aplicación sobre los chroma keys disponibles y selecciona uno. Entonces pasa a rellenar el fondo de la ventan de video con un único color. Los elementos superpuestos ya pueden ser pintados sobre la ventana de aplicación, y la captura de video se active. Por supuesto, los datos de video aparecen sólo las zonas que no pueden ser superpuestas, las cuales se rellenan con el fondo del chroma key.
En lo que se refiere a la captura sintonizadora, el
GD-5446 tiene dos características interesantes: cuenta con una interfaz
del bus I2C a través de dos clavijas y con una interfáz de puerto
de video a través de 16 pines. La interfaz de puerto de video sigue el
modelo del estándar ITU-656 para operaciones de intercambio de datos
de video. No te eches atrás en este punto. ¿Te acuerdas que los
píxels pueden estar formados por más de un byte? Ejemplo: 16 bpp
es igual a dos bytes. Alguien tendría que decirles a los fabricantes
de chips que, en el caso de múltiples bytes por píxel, las transmisiones
entre chips tendrían que realizarse siguiendo un orden. En el caso de
YUV, Y se refiere al brillo, U y V se refiere a los dos colores que componen
un píxel. Que cada componente ocupe un byte no es muy factible en el
mundo real del formato YUV 4:2:2 pero, que narices, podemos poner ejemplos hasta
que nos cansemos. Un píxel, por lo tanto, necesita 3 bytes, es decir
24bits. Ahí va la propuesta: si eres un fabricante de chips y quieres
alardear de una línea extra en el listado de características(para
captar la atención de posibles clientes, por supuesto), ten en cuenta
el sello ITU-656. Pero, ten cuidado, en cuanto esté sellado, el espíritu
de la bestia residirá en tu chip. El video sólo se podrá
transmitir a partir de una determinada orden: U-Y-V. Ahí va la parte
Buena: ¡el VPX 3225D forma parte de la hermandad!, así que ahora
as cosas ya están en su lugar. El controlador VGA y el procesador de
video utilizan una vía clandestina de comunicación, algo con el
nombre VPort. Todavía hay más buenas noticias: el VPX 3225D también
cuenta con un bus I2C. ¡Sorpresa, sorpresa!
Bueno, serenémonos un poco y conozcamos lo que significa todo esto:
Otra tanda de preguntas:
Identifica el chip maestro en el bus I2C de la tarjeta sintonizadora Pixelview.
Echemos un vistazo a los dos pines: SDA y SCL:
TEl pin SDA es de datos. La SCL es la del reloj. La
primera puede ser utilizada tanto or un maestro como por un esclavo, dependiendo
de la dirección de la transferencia de datos. El pin SCL sólo
es utilizado por un chip maestro.
Como programadores de drivers de dispositivo de Linux, somos afortunados. La mayoría de la información sobre el nivel inferior y el nivel de pines está almacenada para nosotros en el kernel. Lo que tenemos que hacer es conectar algunas rutinas helper al kernel. Estas permiten al kernel comunicarse con el bus I2C de nuestra tarjeta sintonizadora. Estas rutinas se podrían comparar con un conductor de rally: no sólo sabe cómo conducir bien, también conocen sus coches a la perfección. Si hay que cambiar el aceite, si el amortiguador pierde líquido o si el disco de embrague está a punto de hacerse trizas son pequeños altercados de los que el conductor se dará cuenta al instante. Por otro lado, el copiloto conoce el terreno y la ruta como la palma de su mano. Por e so, segundos antes de la próxima curva muy cerrada, gritará "¡curva cerrada a la izquierda!", con lo que el conductor reducirá, acariciará el pedal del freno, hará un movimiento de la dirección asistida y, ya está, una curva menos que tomar. Más o menos ocurre lo mismo con el kernel, que conoce el protocolo I2C y sabe cuándo hay que cambiar los pines SDA y SCL. El kernel espeta las órdenes a las funciones del helper, que son las que verdaderamente realizan el cambio. Con el fin de que el kernel hable con las funciones del helper, éstas tendrán que estar registradas en el kernel. El kernel tiene una función de registro para estos casos: i2c_bit_add_bus(). We pass it a structure defined so in linux/i2c-algo-bit.h :
void (*setsda) (void *data, int state);
void (*setscl) (void *data, int state);
int (*getsda) (void *data);
int (*getscl) (void *data);
/* configuración local */
int udelay;
int mdelay;
int timeout;
struct i2c_algo_bit_data gd_bus;
gd_bus.setsda = gd54xx_setsda;
gd_bus.setscl = gd54xx_setscl;
gd_bus.getsda = gd54xx_getsda;
gd_bus.getscl = gd54xx_getscl;
udelay = 16;
mdelay = 10;
timeout = 200;
i2c_bus_add(&gd_bus);
Las variables udelay, mdelay y timeout son nuestro único contacto directo con los sincronizadores del bus I2C, cuando el kernel maneje los pines del I2C. No es necesario decir que el código anterior es un pseudo código y no funciona directamente. Algunos detalles han sido omitidos, pero saldrán a la luz en los próximos párrafos.
Dejad que os indique algunos documentos en el subdirectorio ('Documentation/i2c/')
que contienen información exhaustiva sobre la puesta en práctica
del I2C dentro del kernel. En concreto, ('Documentation/i2c/writing-clients')
es una introducción agradable de leer a los drivers del I2C.
Respuestas a la tanda de preguntas:
El GD-5446.
El kernel proporciona acceso a algunos chips maestros del I2C así como una interfaz directa a los pines SDA y al SCL. Esta interfaz recibe el nombre de bang interfaz. En el caso de la tarjeta sintonizadora Pixelview Combo TV plus, conseguimos acceso directo a los pines SDA y SCL del bus I2C a través del registro SR8 del controlador VGA GD-5446. SR8 es accesible a través de los puertos del hardware 0x3c4 y 0x3c5. He creado estos accesos utilizando las rutinas gd_read_sr() y gd_write_sr(). Referirse a (pvcl.c). A continuación aparece una descripción del registro de control del I2C, SR 8, del GD5446:
| I/O Port Address: | 3C5h |
| Index: | 08h |
| Bit | Descripcion |
|---|---|
| 7 | I2C SDA Readback |
| 6 | I2C Configuration |
| 5 | Reserved |
| 4 | Reserved |
| 3 | Reserved |
| 2 | I2C SCL Readback |
| 1 | I2C Data (SDA) Out |
| 0 | I2C Clock (SCL) Out |
Siempre que se produzca algún cambio en los bits del I2C bits dentro del registro SR8, éste se refleja en el bus I2C y todos los esclavos aprecian el cambio. Por ejemplo, si el bit 1 del SR8 pasa a 0, el GD-5446 coloca la línea SDA en un lugar inferior. Ocurre lo contrario si el bit 0 del SR8 pasa a 1. Es momento de que nos fijemos en el set_sda() y en el get_sda(). Como suele ocurrir, ambos pertenecen a pvcl.c, y han sido descompuestos para facilitar la lectura.
void gd54xx_setsda (int state)
{
set_bit(6, &i2c_state);
/* Fijar/borrar un bit */
state ? set_bit(1, &i2c_state) : clear_bit(1, &i2c_state);
gd_write_sr(, i2c_state, 0x8);
Lo que ocurre, a grandes rasgos, es que el gd54xx_setsda (1) mueve una línea del SDA hacia arriba, mientras que el gd54xx_setsda (0), los hace hacia abajo.
set_scl() funciona de forma parecida, exceptuando el caso en el que el pin SCL
se vea afectado.
Mediante la lectura del correspondiente bit de estado del SR8 se consigue conocer
el estado actual del pin SDA. En este caso, se trata del bit 7. Si el pin SDA
está en una posición alta, el bit 7 será igual a 1. Si
su posición es baja, entonces será igual a 0. Esto se puede observar
en una variable, como se muestra a continuación:
int gd54xx_getsda (i2c_state)
{
El primer concepto es el de adaptador.
Según linux/i2c.h: " i2c_adapter es la estructura que se utiliza para identificar un bus físico i2c junto con los algoritmos de acceso necesarios para acceder al mismo."En este caso, el bus I2C GD-5446 junto con el acceso del bit-bang forman el adaptador.
El segundo concepto es el de algoritmo:
Lo que el (linux/i2c.h) dice sobre los algoritmos es lo siguiente:
"(un algoritmo de acceso) ... es una interfaz para un tipo de solución de hardware, a la que se puede dirigir mediante los mismos algoritmos de bus, es decir, el bit-banging o el PCF8584, para nombrar dos de los más comunes."
Las funciones gd54xx_setsda(), gd54xx_getsda(), gd54xx_setscl() y gd54xx_getscl() , son funciones helper para el algoritmo de acceso bit-bang. Por lo tanto, nunca habrían existido si el bus I2C GD-5446 utilizara algún otro mecanismo, como por ejemplo la interfáz de I2C PCF 8584.
El tercer concepto tiene que ver con un cliente de I2C.
De nuevo (linux/i2c.h) es una referencia fiable:
"(Un cliente) ... identifica un dispositivo (es decir, un chip) que está conectado a un bus I2C."
En nuestro caso, tenemos dos únicos clientes: el VPX-3225D y el módulo sintonizador FM1216ME MK3 de Phillips. Mediante la asignación de ciertas direcciones a algunos chips, el protocolo I2C se asegura de que sólo de accede a un chip de cada vez. Por lo tanto, a cada cliente se le asocia un número de dirección. El VPX-3225D sólo responde a direcciones 0x86 and 0x87 o 0x8e and 0x8f, dependiendo del chip que esté configurado. El módulo sintonizador responde a la dirección 0xc6.
A cada transacción del I2C se le antepone la dirección de destino. De esto se tiene que encargar el maestro. Entonces, sólo los esclavos a los que se dirija el maestro podrán responder a sus peticiones. Este método también se puede utilizar para comprobar que el bus I2C es capaz de detectar algún chip. El kernel de Linux soporta este tipo de pruebas.
«Un driver es capaz de manejar más de un dispositivo físico presente en los adaptadores I2C. Esta información se utiliza para informar driver de los que sucede con el adaptador.»Al principio, puede parecer increíble que estemos hablando de un driver de dispositivo dentro de otro driver. Pero, ocurre que puede haber más de un chip en un adaptador en concreto, y cada uno de ellos tiene que ser programado de forma individual. Podemos denominar driver a toda parte de código, que entiende el funcionamiento de un elemento del hardware y lo programa de acuerdo con el mismo. En este caso, el driver podría ser simplemente un par de rutinas dentro de un módulo y, en este sentido, resulta que puede que haya más de un driver dentro de un módulo del kernel.
Podría resultar útil tener en cuenta que puesto en práctica el driver del I2C para el VPX-3225D dentro de otro archivo denominado vpx322xd.c Con esto, el código se divide entre el driver principal del V4L y la parte del vpx de forma correcta. Ambos drivers se comunicarán entre ellos a través de un acuerdo interno parecido al de la llamada IOCTL en el entorno del usuario. Curiosamente, el driver del módulo sintonizador FM1216ME MK3 de Phillips ya está disponible dentro del kernel 2.4 y puede ser compilado en un módulo independiente. Esto sirve de ejemplo de lo bien que funcionan la fuente abierta. Yo he aportado el adaptador y las funciones para las operaciones con ventanas; otra persona ha creado un driver sintonizador que funciones con mi adaptador, al cual yo añado un módulo de procesador de video del que dispongo; y otra persona ha escrito cliente del entorno del usuario de V4L que entiende la API de V4L. No está mal, ¿eh ?
Para comprender cómo crear el código del driver del I2C para el procesador de video (en este caso, el VPX-3225D), hay que saber dos cosas: el contexto y en entorno en el que el código va a funcionar.
Después de que todo esté dicho y hecho, la finalidad del driver VPX-3225D, es poner en práctica las instrucciones trasmitidas por las aplicaciones. Un driver I2C genérico, a la vez que se registra a sí mismo en el I2C de Linux, registra un elemento llamado función «comando». Una vez registrado, se puede conectar con esta función siguiéndole el rastro a través de una lista de adaptadores I2C disponibles. La lista de relaciones funciona de la siguiente manera: adapter-> clients[n]-> driver-> command, siendo n el cliente nth en un adaptador. Por lo tanto, adapter-> clients[n]-> driver-> command() se traduciría por«contactar con la función comando asociada al driver para el cliente "n" que se encuentra en el adaptador». Por supuesto, se puede acceder a la estructura del adaptador desde el driver principal del V4L, pvcl.c, que ha sido registrado por dicho adaptador en primer lugar. Por lo tanto, todos los clientes del adaptador y, por consiguiente, todos los driver de cliente y sus rutinas de «comando» rellamada, son accesibles a través del pvcl.c, con sólo atravesar la estructura del adaptador.
Marquemos ahora los pasos de una llamada ioctl() para activar la captura:
Así termina este apartado.
El bus PCI es el que se utiliza con más frecuencia el los ordenadores actuales. (Para inocentes novatos: un bus es un pedazo de cable o conjunto de cables a los que se conecta uno o más periféricos al mismo tiempo y, por lo tanto cuenta con un recurso compartido.) Aparte de su velocidad (33MHz como mínimo), el PCI es un bus plug and play. Por su puesto, esto no tiene nada que ver con los cables, ya que son como los cables de una lámpara de mesa. La diferencia reside en que un dispositivo conectado al bus PCI tiene que actuar de acuerdo con una serie de normas llamadas especificaciones PCI. Entre otras cosas, los dispositivos PCI, es decir, los dispositivos conectados al bus PCI, tienen que aportar información al bus maestro, sobre el nombre, el tipo y el número de los chips funcionales, así como sobre sus líneas IRQ preferentes, su capacidad de DMA, etc. Esto contribuye a la correcta distribución de recursos por parte del bus maestro. El bus maestro, en este caso, es un Proxy del procesador de sistema, que normalmente será un "dispositivo conductor" o un "dispositivo puente". No entraremos en detalles en este punto, ya que lo que nos interesa, como programadores de drivers de dispositivo, son tres cosas:
Identificación del dispositivo, el DMA y la asignación de la línea IRQ.
Linux cuenta con una serie de funciones para acceder ala información sobre los dispositivos PCI. Estas funciones, que ya han obtenido detalles sobre las tarjetas conectadas, se comunican con el hardware del PCI. Lo que nos interesa es la identificación del chip conectado a la tarjeta. pci_find_device() completa una estructura con el nombre de la tarjeta, la identificación del vendedor y identificación del chip de la tarjeta. Estos ID están disponibles en linux/pci_ids.h. Se encuentran allí porque los fabricantes de chips, de antemano, registraron sus dispositivos en una base de datos central pública.
En el caso de la tarjeta Pixelview, la operación de identificar el GD-5446 es muy sencilla. Busca el PCI_VENDOR_ID_CIRRUS y el PCI_DEVICE_ID_CIRRUS_5446. Si ambos campos están disponibles en la base de datos de la tarjeta, significa que ésta es controlada por CL-GD5446. Encuentra la función de sondeo en i2c_clgd54xx_find_card() en pvcl.c para obtener información sobré cómo ocurre eso.
Como ocurre con cualquier otro bus, el sistema del PCI permite la transferencia de datos sólo entre un maestro y un esclavo. El maestro inicia la conversación, y el esclavo responde con datos o peticiones. En el bus PCI, el maestro suele ser un Proxy del procesador de sistema. Este chip se comporta como el propio procesador de sistema, haciendo que los demás chips se sometan. De forma efectiva, los dispositivos del sistema reconocen al procesador en el Proxy y obedecen sus instrucciones. No obstante, el procesador es un chip muy ajetreado y no se puede dedicarse exclusivamente a transmitir datos entre los chips PCI sin dejar de lado otras funciones. Por eso, de vez en cuando, permite a otros chips esclavos actuar como maestros, bajo la responsabilidad del procesador de sistema. En tales casos, los nuevos maestros del bus tienen control sobre el bus PCI y pueden comenzar cualquier tipo de transferencia que deseen. Está claro que este nombramiento de maestro es temporal, y el procesador, en cuanto así lo quiera, puede revocar los poderes otorgados y devolver al chip a su situación inicial, con lo cual, el procesador asume de nuevo el control.
Tomemos el ejemplo de una tarjeta sintonizadora que decide transferir datos a la tarjeta VGA. El chip de la tarjeta sintonizadora informa de su intención de realizar esa operación enviando una petición DMA por una línea particular llamada DREQ, en el bus PCI. El chip del controlador del PCI, tras consultar con el procesador, a través líneas fuera del bus PCI, garantiza o deniega el cumplimiento de dicha petición. En cuanto la realización de dicha operación es garantizada, la tarjeta sintonizadora puede dirigirse al chip VGA, tal y como lo haría el procesador, y se puede iniciar la transmisión de datos a través del bus PCI, mientras el procesador de sistema puede ocuparse tranquilamente de otros asuntos. Si ocurriese que el procesador también necesitara acceder al chip VGA, sólo tendría que revocar los derechos del bus de la tarjeta sintonizadora y escribir al chip VGA, como hace siempre.
En buses más antiguos, un chip dedicado, llamado contralor DMA, se utilizaba para llevar el control de los buses delegados. La responsabilidad de asignar recursos al propio controlador DMA correspondía al kernel de sistema y, por ello, las ventajas del DMA estaban limitadas a un número reducido de dispositivos, en tales buses. En el caso del PCI, cualquier chip puede pasar a ser maestro de bus, y el controlador DMA puede ser situado en una tarjeta individual. Esto podría ocasionar disputas en la línea DREQ, el único punto que puede retrasar el proceso. Para aliviar el problema, varias líneas DREQ están disponibles en el bus PCI, con el controlador del bus PCI como árbitro entre DREQ simultaneas en múltiples líneas.
Los dispositivos tienen que informar al procesador de aquellas situaciones que no pueden ser previstas de forma anticipada. Estas situaciones reciben el nombre de eventos asincrónicos y pueden ser: la llegada de un paquete de datos a una tarjeta de red; la apertura de la bandeja del CD-ROM; la finalización, por parte del procesador de video, del llenado de una serie de datos de video; etc.
Los dispositivos informan de los eventos asincrónicos a través de una línea en el bus PCI llamada línea IRQ Interrupt Request Queue (siglas inglesas para línea de petición de interrupción). Las líneas IRQ constituyen unos recursos escasos en un bus, y el bus PCI no es una excepción. No obstante, si existiera algún medio para distinguir entre las diversas partes que pueden compartir la misma línea, las líneas IRQ podrían ser compartidas por los distintos dispositivos. El código que se ocupa de las peticiones IRQ recibe el nombre de Interrupt Service Routine, ISR (rutina de interrupción de servicio). El procesador, si así se lo indica un algún chip a la IRQ, activa de forma inmediata la ISR. Entonces la ISR lee los registros de cada dispositivo sospechoso, hasta que da con el dispositivo en la línea compartida culpable de enviar una IRQ y actúa en consecuencia para atender dicha petición. Atender una petición puede incluir tareas como guardar el paquete que se acaba de recibir, vaciar los buffers del sistema, o resetear los indicadores en el procesador de video. Cada una de estas acciones es específica de un dispositivo y, por ello, el driver habrá de incluir la ISR registrada con el kernel de sistema, de manera que pueda ser llamado en el momento de la interrupción.
Nadie escribe un código partiendo de cero. Los pocos que lo hacen así tienen rezones concretas para ello, pero, incluso así, se basan en plantillas de código o en ideas prestadas de otros códigos, ya sean suyos o de otros. Por esto, si está naciendo en ti un programador de drivers, la mejor manera de empezar sería leer el código del driver que está disponible en el kernel de Linux. Tranquilo, nadie te va a denunciar por plagio, de hecho la licencia Gnu Public License (GPL), por la cual el kernel de Linux fue puesto al público, promueve la reutilización del código. Mientras no hagas copias al pie de la letra del código de otro y luego le cambies el nombre del autor, eres libre de utilizar el código del kernel. Puedes reclamar cualquier parte nueva del código existente, pero, no olvides, que cualquier código GPL alterado tiene que ser puesto al público siempre bajo los términos del GPL, aunque los derechos de los cambios te pertenezcan a ti.
Entra el los siguientes enlaces para ver el código fuente.
Existe un parche no oficial de driver de Linux del autor para la tarjeta sintonizadora de televisión Pixelview Combo TV plus que se encuentra en http://cherry.freeshell.org/downloads/
Este documento se creó utilizando la versión 2K.1beta (1.48) del traductor LaTeX2HTML
Copyright © 1993, 1994, 1995, 1996, Nikos
Drakos, Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999, Ross
Moore, Mathematics Department, Macquarie University, Sydney.
Los argumentos de la línea de comando son:
latex2html -no_subdir -split 0 -show_section_numbers /tmp/lyx_tmpdir12763rVg3I/lyx_tmpbuf1276gZAXat/article.tuner.tex
La traducción fue comenzada por Cherry George
Mathew el 20/05/2003
Cherry es licenciado en ingeniería electrónica. Vive en la
ciudad de Bangalore, en la India. sus aficiones preferidas son leer novelas,
tocar la guitarra y hackear código.