En lo sistemas empotrados (también en routers, switches...) es muy común no tener tarjeta gráfica, o puertos PS/2 para el teclado y el ratón, con lo que, si tenemos que reconfigurar algo, ¿cómo lo hacemos si no disponemos de pantalla o teclado?

La respuesta pasa por unos sencillos pasos que nos permitirán dejar disponible el acceso por el puerto serie a la consola, habilitándonos para tocar la configuración cuando aparentemente todo esté perdido.

Empecemos respondiendo unas cuántas preguntas para conocer más profundamente el estándar RS-232 (el habitual -aunque mal llamado- estándar del puerto serie DE-9) y así poder configurar y controlar más al detalle estas comunicaciones.

  • ¿Qué es el puerto serie?

Es una interfaz física, de 9 o 25 pines dispuestos en dos hileras, por la que se manda información bit a bit (de ahí el nombre de "serie"). Es una interfaz heredada y obsoleta en los equipos de electrónica de consumo y está empezando a desaparecer en los equipos portátiles (hay excepciones, como la gama Tecra de Toshiba, aunque acabará desapareciendo en favor del USB). No obstante, sigue siendo muy útil en electrónica industrial, robótica y otras aplicaciones diversas, como en la programación de routers y switches.

  • ¿Cómo se manda la información?

A través de los pines TD (Transmission Data) y RD (Receive Data) -también conocidos como Tx y Rx respectivamente. Los valores de tensión (que serán interpretados como 1s, 0s o banderas de control o control flags) deben estar entre -3 y -15 V para "entender" un 1 (OFF en caso de ser una bandera), y entre +3 y +15 V para "entender" un 0 (ON en caso de ser una bandera). Podemos ver estas señales en la siguiente imagen por gentileza de wikipedia:

  • ¿Que son los bits de start, stop y paridad?

Son unos bits que se utilizan como banderas de control, para indicar el inicio de un dato (bit de start), el fin del mismo (bit de stop) y para detectar errores. Como se observa en la figura anterior, el bit de start provoca un cambio en la línea (línea en reposo significa menos de -3 V), mientras que el bit de stop devuelve la línea al reposo.

  • ¿Qué es la paridad par e impar?

Por paridad par se entiende que el número de 1s de la palabra será rellenado con el bit de paridad para que el conjunto (palabra + bit de paridad) sume un número de 1s par. La paridad impar es su dual, es decir, el bit de paridad tomará el valor adecuado para que el conjunto conforme un número impar de 1s. Veamos algunos ejemplos:

Dato (8 bits) Bit de paridad (par) Bit de paridad (impar) Conjunto final (par) Conjunto final (impar)
00000000 0 1 000000000 000000001
01010101 0 1 010101010 010101011
11100000 1 0 111000001 111000000
  • ¿Qué son los baudios?

El baudio (baud) es una unidad de medida, usada en telecomunicaciones, que representa la cantidad de veces que cambia el estado de una señal en un periodo de tiempo.

Es importante resaltar que no se debe confundir la velocidad en baudios (baud rate) con la velocidad en bits por segundo (bit rate), ya que cada evento de señalización (símbolo) transmitido puede transportar uno o más bits. Sólo cuando cada evento de señalización (símbolo) transporta un solo bit coinciden la velocidad de transmisión de datos en baudios y en bits por segundo.

En el estándar RS-232C, como sólo hay dos símbolos (señal binaria, ceros y unos, voltajes positivos y negativos), la tasa en bits y en baudios coinciden, definiéndose las siguientes velocidades estandarizadas (se señalan en negrita las más extendidas): 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200.

  • ¿Qué significa comunicación símplex, half-dúplex o full-dúplex?

Sencillamente, indican la capacidad del canal de transmitir en un único sentido (símplex), en ambos sentidos con petición y conmutación (half-dúplex) o en ambos sentidos con disponibilidad total (full-dúplex).

Para entenderlo, podemos hacer una analogía con la circulación de vehículos. Una vía símplex es una calle normal, de un único carril y de un único sentido; Una vía half-dúplex es una calle de un único carril donde hay un agente de tráfico habilitando el paso en una o en otra dirección en función de la congestión de tráfico; Una vía full-dúplex es una avenida, donde se puede circular en ambas direcciones a la vez.

  • ¿Qué es el control de flujo?

