Linux integrado comprueba si la consola serie está funcionando
2. ¿La función PRINTK envía información a la consola? ¿Cómo hacer que PRINTK envíe información a través del puerto serie? ¿O dónde decide el sistema si envía información al display o al puerto serie?
3. La función printk al comienzo de start_kernel (parece ser printk (linux_banner o algo así)). nuestro sistema, el sistema se inicia usando el programa BOOTLOADER de Hyundai Company, y luego parece saltar a head-armv.s en LINUX, y luego saltar a start_kernel. El puerto serie ya está disponible en el gestor de arranque, por lo que es necesario. ¿Qué pasa con el kernel?
La pregunta anterior puede ser confusa, porque mi propio cerebro también es confuso, principalmente porque no entiendo la relación entre tty, consola y serial, especialmente cómo se llama el puerto serial, hay relativamente poca información en esta área (hablé un poco sobre el análisis de escenas), espero que los expertos puedan darme algún consejo, ¡muchas gracias! He estado estudiando este tema recientemente y escribiendo un controlador para dispositivos seriales
He estado investigando durante casi un mes, navegando por Internet, mirando el código fuente y haciendo de todo
Pero sigue siendo un desastre.
En /proc/device (este es un archivo)
Está en el archivo /proc/device y el controlador /proc/device está en el archivo /proc/device
En /proc/device (creo que es esto)
Hay un controlador llamado serial, su número de dispositivo principal es 4, y su El número de dispositivo menor es 64-12X (creo que es este)
Todos, como saben, el número de dispositivo secundario del puerto serie comienza desde 64, y el número de dispositivo auxiliar correspondiente al puerto serie 1 / dev/ttyS0 es 64 y el número de dispositivo auxiliar correspondiente del puerto serie 2 es 65
El problema soy yo. Solo hay dos puertos serie en mi máquina y no puedo usarlos de la misma manera.
Mi máquina solo tiene dos puertos serie. ¿Por qué registra tantos números de subdispositivo?
Para un dispositivo conectado al puerto serie 1, cuando registro el controlador,
¿Necesito encontrar un número de dispositivo principal yo mismo?
¿O simplemente uso el número maestro 4 y luego selecciono el número de dispositivo menor del 12X arriba?
Alternativamente, ¿debería usar el dispositivo principal número 4 y el dispositivo secundario número 64?
¿Hay uno en la capa tty del kernel de Linux? En mi opinión, aquí es donde comienzan algunos controladores en serie.
Por ejemplo, llamar a tty_register_driver() para registrar un controlador
es como en el subsistema pci se llama a pci_register_driver().
Entonces, ¿qué pasa con los conductores registrados mediante este mecanismo?
¿Se ejecuta directamente en el puerto serie (por ejemplo, usando inb(), outb(), etc.)? Utilice inb(), outb()...
¿O alguna interfaz de controlador de bajo nivel?
Estos problemas me molestaron durante mucho tiempo y finalmente tuve que rendirme.
Ahora recurro a las aplicaciones del espacio de usuario para ver si hay una manera más eficiente de implementarlo
(En el espacio de usuario, solo puede usar open("/dev/ttyS0", O_RDWR))
Además, el sistema ha implementado el controlador del puerto serie para nosotros
Podemos utilizar el mismo controlador para el puerto serie.
Así que simplemente abrimos ("/dev/ttyS0") en un programa de espacio de usuario
Pero ahora queremos escribir un controlador para el dispositivo conectado al puerto serie
>
¿Podemos incluir un archivo de encabezado en el módulo del kernel para que podamos usar directamente la interfaz desde el controlador serial?
Cuando vi tu pregunta, pensé que era muy representativa, así que dediqué un tiempo a mirarla e hice algunos consejos para publicar aquí. Todos pueden discutirme y corregirme:
1. ¿Cuál es la relación jerárquica entre LINUX TTY, CONSOLA y puerto serie? ¿Cuál es la interfaz funcional específica? ¿Cómo se llama el puerto serie?
Los conceptos de Tty y consola son principalmente conceptos de dispositivos virtuales, mientras que el puerto serie se refiere más al controlador de dispositivo real. Tty es en realidad un tipo de abstracción de dispositivo de E/S de terminal. es un concepto de administración que se combina con tty_ldisc (protocolo de línea) y tty_driver (controlador de dispositivo real) para proporcionar una interfaz unificada para el VFS superior, que se puede configurar a través de tty_ioctl en la estructura file_operatives. Busque tty_driver y obtendrá n resultados, que en realidad son controladores para el chip en cuestión. Por lo tanto, lo que se puede concluir es que (la situación real es mucho más complicada que esto), cada tty_struct que describe un dispositivo tty debe inicializarse usando el controlador de dispositivo de caracteres de un chip específico (no necesariamente un controlador de dispositivo de caracteres), y el controlador de dispositivo de caracteres específico El chip puede ser una tarjeta gráfica o un chip de puerto serie, etc. Sé qué tipo de ARM Soc tienes, pero dependiendo de la situación, deberías usar un chip normal. Estos controladores en realidad tienen el concepto de un búfer con la consola. Su propósito es algo similar al de un tty. No solo debe estar conectado al tty, sino que también debe estar conectado al framebuffer. El motivo específico depende de la interrupción en el teclado a continuación. Un subconjunto del tty necesita usar la consola (generalmente el número de dispositivo principal es 4). y el número de dispositivo menor es 1-64), pero preste atención al tty Existe sin consola
El puerto serie se llama tty_driver. Este es un ejemplo típico:
Analizando interrupciones. en el teclado.
Proceso de procesamiento:
keyboard_interrupt-gt; handle_kbd_event-gt; handle_keyboard_event-gt; handle_ scancode
void handle_scancode(código de escaneo de caracteres sin firmar, int abajo)
{
........
tty = ttytab? ttytab[fg_console]: NULL
if (tty amp; amp; (!tty- gt; datos_conductor)) {
...... .........
tty = NULL
}
........................
schedule_console_callback();
}
Hay dos lugares en este código. Vale la pena señalar que además de obtener el tty (registrado a través del número global tty), también se ejecuta la devolución de llamada de la consola Schedule_console_ ¡La relación entre callbackTty y la consola se aclara aquí! !!!
2. La función PRINTK envía información a la consola, ¿verdad? ¿Cómo consigo que PRINTK envíe información a través del puerto serie? ¿O dónde decide el sistema si envía información al monitor o al puerto serie?
Si observa específicamente la implementación de la función printk, sabrá que printk no necesariamente envía información a la consola. Configurar los parámetros de inicio del kernel puede tener el efecto de enviar información al monitor. Hay una pieza interesante en inglés antes de la función:
/*Esto es printk. Se puede llamar en cualquier contexto. Esperamos que funcione.
*
* Intentamos capturar console_sem. Si lo logramos, es simple: registramos la salida y llamamos al controlador de la consola. Si no logramos obtener el semáforo, colocamos la salida
* en el búfer de registro y la devolvemos. El titular actual de console_sem
* notará la nueva salida en release_console_sem() y la enviará a la
* consola antes de liberar el transmisor de señal.
*
* Un efecto de esta impresión retrasada es que cuando se llama a printk() y
* se imprime el código modificado, esto se debe a que El nivel console_logle se comprueba durante la impresión real
*.
*/
El punto principal de este párrafo es que para operar la consola, primero debes obtener el semáforo console_sem. Si obtiene la señal console_sem, puede "registrar la salida y llamar al controlador de la consola" y viceversa.
Si obtiene el semáforo console_sem, puede "registrar la salida y llamar al controlador de la consola", o viceversa, puede "poner la salida en el búfer de registro y regresar", que en realidad está en el código:
asmlinkage int printk(const char *fmt, ...)
{
va_list args
banderas largas sin firmar
int print_len;
char *p;
carácter estático printk_buf[1024];
static int log_level_unknown = 1; (oops_in_progress) { /* Si es 1, asegúrese de que el sistema no falle*/
/* Si el sistema falle, asegúrese de que no se produzca un punto muerto*/
spin_lock_init( amp; logbuf_lock );
/* y asegúrese de imprimir inmediatamente*/
init_MUTEX(amp; console_sem).
}
/* Esto hará que el titular console_sem se detenga donde queremos que se detenga */
spin_lock_irqsave(amp; logbuf_lock, flags
/* Descarga la salida a un búfer temporal); * /
va_start(args, fmt);
printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args); todavía no Para escribir en la terminal, simplemente formatee la cadena entrante */
va_end(args
/* Copie la salida a log_buf. Si la persona que llama no proporciona marcadores de nivel de registro adecuados, los insertaremos aquí */
/* Comentario para mayor claridad */
for (p = printk_buf; *p; p ) {
if (log_level_unknown) {
if (p[0] != 'lt;' || p[1] lt; '0' || p[1 ] gt ; '7' || p[2] ! = 'gt;') {
emit_log_char('lt;');
emit_log_char(default_message_loglogvel '0'); p>
emit_log_char('gt;');
}
log_level_unknown = 0;
}
emit_log_char (* p);
if (*p == '\n')
log_level_unknown = 1;
}
si (! arch_ consoles_callable()) {
/* En algunas arquitecturas, las consolas no están disponibles en la CPU secundaria al principio del proceso de arranque.
*/
spin_unlock _irqrestore(amp; logbuf_lock, flags);
goto out
}
if (!down_trylock(amp; ;console_sem)) {
/* Tenemos el controlador.
spin_unlock_irqrestore(amp; logbuf_lock, flags);
console_ may_schedule = 0;
release_console_sem(); /p>
/*Alguien más posee los controladores. Cancelamos el spinlock, lo que permitirá al titular de la señal
continuar y llamar al controlador de la consola e imprimir la salida que acabamos de generar. */
spin_unlock_irqrestore(amp. logbuf_lock, flags);
}
out:
return print_len; p>}
El hecho de que printk coloque la cadena formateada en un buffer y luego la muestre en el momento adecuado responde a la pregunta de cuál es la salida de start. Esto también responde a la pregunta de por qué la función printk se usa al comienzo de start_kernel
3. La función printk se usa al comienzo de start_kernel (como printk (linux_banner u otros)), cuando. todo el kernel aún no se ha iniciado ni se está ejecutando. Entonces, ¿cómo se llama a printk en este momento? En nuestro sistema, el sistema usa el programa BOOTLOADER moderno cuando se inicia y luego parece saltar a head-armv.s en LINUX. y luego salte a start_kernel, y el puerto serie ya está disponible en el gestor de arranque. ¿No debería restablecerse el puerto serie después de ingresar al kernel?
El gestor de arranque generalmente realiza una inicialización básica y copia el kernel en formato físico. espacio y luego salta a la ejecución del kernel. Sin duda, el kernel definitivamente necesita restablecer el puerto serie. La razón es que hay muchos tipos de cargadores de arranque, algunos de los cuales no necesariamente configuran el puerto serie y el kernel no puede confiar en ellos. bootloading.
Gracias por el maravilloso análisis.
La CPU que usamos es la hms7202 de hynix, y usamos el puerto serie 0 que sirve como consola y toda la información durante el inicio. enviado a través de este puerto serie
La función ser_printf está definida en el gestor de arranque para la interacción a través del puerto serie.
Pero todavía no entiendo, ¿cómo funciona printk cuando se salta al kernel de Linux y la consola y el puerto serie aún no se han inicializado? Miré el proceso start_kernel
(e hice un seguimiento a través de Hyperterminal) y la consola se inicializa en la función console_init, mientras que el puerto serie en realidad se inicializa en el proceso n.° 1
(init-gt; do_basic_setup-gt; do_initcall). do_initcalls-gt; rs_init),
Entonces, ¿cómo funciona printk antes de que se inicialice el puerto serie? Específicamente, hay printk(linux_banner) al comienzo de
start_kernel, cuando aún no se han inicializado ni el puerto serie ni la consola.
1. Cuando no se han inicializado ni el puerto serie ni la consola, ¿hay printk(linux_banner) al principio de start_kernel?
Un análisis cuidadoso del código printk puede responder a esta pregunta:
/* Salida al búfer temporal*/
va_start(args, fmt); >
printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
va_end(args
Poner la entrada en printk_buf, siguiente paso
for (p = printk_buf; *p; p ) {
if (log_level_unknown) {
if (p[0] != 'lt;' || p [1] lt; '0' || p[1] gt; '7' || p[2] ! = 'gt;') {
emit_log_char('lt;'); p>
emit_log_char('lt;'); p>
emit_log_char(default_message_loglogvel '0');
emit_log_char('gt;'); /p>
log_level_unknown = 0;
}
emit_log_char(*p
if (*p == '\n')
log_level_unknown = 1;
}
Luego, el contenido de printk_buf se analiza y se coloca en el global. If (!down_trylock(amp; console_sem)) {
/*
* Tenemos el controlador. Podemos abandonar el spinlock y dejar que
* seamos dueños del controlador. y dejar
* release_console_sem() imprimir texto
*/
spin_unlock_irqrestore(amp;logbuf_lock, flags
console_may_ Schedule =); 0;
release_console_sem();
} else {
/*
* El controlador es propiedad de otra persona.
*Desechamos el spinlock, que permite que el porta señal siga funcionando.
* Permite que el titular de la señal continúe e invoque el controlador de la consola
* con la salida que acabamos de producir.
*/
spin_unlock_ irqrestore(amp; logbuf_lock, flags);
}
Luego basado en down_trylock(amp; console_sem) El resultado es una llamada a release_console_sem(), que se llama antes de procesar realmente el contenido del log_buf global del controlador del dispositivo de consola correspondiente. En este punto, podemos sacar las siguientes conclusiones:
(1) La operación principal de printk es en realidad para un búfer (log_buf) si el contenido del búfer se muestra (o se envía al terminal). Depende de nosotros si se puede obtener console_sem (2) El archivo donde se encuentra printk es printk.c, que no tiene nada que ver con la arquitectura, por lo que no se puede obtener console_sem.
(1) Durante el proceso de inicialización, el kernel marca console_sem como bloqueado, por lo que printk(linux_banner) al comienzo de start_kernel solo escribe la entrada en el búfer, y la llamada a printk solo escribe la entrada en el buffer una vez, después de inicializar el puerto serie y la consola. Después de inicializar el puerto serie y la consola, llame a printk una vez para enviar el contenido del búfer al puerto serie y la consola. (2) Durante la inicialización del puerto serie y la consola, se debe realizar una operación ascendente en console_sem.
(3) Por lo tanto, en la depuración integrada, si hay un problema con el sistema antes de que se inicialice la consola, no habrá salida. Los únicos que se pueden utilizar son led o jtag. (4) Se puede ver que su pregunta ha sido respondida.
No sé qué versión de kernel estás usando, pero en 2.4.18 y 2.4.19 que he visto, la inicialización de la consola se realiza en start_kernel. Según el análisis anterior, la inicialización de la consola no debería ser demasiado tarde, de lo contrario log_buf puede desbordarse.
¡Gracias por el maravilloso análisis arriba!
Estamos usando la versión 2.4.18 del kernel y la consola efectivamente está inicializada en
start_kernel-gt;console-gt;init. Con respecto a los puertos serie y tty, me gustaría preguntar nuevamente. El punto de entrada general para las operaciones del dispositivo tty es
es
static struct file_operatives tty_fops = {
. llseek: no_llseek,
leer: tty_read,
escribir: tty_write,
encuesta: tty_poll,
ioctl: tty_ioctl, p>
abierto: tty_open,
lanzamiento: tty_release,
fasync: tty_fasync,
};
En la serie port Las operaciones se definen en:
estructura estática tty_driver serial_driver
La mayoría de las funciones en serial.c son punteros y se utilizan para enviar datos al puerto serie.
La mayoría de las funciones en c son punteros a funciones que completan el serial_driver.
Por lo tanto, cuando se opera en un puerto serie, las operaciones en tty_fops deben llamarse primero (por ejemplo,
tty_open, etc.), lo que luego llama a tty_fasync, open, etc.) y luego descarga a una operación de puerto serie específica (rs_open, etc.), ¿verdad?
Pero hay muchos punteros de función en tty_driver (serial_driver para puertos serie) que no corresponden a los punteros de función en file_operaciones, por lo que no sé cómo se realizan estas operaciones no correspondientes. Por ejemplo, put_char, flush_char, read_proc,
write_proc, iniciar, detener, etc.
Lo siguiente es algo de mi comprensión del problema:
Esto en realidad se remonta al antiguo problema original, que es la relación entre tty y tty_driver. Desde el punto de vista de la implementación, tty_driver es en realidad uno de los componentes de implementación del mecanismo tty. Tomando prestado un ejemplo común en el diseño orientado a objetos, tty_driver en este momento es como los neumáticos del automóvil tty. ejecutarse normalmente, también debe Requiere infraestructura como tty_ldisc (protocolo de línea), termios e incluso struct tq_struct tq_hangup (ver tty_struct). La relación entre ellos no es una relación de herencia. En cuanto a los punteros de función en tty_driver, usando C como analogía, en realidad son muy similares a las funciones virtuales, es decir, se pueden definir, pero es posible que no se implementen. De hecho, y mucho menos tty_driver, simplemente busque serial_driver y. Encontrará que hay N múltiples implementaciones concretas, pero las funciones en tty_driver no necesariamente heredan de dispositivos específicos. Es posible que no todas las funciones del controlador estén implementadas, por lo que put_char, flus_char, read_proc, write_proc, start y stop pueden o no implementarse. Incluso si están implementadas, es posible que no se utilicen en la capa superior (capa VFS).