El control de flujo se ejecuta a través de las señales CTS (Clear To Send) y RTS (Request To Send). Se puede hacer un control de flujo por software o por hardware, siendo preferible, siempre que sea posible en la UART (o USART), el segundo, aunque éste (el control de flujo) sólo tiene sentido cuando la comunicación es half-dúplex.

  • ¿Qué es un cable de módem nulo -null modem cable-?

Un cable de módem nulo, o cable de módem cruzado, es aquél que tiene cruzadas las líneas de Rx con Tx, para posibilitar la comunicación entre dos DTE (Data Terminal Equipment), los cuales no están habilitados para una comunicación directa (están preparados para conectarse a un DCE, Data Communications Equipment). No se encuentra estandarizado, con lo que pueden existir múltiples formas de conexión y ensamblado del mismo, pero una cosa si está clara: Deben tener sus dos extremos hembra (female), como puede observarse en la imagen.

Cable de módem nulo

  • ¿Cuál es el pinado de un conector DE-9 y de un null modem?

Como una imagen vale mas que mil palabras, vamos allá con dos que hablan por si mismas:

Pinout null-modem

Pinout de un cable de módem nulo

Fuente: http://orkanet.com/production/cabling/null_modem_cable_db9f_to_db9f_ar.htm

Pinout DE-9

Pinout de un DE-9 en un DTE

Fuente: http://www.noktaendustriyel.com/eng/pratx0x1.html


Tras resolver esas dudas que siempre nos pueden rondar por la cabeza, pasemos ahora a hablar de la configuración en la BIOS. Este paso lo podemos obviar o saltar, ya que aquí simplemente habilitaremos la entrada/salida de datos por el puerto serie en el arranque de nuestro equipo (un sistema empotrado en nuestro caso, aunque pudiera ser un router, switch... o lo que sea que tenga interfaz serie).

Dicho esto, debemos habilitar en la BIOS de nuestra máquina los puertos serie. Aparecerá algo como "Enable console" o "Enable COM1" o similar. Si no apareciera nada de esto, es probable que tengamos que consultar las hojas de características (datasheets) para saber si el fabricante ha habilitado el puerto serie en el arranque sin configuración en la BIOS. Si este fuera el caso, entonces habrá que consultar en dichas hojas la velocidad, la paridad y el número de bits de stop que ha impuesto el fabricante.

De todos modos, esto es recomendable aunque no imprescindible, ya que una vez que pase el control de la BIOS al Sistema Operativo (normalmente, cuando GRUB, LILO o el gestor de arranque que tengamos, inicie los discos duros) se puede habilitar por software, aunque es preferible que se deje activado desde el inicio en la BIOS (si es posible) para facilitar todo y ahorrar errores (siempre es mejor tener accesible el COM1 desde el inicio que no tras la carga de las imágenes del kernel). En nuestro caso particular, hemos seleccionado en la BIOS (apartado "Remote Access") los siguientes parámetros:

  • Remote Access: Enabled
  • Serial Port Number: COM1
  • Base Address: 0x3F8
  • IRQ: 4
  • Serial Port Mode: 115200, 8, n, 1
  • Flow Control: None
  • Redirection After BIOS POST: Always
  • Terminal Type: VT100
  • VT-UTF8 Combo Key Support: Enabled
  • Sredir Memory Display Delay: No Delay

Tras esto, ya tenemos medio trabajo hecho. Ahora, sólo deberemos modificar cuatro ficheros para también tener la consola en Linux.


Para tener la consola por el puerto serie habilitada en Linux (consola en ttyS0), debemos modificar los siguientes ficheros, o crearlos si alguno no existiera (si os sirve de orientación, nosotros los teníamos todos disponibles en un Linux Slackwaare 12.1 parcheado con Xenomai, con kernel 2.6.30.8 y boot loader LILO):

  • /etc/lilo.conf:

Hay que añadir en el APPEND de la configuración global:

APPEND = "console=tty0 console=ttyS0,115200n8"

si hay tarjeta gráfica, o si no la hay ni la habrá, basta con:

APPEND = "console=ttyS0,115200n8"

Como orientación, mostramos nuestro fichero lilo.conf modificado, donde subrayamos las líneas que hemos modificado:

# LILO configuration file
# generated by 'liloconfig'
#
# Start LILO global section
# Append any additional kernel parameters:
# Append=" vt.default_utf8=0 ide-core.nodma=0.0"
Append="console=tty0 console=ttyS0,115200n8 ide-core.nodma=0.0"
boot = /dev/hda
install=text

# Standard menu.
# Or, you can comment out the bitmap menu above and
# use a boot message with the standard menu:
message = /boot/boot_message.txt

# Wait until the timeout to boot (if commented out, boot the
# first entry immediately):
prompt
default = 1

# Timeout before the first entry boots.
# This is given in tenths of a second, so 600 for every minute:
timeout = 300

# Override dangerous defaults that rewrite the partition table:
change-rules
reset

# Normal VGA console
vga = normal
# VESA framebuffer console @ 1024x768x64k
# vga=791
# VESA framebuffer console @ 1024x768x32k
# vga=790
# VESA framebuffer console @ 1024x768x256
# vga=773
# VESA framebuffer console @ 800x600x64k
# vga=788
# VESA framebuffer console @ 800x600x32k
# vga=787
# VESA framebuffer console @ 800x600x256
# vga=771
# VESA framebuffer console @ 640x480x64k
# vga=785
# VESA framebuffer console @ 640x480x32k
# vga=784
# VESA framebuffer console @ 640x480x256
# vga=769
# End LILO global section

# Linux bootable partition config begins
image = /boot/vmlinuz
root = /dev/hda1
label = 1
read-only

image = /boot/vmlinuz-huge-2.6.29.6
root = /dev/hda1
label = 2
read-only
# Linux bootable partition config ends

Observad que hemos indicado 'install=text' frente al 'install=menu' que pudiera existir, con lo que debemos eliminar o comentar todas las líneas que se refieran a mapas de bits (bitmaps, o bmp), ya que si las dejamos LILO se quejará indicándonos que no es compatible la opción gráfica con 'install=text'. Esto es debido a que la consola remota no acepta entornos gráficos ni imágenes, sólo acepta texto.

¡Recordad que tras modificar lilo.conf siempre hay que teclear "lilo" en el shell para actualizar el boot loader o los cambios no tendrán efecto!

$ lilo

  • /etc/inittab:

En este fichero hay que descomentar (o añadir, depende del caso) las siguientes líneas:

# Local serial lines:
s1:12345:respawn:/sbin/agetty -L ttyS0 115200 vt100

Con esta línea decimos que en los runlevels 1, 2, 3, 4 y 5, relance el comando 'agetty -L ttyS0 115200 vt100'. agetty, al igual que getty, ugetty y otros similares, lo que hace es lanzar el prompt de login por la interfaz indicada (ttyS0, puerto serie COM1 en Windows), a la velocidad indicada (115200 bps) y con el tipeado de datos del terminal indicado (vt100).

Gracias a esta línea, aunque nuestra BIOS no permita mostrar nada en el arranque por el puerto serie, se nos mostrará, tras la carga del Sistema Operativo, la invitación para hacer login, lo cual nos habilita para iniciar sesión en nuestro equipo remoto a través del puerto serie. ¿No es maravilloso?

Mostramos a continuación nuestro fichero inittab completo para quien le sirva:

#
# inittab This file describes how the INIT process should set up
# the system in a certain run-level.
#
# Version: @(#)inittab 2.04 17/05/93 MvS
# 2.10 02/10/95 PV
# 3.00 02/06/1999 PV
# 4.00 04/10/2002 PV
#
# Author: Miquel van Smoorenburg, <Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.>
# Modified by: Patrick J. Volkerding, <Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.>
#

# These are the default runlevels in Slackware:
# 0 = halt
# 1 = single user mode
# 2 = unused (but configured the same as runlevel 3)
# 3 = multiuser mode (default Slackware runlevel)
# 4 = X11 with KDM/GDM/XDM (session managers)
# 5 = unused (but configured the same as runlevel 3)
# 6 = reboot

# Default runlevel. (Do not set to 0 or 6)
id:3:initdefault:

# System initialization (runs when system boots).
si:S:sysinit:/etc/rc.d/rc.S

# Script to run when going single user (runlevel 1).
su:1S:wait:/etc/rc.d/rc.K

# Script to run when going multi user.
rc:2345:wait:/etc/rc.d/rc.M

# What to do at the "Three Finger Salute".
ca::ctrlaltdel:/sbin/shutdown -t5 -r now

# Runlevel 0 halts the system.
l0:0:wait:/etc/rc.d/rc.0

# Runlevel 6 reboots the system.
l6:6:wait:/etc/rc.d/rc.6

# What to do when power fails.
pf::powerfail:/sbin/genpowerfail start

# If power is back, cancel the running shutdown.
pg::powerokwait:/sbin/genpowerfail stop

# These are the standard console login getties in multiuser mode:
c1:1235:respawn:/sbin/agetty 38400 tty1 linux
c2:1235:respawn:/sbin/agetty 38400 tty2 linux
c3:1235:respawn:/sbin/agetty 38400 tty3 linux
c4:1235:respawn:/sbin/agetty 38400 tty4 linux
c5:1235:respawn:/sbin/agetty 38400 tty5 linux
c6:12345:respawn:/sbin/agetty 38400 tty6 linux

# Local serial lines:
s1:12345:respawn:/sbin/agetty -L ttyS0 115200 vt100
#s2:12345:respawn:/sbin/agetty -L ttyS1 9600 vt100

# Dialup lines:
#d1:12345:respawn:/sbin/agetty -mt60 38400,19200,9600,2400,1200 ttyS0 vt100
#d2:12345:respawn:/sbin/agetty -mt60 38400,19200,9600,2400,1200 ttyS1 vt100

# Runlevel 4 used to be for an X window only system, until we discovered
# that it throws init into a loop that keeps your load avg at least 1 all
# the time. Thus, there is now one getty opened on tty6. Hopefully no one
# will notice. ;^)
# It might not be bad to have one text console anyway, in case something
# happens to X.
x1:4:respawn:/etc/rc.d/rc.4

# End of /etc/inittab

Por último, indicar que si se quiere reiniciar el fichero inittab sin reiniciar el equipo, basta con poner en un terminal:

$ init q

  • /etc/securetty:

Con este fichero, permitimos el login al usuario root a través del puerto serie.Si se quiere, no hace falta modificarlo, porque nos podemos loguear como un usuario normal y, luego, una vez estemos dentro, loguearnos como root.

Mostramos a continuación nuestro fichero securetty para que lo podáis copiar si no lo teníais en vuestra máquina. Nosotros sólo tuvimos que descomentar (borrar la almohadilla) en la línea de ttyS0, ya que lo demás lo dejamos igual:

# This file defines which devices root can log in on.

# These are the ttys on the physical console:
console
tty1
tty2
tty3
tty4
tty5
tty6

# These are some remote ttys, and uncommenting them might be less than fully secure:
ttyS0
#ttyS1
#ttyS2
#ttyS3
#ttyp0
#ttyp1
#ttyp2
#ttyp3
#ttyp4
#ttyp5
#ttyp6
#ttyp7
#pts/0
#pts/1
#pts/2
#pts/3
#pts/4
#pts/5
#pts/6
#pts/7

  • /boot/boot_message.txt:

Este fichero es, simplemente, para rizar el rizo. Si vuestra arquitectura permite el arranque por puerto serie y habéis seleccionado 'install=text' en el fichero /etc/lilo.conf, podéis optar a mostrar un mensaje de texto personalizado en el arranque que os permita elegir entre los distintos Sistemas Operativos que tengáis instalados. Como nosotros tenemos dos kernels -uno de tiempo real y otro normal-, creamos el siguiente mensaje (con 'vim /boot/boot_message.txt' por ejemplo):

Bienvenido al arrancador LILO:

Por favor, teclee el numero de la opción que desee cargar
de las mostradas abajo y pulse [ENTER]. Las opciones son:

1 - Linux-2.6.30.8
2 - Linux-2.6.29.6

Una vez creado, le decimos a LILO dónde se encuentra este fichero, añadiendo la línea siguiente:

message = /boot/boot_message.txt

Tras esto, y como ya sabéis, hacemos:

$ lilo

para actualizarlo y habilitar los cambios que hemos hecho.

Y esto es todo. Con esta última modificación ya tenemos todo lo necesario para hacer un logueado a través del puerto serie en un sistema remoto. Pero, ¿con qué programa veo lo que me devuelva el sistema remoto? No te preocupes, en la última página te solucionamos esta duda.


Para conectarnos al sistema remoto y mandarle órdenes, podemos utilizar muchos programas como minicom, putty o incluso el hyperterminal de Windows. No obstante, nosotros vamos a explicar un gran desconocido para muchos, que ni siquiera es un programa, sino un comando de Linux: El comando 'screen'.

screen es un comando para crear sesiones virtuales. Es muy poderoso y tiene muchísimas opciones, con lo que no podemos explicarlas todas. Para ello, os remitimos a los tutoriales que hay por la web o al archiconocido man screen que se puede poner en cualquier terminal. Si no lo tenéis instalado, basta con hacer en Fedora:

# sudo yum install screen

O en Debian/Ubuntu:

# sudo apt-get install screen

Lo primero que hay que saber es que screen hay que ejecutarlo como súper usuario. Lo segundo que debéis hacer es que los procesos que se creen cuando se llaman desde screen no mueren. Es decir, y por poner un ejemplo clásico, si yo hago lo siguiente:

# sudo screen

se limpiará la terminal actual y volverá a aparecer nuestro prompt, pudiendo parecer que el comando ha fallado. Sin embargo, si escribimos ahí:

# ping 127.0.0.1

y luego nos desasociamos de screen pulsando CTRL + A - D, volvemos al terminal primero que teníamos. ¿Qué ha pasado? Pues que hemos creado una sesión virtual en la que, al desasociarnos, volvemos a la que teníamos. Y ahora viene lo mejor. Si ponemos:

# sudo screen -r

¡Vemos que el ping se ha seguido ejecutando! ¿Veis el potencial de este comando? Podéis lanzar tareas remotas que, aunque el sistema host caiga, en el remoto se seguirán ejecutando. Para finalizar realmente la tarea (no desasociarse, sino finiquitarla), hay que hacer CTRL + A - K, y luego pulsar 'y'.

Y bueno, volvamos a lo que estábamos. Para ver lo que escupe el puerto serie, tecleamos lo siguiente desde un equipo host conectado al remoto a través de un cable de módem nulo antes de arrancar el remoto (ya que podemos perder caracteres si no lo hiciéramos así):

# sudo screen /dev/ttyS0 115200

donde /dev/ttyS0 indica que vamos a usar el primer puerto serie en la máquina host (COM1) y 115200 es la velocidad de línea a la que vamos a trabajar, la tasa en baudios por segundo. Mucho cuidado de no confundirse con el ttySx, ya que puede diferir el puerto del host con el del sistema remoto: El del host se escribe en el screen (y puede ser ttyS1 por ejemplo), y el del sistema remoto se escribe con los ficheros lilo.conf, inittab y securetty (y puede ser ttyS0, por ejemplo, ya que no es necesario que coincidan).

Por otro lado, es conveniente señalar que lo que se selecciona, es el label que hemos introducido en cada entrada de los kernels en lilo.conf Si os fijáis, para nuestro primer kernel, hemos modificado la etiqueta para convertirla en label=1, y, en el segundo, hemos hecho lo apropiado para dejarla como label=2. Esto es conveniente ya que facilitamos la elección del kernel reduciéndolo a dos pulsaciones de teclas (pulsar un número más [ENTER]).

Tras esto, y si todo ha ido bien y en todos los pasos hemos seleccionado la misma velocidad de línea (recomendamos probar primero con 9600 si se desconoce la de algún equipo, ya que suele ser el estándar de facto en Linux para el puerto serie), tendremos nuestra consola remota por el puerto serie. ¡Felicidades!

Fuentes:

[1] Wikipedia: www.wikipedia.es

[2] Linux Serial Console HOWTO: www.vanemery.com

[3] Serial HOWTO: tldp.org

[4] How To Configure Linux Serial Consoles: lonesysadmin.net

[5] Linux man page: linux.die.net

[6] Imagen del cable de módem nulo: orkanet.com

[7] Imagen del conector DE-9 RS-232 macho: www.noktaendustriyel.com