Archivo de la categoría: Cacharreo

Cargando fácilmente mis cascos

Hace tiempo acabé cansado de mis cascos con cable: se enredaban con todo, y a menudo me olvidaba que los llevaba puestos, con lo que, al levantarme, me daban un tirón. Es por esto que decidí comprar unos cascos inalámbricos. Además, para evitar interferencias o que los vecinos puedan escuchar desde otro equipo lo que estoy viendo en ese momento (cof, cof-porno-cof, cof), decidí usar unos cascos bluetooth. Compré unos bastante baratos, pero eran incómodos, así que trasplanté la circuitería a mis viejos cascos de cable.

Todo funcionaba bien, excepto por un detalle: cada dos por tres me encontraba sin batería porque me los quitaba y no los enchufaba al cargador, con lo que tenía que usarlos durante un buen rato con el cable enchufado, perdiendo así las ventajas. Ante esto empecé a pensar en maneras de recargar la batería de una manera más automática. La primera idea fue utilizar carga inalámbrica: montaría un soporte para los cascos con una bobina sobre él; a su vez, una segunda bobina montada en la diadema de los cascos estaría conectada a los pines de recarga. Así, al colocarlos en el soporte, ambas bobinas quedarían alineadas formando un transformador, igual que se hace en los sistemas de carga inalámbrica de los móviles. Por desgracia, aunque la teoría era muy sencilla, la práctica no lo era tanto: hacía falta un oscilador para alimentar la primera bobina, ajustar la impedancia del conjunto para que estuviese en resonancia… un cristo.

Decidí entonces probar una aproximación más pedestre: colocaría un par de contactos metálicos en la diadema que coincidiesen con otros situados en un soporte, de manera que cuando colgase los cascos éstos recibirían alimentación directamente. La primera idea fue colocar dos contactos en los laterales de la diadema para uno de los polos, y otro en el centro, por debajo, para el otro. De esta manera no habría riesgo de invertir la polaridad si colgaba los cascos al revés. Sin embargo, enseguida deseché esta opción por dos motivos: para empezar, no era sencillo construir un soporte con los tres contactos; y por otro, no me gustaba la idea de tener un contacto metálico apoyado en mi cuero cabelludo (con el sudor acabaría estropeándose). Tras darle varias vueltas, decidí que la mejor opción era usar sólo dos contactos, situados en cada lado de la diadema. Para evitar estropear los cascos por recibir la polaridad al revés si los colgaba mal, decidí añadir un puente de diodos entre los contactos y el circuito de carga. Por desgracia, a la salida tendría 1,4 voltios menos que en la entrada por culpa de las pérdidas de los diodos, y, por otro lado, no quería correr el riesgo de alimentar un dispositivo con una batería de Li-ion con una tensión excesiva(y menos si está situada junto a mi oreja) .

Dado que el diseño original estaba pensado para cargarse desde un USB y, por tanto, la tensión tenía que ser de 5V estabilizados, decidí añadir un regulador justo entre el puente de diodos y el circuito bluetooth. La primera idea que me vino a la cabeza fue el clásico 7805, pero dado que es un regulador lineal, podría tener problemas de sobrecalentamiento (y, una vez más, hay que recordar que eso va a ir pegado a nuestra oreja…), así que lo sustituí por un regulador conmutado, en concreto un TSRN 1-2450 de la marca TRACO. Gracias a él, podría usar cualquier tensión superior a 8 voltios en la entrada del circuito sin riesgo de quemar nada orgánico.

El sistema, finalmente, quedó así:

esquematico

Vemos que la tensión (que no es necesario que esté estabilizada) disponible en el soporte llega a los cascos mediante dos contactos metálicos situados a cada lado de la diadema. Esta tensión (que puede ir con cualquiera de las dos posibles polaridades) llega al puente de diodos, que nos garantiza tener el positivo y el negativo donde nos interesa, pero con una pérdida de 1,4 voltios (con lo que tendremos entre 7,6 y 10,6 voltios). A continuación viene el regulador de tensión, que nos proporciona los 5 voltios estabilizados que necesitamos para el circuito bluetooth.

Una vez diseñado llegó el momento de construirlo, y aquí fue donde empezaron los problemas, en concreto el problema de como hacer los contactos. La primera idea fue utilizar lámina de cobre, pero no fui capaz de encontrarla en ningún lugar. Decidí probar con papel de aluminio: supuse que, si iba bien pegado, no debería haber riesgo de rotura, y sería muy sencillo de fabricar al poder aplicar una capa de loctite primero, pegar encima el papel, y luego recortar con un cutter. Por desgracia el invento no aguantó ni dos días (no sólo el papel de aluminio es demasiado frágil, sino que el loctite no se adhiere nada bien a él). Empecé a buscar alternativas, llegando incluso a intentar hacer una especie de trenza con cable de cobre, pero no encontraba nada que fuese adecuado.

Y de pronto, la solución apareció por pura casualidad: malla de desoldar.

desoldar

Para los que no la conozcan, se trata de cable fino de cobre trenzado formando una tira plana, utilizado para desoldar componentes: básicamente se coloca encima del estaño que se quiere retirar y se calienta el conjunto con el soldador. Cuando el estaño se funde, la malla lo absorbe por capilaridad, quedando el punto completamente libre.

Probé a pegarlo con loctite y el resultado fue excelente… hasta que intenté alimentar el circuito: la malla había absorbido el loctite por capilaridad, por lo que había quedado completamente cubierta y ya no era conductora. Probé también con pistola de pegamento térmico, pero no agarraba en el cobre y se soltaba.

Entonces decidí hacer una prueba a la desesperada, así que fui a la papelería de enfrente de mi casa y compré cinta adhesiva de doble cara, la apliqué en ambos lados de la diadema, pegué la malla poco a poco pasando la uña fuertemente por encima para asegurarme de que quedase bien pegada, y recorté los trozos sobrantes con un cutter. El resultado fue perfecto, y es capaz de aguantar muy bien el rozamiento diario de colgarlo y quitarlo del soporte:

contactos

La cinta aislante negra colocada en los dos extremos es para evitar que la malla se suelte: aunque a lo largo queda muy bien fijada, en los extremos es muy fácil que se acabe levantando, lo que acaba en un «efecto cremallera». Es por esto que es fundamental protegerla de esta manera si queremos que nos dure mucho tiempo.

Para el soporte utilicé una alcayata grande en forma de L, y para los contactos aproveché las láminas de una pila de 4,5 voltios (la clásica «pila de petaca»):

pila_de_petaca

Con ellos hice dos contactos, uno en cada lado, y los conecté a un alimentador de 12 voltios:

soporte

El resultado no es muy elegante y puede mejorarse, lo reconozco, pero será cuando tenga algo de tiempo.

Y este es el resultado final: ahora sólo tengo que asegurarme de colgar los cascos en el soporte y siempre estarán cargados y listos para su uso.

completo

Más sobre Debian en Android

A raíz de la aparición de MaruOS, decidí intentar hacer algo similar en mi móvil Android, así que me puse a preparar una instalación de Debian para Android como ya había hecho otras veces. Por desgracia las cosas se torcieron ya al principio, así que voy a comentar los pasos que di para corregir los problemas que encontré, que no han sido pocos:

Para empezar, mi móvil tiene Android 6 (en concreto la distribución PureNexus para Nexus 4 que encontré en la página de El tendero digital). Parece que algunos cambios de seguridad hechos en él, junto con otros en APT fueron los que acabaron dando guerra. Pero no adelantemos acontecimientos…

Empecé creando el entorno básico con debootstrap, con el comando

sudo debootstrap --arch=armhf --variant=minbase --foreign sid  /home/raster/tmp  http://ftp.debian.org/debian

El resultado lo comprimí con TAR, lo pasé al móvil, y me conecté a través de USB con un shell ADB (para disponer de ADB hay que bajarse el SDK de Android):

sudo ./platform-tools/adb shell

Una vez hecho esto, lo primero es ejecutar el comando resize para ajustar el tamaño lógico de la ventana a la que tenemos físicamente, y que aplicaciones como los editores de texto y demás se vean correctamente.

Tras ello, fui hasta /data/media/0, que es donde se encuentra la zona de datos de usuario, y allí descomprimí el fichero .tar con el sistema Debian básico. Luego usé un sencillo script para entrar dentro de él para seguir la instalación:

resize
export SDCARD=/data
export ROOT=$SDCARD/media/0/debian
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH
export HOME=/root
mount -o remount,exec,dev,suid $SDCARD
for f in dev dev/pts proc sys ; do mount -o bind /$f $ROOT/$f ; done
chroot $ROOT /bin/bash -l
for f in sys proc dev/pts dev ; do umount $ROOT/$f ; done

Tras lanzarlo, procedí a terminar la instalación del sistema, pero me encontré con que al ejecutar debootstrap/debootstrap –second-stage, fallaba y no continuaba la instalación. Tras varias pruebas, descubrí que el problema era que intentaba crear varias veces una serie de nodos en /dev.

Se me ocurrió probar a usar QEMU para realizar la operación en mi propio PC, así que instalé qemu-static, copié qemu-arm-static al directorio /usr/bin del sistema Debian sin configurar, y lancé una sesión de shell dentro de ella con CHROOT en mi propio PC. Ahí el comando debootstrap/debootstrap –second-stage sí funcionó, así que procedí a comprimir el sistema ya listo y subirlo al móvil.

Ahora ya tenía un sistema Debian. O casi, porque cualquier intento de usar APT para instalar paquetes o actualizar la lista de programas fallaba con un error muy raro: no era capaz de resolver la dirección del repositorio:

root@localhost:/# apt-get update
Err:1 http://ftp.debian.org/debian sid InRelease
  Temporary failure resolving 'ftp.debian.org'
Reading package lists... Done
W: Failed to fetch http://ftp.debian.org/debian/dists/sid/InRelease  Temporary failure resolving 'ftp.debian.org'
W: Some index files failed to download. They have been ignored, or old ones used instead.

Sin embargo, un PING o un WGET a dicha dirección sí funcionaba perfectamente, por lo que no parecía ser un problema de la red en sí. Probé a poner manualmente la IP en el fichero /etc/hosts y entonces el error pasó a ser otro:

root@localhost:/# apt-get update
Ign:1 http://ftp.debian.org/debian sid InRelease
Ign:2 http://ftp.debian.org/debian sid Release
Ign:3 http://ftp.debian.org/debian sid/main armhf Packages.diff/Index
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:5 http://ftp.debian.org/debian sid/main Translation-en.diff/Index
Ign:6 http://ftp.debian.org/debian sid/contrib armhf Packages.diff/Index
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:8 http://ftp.debian.org/debian sid/contrib Translation-en.diff/Index
Ign:9 http://ftp.debian.org/debian sid/non-free armhf Packages.diff/Index
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:11 http://ftp.debian.org/debian sid/non-free Translation-en.diff/Index
Ign:12 http://ftp.debian.org/debian sid/main armhf Packages
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Ign:14 http://ftp.debian.org/debian sid/contrib armhf Packages
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Ign:16 http://ftp.debian.org/debian sid/non-free armhf Packages
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Ign:12 http://ftp.debian.org/debian sid/main armhf Packages
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Ign:14 http://ftp.debian.org/debian sid/contrib armhf Packages
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Ign:16 http://ftp.debian.org/debian sid/non-free armhf Packages
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Ign:12 http://ftp.debian.org/debian sid/main armhf Packages
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Ign:14 http://ftp.debian.org/debian sid/contrib armhf Packages
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Ign:16 http://ftp.debian.org/debian sid/non-free armhf Packages
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Ign:12 http://ftp.debian.org/debian sid/main armhf Packages
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Ign:14 http://ftp.debian.org/debian sid/contrib armhf Packages
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Ign:16 http://ftp.debian.org/debian sid/non-free armhf Packages
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Ign:12 http://ftp.debian.org/debian sid/main armhf Packages
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Ign:14 http://ftp.debian.org/debian sid/contrib armhf Packages
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Ign:16 http://ftp.debian.org/debian sid/non-free armhf Packages
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Err:12 http://ftp.debian.org/debian sid/main armhf Packages
  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Err:14 http://ftp.debian.org/debian sid/contrib armhf Packages
  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Err:16 http://ftp.debian.org/debian sid/non-free armhf Packages
  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Reading package lists... Done
W: The repository 'http://ftp.debian.org/debian sid Release' does not have a Release file.
N: Data from such a repository can't be authenticated and is therefore potentially dangerous to use.
N: See apt-secure(8) manpage for repository creation and user configuration details.
W: Failed to fetch http://ftp.debian.org/debian/dists/sid/main/binary-armhf/Packages  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
W: Failed to fetch http://ftp.debian.org/debian/dists/sid/contrib/binary-armhf/Packages  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
W: Failed to fetch http://ftp.debian.org/debian/dists/sid/non-free/binary-armhf/Packages  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
E: Some index files failed to download. They have been ignored, or old ones used instead.

Esto era otra cosa, claramente: por algún motivo, APT no conseguía permisos para acceder a la red. Parecía un problema de SELINUX, pero realmente estaba en modo permisivo, por lo que no debería estar fallando.

Tras rebuscar por todas partes, descubrí una entrada donde se comentaba el mismo problema para Kali-Rolling en Android: la clave parece estar en que el usuario _apt está en el grupo nogroup, y eso en Android supone no tener acceso a nada, ni siquiera a la red. Para solucionarlo, sólo tuve que editar el fichero /etc/passwd y cambiar el grupo de _apt de 65534 (nogroup) a 3004, que aunque es un grupo que no existe en mi sistema, es suficiente para que todo vuelva a funcionar como debe.

Peleandome con Python 3.4

Dado que quiero tener soporte para gnutls, tuve que cambiar el USE de mi distribución a:

USE="${ARCH} -pam -fortran -sanitize -iptables -static -systemd -mdev gnutls internal-glib -caps -filecaps -X -gtk -qt -tk"

Las siete últimas adiciones fueron para no añadir nada de entorno gráfico (pues no tiene sentido en el WebTV) y para asegurar de que las nuevas bibliotecas necesarias para incluir gnutls se puedan compilar. Hubo varios problemillas, pero uno a uno los fui resolviendo.

Por desgracia, el último escollo estaba en python. Cuando compilé el sistema la versión estable era la 3.3, pero ahora ya salió la 3.4. El problema es que se negaba a compilar, dando un error raro:

Python build finished successfully!
The necessary bits to build these optional modules were not found:
    _tkinter
To find the necessary bits, look in setup.py in detect_modules() for the module's name.
Failed to build these modules:
    _socket               _ssl

Decía que se había compilado correctamente, pero emerge devolvía un error. Al principio pensaba que el problema estaba en tkinter, el módulo gráfico de python, pero no tenía sentido porque había especificado que no quería ni tk, ni X ni nada relacionado con un entorno gráfico. Entonces, revisando el log, vi que en medio de la compilación había este error:

/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.o
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c: In function 'makesockaddr':
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c:1175:14: error: dereferencing pointer to incomplete type
         if (a->can_ifindex) {
              ^
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c:1176:32: error: dereferencing pointer to incomplete type
             ifr.ifr_ifindex = a->can_ifindex;
                                ^
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c:1183:38: error: dereferencing pointer to incomplete type
                                     a->can_family);
                                      ^
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c: In function 'getsockaddrlen':
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c:1802:28: error: invalid application of 'sizeof' to incomplete type 'struct sockaddr_can'
         *len_ret = sizeof (struct sockaddr_can);
                            ^
building '_ssl' extension

¿CAN? ¿En el módulo de sockets? Bastante raro. Rebuscando encontré que, efectivamente, desde el núcleo 2.6.25 hay soporte para el bus CAN; sin embargo, mi núcleo (y sus cabeceras) es el 2.6.22. ¿Por qué se empeñaba en incluir soporte? Por otro lado, python 3.3 también trae de serie soporte para bus CAN, pero esa versión sí compilaba bien. ¿Qué estaba pasando?

Al final descubrí que el núcleo 2.6.22 tiene algo de soporte del bus CAN, pero parece que no el suficiente, y eso lía a python 3.4.

La solución que encontré fue editar el fichero /usr/include/bits/socket.h, y comentar la línea donde se define AF_CAN:

//#define AF_CAN PF_CAN

Y con eso, por fin, pude compilar absolutamente todo, listo para empezar a preparar el sistema que va a llevar definitivamente.

No puede caber aqui

Llevo un par de días incapaz de actualizar el paquete binutils usando la emulación de mipsel sobre mi PC. Es una cosa misteriosa, pues daba un error al compilar el linker gold. Tras intentar hacerlo a mano, me devolvió como mensaje de error:

(for i in `seq 1 70000`; do 
  echo "int var_$i __attribute__((section("section_$i"))) = $i;"; 
done) > many_sections_define.h.tmp
make: execvp: /bin/sh: Argument list too long

¿Argument list too long? Un error bastante extraño, sin duda. Y más en el propio make. Encima, si eliminaba todo ese código y metía un simple echo, el error persistía. ¿Qué estaba pasando?

Tras probar de todo y rebuscar por todas partes, por fin encontré el problema: qemu define un tamaño máximo para la línea de comandos (MAX_ARG_PAGES) demasiado pequeño para compilar binutils, y por eso casca. Encima, dicho valor se define a piñón en el código fuente, por lo que la única solución consiste en bajarse los fuentes de qemu, modificar el fichero linux-user/qemu.h para aumentar a 64 o más las páginas reservadas para la línea de comandos (yo puse 129), y compilarlo todo con:

./configure --static --target-list=mipsel-linux-user
make

Con esto ya tendremos en mipsel-linux-user/qemu-mipsel el ejecutable estático, el cual podemos copiar dentro de la carpeta de nuestra máquina virtual como usr/bin/qemu-mipsel-static. Y con esto deberíamos ser capaces de compilar cualquier cosa.

(Si, el título es por esta escena 🙂 )

Actualizando la Gentoo del WebTV desde el PC

Siguiendo con lo que hice el otro día, ahora quería empezar a instalar cosas en el sistema Gentoo del WebTV. Por desgracia la cosa no es tan sencilla porque enseguida pide actualizar algunos paquetes, lo cual tarda mucho tiempo al hacer la compilación en el propio dispositivo. Y por si fuera poco, con alguno necesita tanta memoria que, directamente, casca a la mitad de la compilación.

Afortunadamente hay una forma de hacer todo esto directamente en un PC, pero haciendo creer al sistema Gentoo que está corriendo de forma nativa en un sistema Mipsel. Para ello sólo necesitamos QEMU.

Para empezar necesitamos el binario /usr/bin/qemu-mipsel-static, así que buscamos en qué paquete está disponible y lo instalamos en nuestro sistema. En el caso de Debian, el paquete es qemu-user-static. Este binario nos permite ejecutar binarios de la arquitectura deseada, pero (y esto es lo interesante) encaminando las llamadas al núcleo directamente al de la máquina física, con lo que no necesitamos compilar otro núcleo.

Ahora descomprimimos el fichero entorno_gentoo_mipsel.tar.bz2 en un directorio (por ejemplo, en /tmp), y descomprimimos en lugar adecuado (en nuestro ejemplo, en /tmp/bg_apps/usr) también el fichero de portage, tras bajarlo. Por último, copiamos /usr/bin/qemu-mipsel-static dentro de nuestro sistema mipsel (en nuestro caso, en /tmp/bg_apps/usr/bin/). Con esto hemos terminado los preparativos.

Ahora lanzamos nuestra sesión mediante:

sudo systemd-nspawn -u 1000 -D /tmp/bg_apps /bin/bash

De esta manera lanzamos nuestra sesión como usuario 1000 (que es el que usa el WebTV cuando se arranca una sesión en segundo plano). Además, gracias a que copiamos el binario de qemu, los binarios de mipsel se ejecutarán directamente, sin ningún problema, como si fuesen nativos de nuestro sistema (por increíble que parezca).

Una vez hecho esto ya podemos actualizar el sistema y demás, sin temor a quedarnos sin memoria y a mucha más velocidad. Pero echad un vistazo también a esta entrada posterior: https://blog.rastersoft.com/?p=1645.

Generando Gentoo para el WebTV

Nota: actualizado el parche para BusyBox.

Estos días estoy bastante liado con el trabajo-que-paga-las-facturas, pero por suerte he podido sacar un rato para cacharrear. Me he puesto con el WebTV (que tengo bastante abandonado desde que me compré la Raspberry Pi) y he decidido intentar meter un sistema «decente». ¿A qué me refiero? Pues a que, por defecto, la máxima versión de Debian que puede correr es wheezy, que ya es old-stable. La estable actual (jessie) necesita un núcleo más reciente, y se niega a trabajar con el que trae el WebTV (2.6.22).

Ante esto decidí probar con Gentoo, a ver si conseguía compilarlo todo. A continuación indicaré como lo hice.

NOTA: para los que no quieran leerse este tocho, en mi web está disponible este entorno Gentoo completo para WebTV, ya compilado y listo para usar.

Para empezar, me bajé la stage 3 de Gentoo para X86_64, bajé también la última lista de paquetes de portage, y descomprimí ésta última en /usr de la stage 3. Con ello ya pude lanzar un contenedor de gentoo con

sudo systemd-nspawn -D /directorio/con/la/stage3 /bin/bash

Aquí toca primero preparar el sistema para hacer crossdev. Esto se puede repasar en una entrada anterior: Emergiendo.

Una vez dentro intenté hacer un crossdev para compilar una gentoo para mipsel usando

crossdev --kernel 2.6.22 -t mipsel -v

Por desgracia fallaba: se empeñaba en utilizar las cabeceras de la versión 2.4.36. ¿Qué pasaba? Pues que aunque en los repositorios de Gentoo sí existen las cabeceras del kernel 2.6.22, éstas no están disponibles en la lista de ebuilds de portage.

Ante esto empecé a buscar y probar, y finalmente con la ayuda de la gente de IRC del canal #Gentoo-kernel conseguí el ebuild de las cabeceras para la versión 2.6.22-r2. Sin embargo tuve que grabarlo en /usr/portage/sys-kernel/linux-headers con el nombre linux-headers-2.6.22-r3, pues la R3 es la versión disponible en el repositorio.

Tras ello actualicé el fichero Manifest para que encontrase el nuevo ebuild con

ebuild /usr/portage/sys-kernel/linux-headers/linux-headers-2.6.22-r3.ebuild manifest

Probé de nuevo a generar el crossdev pero seguía intentando usar la versión 2.4.36… porque la versión 2.6.22-r3 es posterior a la 2.6.22 a secas. Cambiando el parámetro en crossdev solucionó el problema.

crossdev --kernel 2.6.22-r3 -t mipsel -v

Por desgracia, ahora me encontraba con otro: ocurría un error durante la instalación de las cabeceras:

HOSTCC  scripts/unifdef
scripts/unifdef.c:209:25: error: conflicting types for 'getline'
 static Linetype         getline(void);
                         ^
In file included from scripts/unifdef.c:70:0:
/usr/include/stdio.h:678:20: note: previous declaration of 'getline' was here
 extern _IO_ssize_t getline (char **__restrict __lineptr,
                    ^
scripts/Makefile.host:118: recipe for target 'scripts/unifdef' failed
make[1]: *** [scripts/unifdef] Error 1
Makefile:927: recipe for target 'headers_install' failed
make: *** [headers_install] Error 2
emake failed

Tocaba buscar más soluciones. Afortunadamente esta era sencilla: bastaba con editar el fichero unifdef.c y sustituir todas las ocurrencias de getline por otra cosa, como por ejemplo get_line. Preparé el siguiente parche para ello:

--- a/scripts/unifdef.c
+++ b/scripts/unifdef.c
@@ -206,7 +206,7 @@ static void             done(void);
 static void             error(const char *);
 static int              findsym(const char *);
 static void             flushline(bool);
-static Linetype         getline(void);
+static Linetype         get_line(void);
 static Linetype         ifeval(const char **);
 static void             ignoreoff(void);
 static void             ignoreon(void);
@@ -512,7 +512,7 @@ process(void)
 
 	for (;;) {
 		linenum++;
-		lineval = getline();
+		lineval = get_line();
 		trans_table[ifstate[depth]][lineval]();
 		debug("process %s -> %s depth %d",
 		    linetype_name[lineval],
@@ -526,7 +526,7 @@ process(void)
  * help from skipcomment().
  */
 static Linetype
-getline(void)
+get_line(void)
 {
 	const char *cp;
 	int cursym;

Y entonces me encontré con el problema de como aplicarlo durante la generación del crossdev. La cosa no era sencilla, porque se empeña en comprobar los valores de sha256, sha512 y whirlpool de todo lo que baje. En teoría se pueden añadir parches manualmente en /etc/portage/patches, pero tras probar de todo no conseguí que funcionase, así que al final fui a la solución cazurra y metí el comando de parcheado directamente en el ebuild. Para ello edité el fichero /usr/portage/sys-kernel/linux-headers/linux-headers-2.6.22-r3.ebuild y lo dejé como sigue:

# Copyright 1999-2007 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/cvsroot/gentoo-x86/sys-kernel/linux-headers/Attic/linux-headers-2.6.22-r2.ebuild,v 1.11 2008/04/12 22:24:36 vapier dead $

ETYPE="headers"
H_SUPPORTEDARCH="alpha amd64 arm cris hppa m68k mips ia64 ppc ppc64 s390 sh sparc x86"
inherit kernel-2
detect_version

echo ${PV}
echo ${PATCH_VER}

PATCH_VER="3"
SRC_URI="mirror://gentoo/gentoo-headers-base-${PV}.tar.bz2"
[[ -n ${PATCH_VER} ]] && SRC_URI="${SRC_URI} mirror://gentoo/gentoo-headers-${PV}-${PATCH_VER}.tar.bz2"

KEYWORDS="-* alpha amd64 arm hppa ia64 m68k mips ppc ppc64 s390 sh sparc x86"

DEPEND="dev-util/unifdef"
RDEPEND=""

S=${WORKDIR}/gentoo-headers-base-${PV}

src_unpack() {
        unpack ${A}
        cd "${S}"
        [[ -n ${PATCH_VER} ]] && EPATCH_SUFFIX="patch" epatch "${WORKDIR}"/${PV}
        patch -p1 < /getline.patch
}

src_install() {
        kernel-2_src_install
        cd "${D}"
        egrep -r '[[:space:]](asm|volatile|inline)[[:space:](]' .
        headers___fix $(find -type f)
}

src_test() {
        make ARCH=$(tc-arch-kernel) headers_check || die
}

Luego copié el texto del parche en el fichero /getline.patch y actualicé de nuevo el manifest. Ahora, siempre que se intente instalar el paquete, se parcheará correctamente.

A intentarlo otra vez… y otra vez falla, esta vez porque la versión 2.20 de glibc necesita, al menos, un kernel 2.6.32. Como el núcleo disponible es el que es, toca probar con una versión anterior. La 2.19r1 fue suficiente:

crossdev --kernel 2.6.22-r3 --l 2.19-r1 -t mipsel -v

Finalmente, con esto ya es capaz de compilar glibc. Ahora vienen las curvas, porque no es capaz de compilar el soporte de Fortran para el GCC, ni las pruebas sanity ni algunas cosas más, así que toca armarse de paciencia e ir probando opciones de USE hasta que todo compile. El resultado final es que hay que usar la siguiente línea (añadí el -X para reducir las dependencias, pues en el WebTV no es necesario):

USE="-fortran -sanitize -X" CFLAGS="-O2 -pipe" crossdev --kernel 2.6.22-r3 --l 2.19-r1 -s4 -t mipsel -v

Inicializamos los wrappers de compilación cruzada…

emerge-wrapper --target mipsel-unknown-linux-gnu --init

Y ya tenemos el sistema de compilación cruzada para MIPSel. Ahora toca configurar el entorno y preparar el sistema. Para ello lo primero es borrar el enlace /usr/mipsel-unknown-linux-gnu/etc/portage/make.profile (que, por defecto, apunta a /usr/portage/profiles/embedded) y sustituirlo por uno que apunte a /usr/portage/profiles/ default/linux/mips/13.0/mipsel.

Una vez hecho esto editamos /usr/mipsel-unknown-linux-gnu/etc/portage/make.conf, y ahí modificamos la línea donde se define el USE para añadir, al menos, -fortran -sanitize -X -iptables. También podemos añadir, opcionalmente, un MAKEOPTS=»-jX» (siendo X el número de núcleos de nuestro procesador más uno), para que la compilación sea más rápida.

También tenemos que editar el fichero /usr/mipsel-unknown-linux-gnu/etc/portage/package.mask, y añadir estas líneas:

>sys-libs/glibc-2.19-r1
>sys-kernel/linux-headers-2.6.22-r3

Con ellas evitamos que instale versiones posteriores de ambos paquetes, que harían que el sistema dejase de funcionar en nuestro WebTV.

Con esto ya podemos intentar generar nuestro sistema base con

emerge-mipsel-unknown-linux-gnu system

Si falla al compilar Busybox, es probable que haya alguna opción que no le gusta. En ese caso hay que editar su fichero .ebuild en /usr/portage/sys-apps/busybox/busybox-X.Y.Z.ebuild, añadir las opciones de configuración que se quieren activar o desactivar, y luego ejecutar ebuild /usr/portage/sys-apps/busybox/busybox-X.Y.Z.ebuild manifest para actualizar el manifest. En mi caso, el problema es que se activan por defecto el soporte de UBIFS y de I2C, cosa que no parece gustarle, así que para eliminarlo tuve que añadir las siguientes líneas en el sitio adecuado del ebuild:

busybox_config_option n I2CGET
busybox_config_option n I2CSET
busybox_config_option n I2CDUMP
busybox_config_option n I2CDETECT
busybox_config_option n UBIATTACH
busybox_config_option n UBIDETACH
busybox_config_option n UBIMKVOL
busybox_config_option n UBIRMVOL
busybox_config_option n UBIRSVOL
busybox_config_option n UBIUPDATEVOL

Con suerte, en un par de días este parche ya estará incluido en los repositorios oficiales.

Otro problema, esta vez más grave, es con Perl: se trata de un paquete al que no le gusta que le hagan compilación cruzada. El resultado es que, simplemente, no podemos instalarlo así. La solución consiste en, de momento, hacer creer al sistema que sí está instalado, e instalarlo manualmente desde el sistema final una vez que ya estamos en el equipo. Para hacer esto basta con editar el fichero /usr/mipsel-unknown-linux-gnu/etc/portage/profile/package.provided y poner, en cada línea, los paquetes que queremos marcar como instalados. Hice lo mismo con los paquetes de UDev, que tampoco los necesito. En mi caso su contenido fue:

dev-lang/perl-5.22
virtual/perl-Data-Dumper-2.158.0
perl-core/File-Temp-0.230.400-r1
virtual/perl-File-Temp-0.230.400-r3
dev-perl/Text-Unidecode-1.230.0
dev-perl/libintl-perl-1.240.0
virtual/perl-File-Spec-3.560.0
dev-perl/Unicode-EastAsianWidth-1.330.0-r1
sys-fs/udev-222
virtual/udev-217
sys-fs/udev-init-scripts-30
virtual/dev-manager-0

Pero, obviamente, depende de la versión de portage y de los paquetes disponibles.

Tras instalar todo esto, si el equipo es de 64 bits nos encontraremos con que nos ha metido varios elementos de python en /usr/lib64, cuando todo debería ir en /usr/lib. Es por esto que debemos mover todos los ficheros del primero al segundo.

Ahora ya podemos copiar el contenido de /usr/mipsel-unknown-linux-gnu/ a un disco duro externo (dentro de una carpeta llamada bg_apps), añadir un fichero init y otro vacío llamado no_base_system, y ya podemos arrancar nuestro sistema Gentoo en el WebTV.

Pero aún no hemos acabado. Para empezar, es necesario hacer el siguiente enlace cada vez que se encienda el equipo:

ln -s /proc/self/fd /dev/fd

para que emerge funcione correctamente. También es recomendable editar el fichero /etc/portage/make.conf y eliminar la opción de compilación -pipe, pues consume más memoria, y en un equipo relativamente limitado como el WebTV nos puede dar problemas con compilaciones muy tochas.

Por otro lado, tenemos que comentar las entradas de Perl que pusimos en el fichero /usr/mipsel-unknown-linux-gnu/etc/portage/profile/package.provided, y procer a instalarlos todos con emerge.

No hay que olvidar que, debido a la gran cantidad de ficheros que tiene el directorio /usr/portage, el arranque de la sesión en segundo plano del WebTV tardará bastante tiempo (en torno a un minuto), pues antes de lanzar la sesión, el sistema revisa todos y cada uno de los ficheros para asegurarse de que no hay «cosas raras».

Un voladizo para la mesa del ordenador

Por mucho espacio que tenga siempre acabo necesitando más, y como la mesa del ordenador se me empezaba a quedar pequeña decidí construir un voladizo para ganar espacio, al tener así un estante superior donde poner la impresora y otros elementos.

Empecé por comprar las piezas. En tiendas como Bricoking o Bricocentro recortan tablero a medida y lo cantean en base a nuestras instrucciones. La lista completa es la que aparece en la siguiente imagen, indicando cuantas de cada una se necesitan y el tamaño en centímetros de cada lado. Aquellos lados que tienen un punto son los que deben ir canteados. Las medidas indicadas están pensadas para una mesa de 138 cm de largo, y para tablero de 2 cm de grosor.

piezas

Las piezas azules son los laterales del voladizo, que sostienen el peso; la pieza amarilla es la parte superior, y las verdes son los listones frontal y trasero, que, además de tapar la zona inferior de la pieza superior, le da más resistencia. Las dos piezas naranjas son los estantes laterales, y la pieza rosa y la gris forman el fondo, donde además se colgará el monitor.

Empezamos por fijar a la parte superior los dos listones, usando cuatro ángulos de 20x20mm. Nótese que los listones son cuatro centímetros más cortos que la parte superior para que encajen con los laterales, que tienen dos centímetros de grosor cada uno:

re_IMG_20140513_191355 re_IMG_20140513_191550

re_IMG_20140513_193423

A continuación añadimos las dos piezas laterales, fijándolas también con ángulos tanto a la parte superior como a los dos listones:

re_IMG_20140513_200141

Y con esto tenemos la primera parte:

re_IMG_20140513_200158

Ahora podemos colocarla encima de la mesa y fijarla con cuatro ángulos para que no se mueva ni se nos caiga nada. Otra opción sería utilizar tacos de madera para que no se vea, pero en mi caso prefiero utilizar algo lo más resistente posible:

re_IMG_20140513_213416

El siguiente paso consiste en fijar los estantes laterales (piezas naranjas). Para ello utilicé el soporte ekby töre de Ikea, ideal para estas cosas: fijé dos en cada tablero por la parte trasera, los fijé a la mesa, y finalmente atornillé cada tablero a su pieza lateral por la zona delantera para que el conjunto aguante más peso (mi intención es poner el ordenador encima de una de ellas).

re_IMG_20150114_171233

La última parte es la zona posterior, formada por las piezas rosa y gris. La rosa es la parte frontal, que mirará hacia nosotros, y la gris es la de refuerzo. El motivo es que estas dos piezas sostendrán nuestro monitor, además de reforzar el voladizo para que no tienda a doblarse, por lo que nunca está de más garantizar que tenga una buena resistencia. Empezamos por colocar una sobre otra y atornillarlas para que queden perfectamente solidarias:

re_IMG_20150113_131736 re_IMG_20150113_132939 re_IMG_20150113_134553

Si nos fijamos, la pieza gris es algo más corta que la rosa. Esto es para que no tropiece con el listón posterior que va en el voladizo.

El siguiente paso consiste en fijar el soporte para el monitor. Yo escogí uno fijo porque ocupa menos espacio (la ventaja de colgar el monitor es que ganamos el espacio que ocupaba el soporte, y nos deja mover el teclado hasta el fondo por si queremos leer o hacer alguna otra cosa). Para ello empezamos por medir a qué altura están los agujeros del soporte VESA de nuestro monitor y, en base a ello, fijar la base:

re_IMG_20150113_134730 re_IMG_20150113_135946 re_IMG_20150113_165440

Ahora hay que añadir dos ángulos en la parte superior para fijar el tablero al voladizo:

re_IMG_20150113_170933

A continuación hay que hacer los agujeros para pasar los cables de alimentación y vídeo. Primero hacemos una plantilla de la disposición concreta de los conectores en nuestro monitor (truco: usar los tornillos del soporte para fijar un folio a los agujeros del monitor, y así tener una guía):

re_IMG_20150113_180735

El siguiente paso consiste en pasar la plantilla al tablero, alineándola con el soporte instalado (¡no olvidar darle la vuelta!) y marcar los puntos en los que deben ir los agujeros:

re_IMG_20150113_181203

Hecho esto utilizamos una corona y un taladro para perforar el tablero en los puntos indicados, y procedemos a montar la pieza en la mesa, fijándola a la parte superior mediante los dos ángulos:

re_IMG_20150114_121636 re_IMG_20150114_122420

Ahora fijamos por detrás la parte inferior del tablero para evitar que se mueva, utilizando un ángulo más largo. En la foto vemos que el ángulo se atornilla también por debajo de la mesa:

re_IMG_20150114_123107

Hecho esto colocamos el monitor en el tablero y conectamos los cables:

re_IMG_20150114_123404

Yo añadí a mayores un tubo fluorescente debajo de la balda para tener luz extra si un día quiero hacer algo en la mesa que no sea trabajar con el ordenador. Este tubo está justo detrás del listón frontal:

re_IMG_20150114_155201

Y con esto ya está completo nuestro maravilloso voladizo:

re_IMG_20150114_124608

Arrancando la Raspberry Pi desde un disco duro externo

Estas navidades me he autorregalado una Raspberry Pi, porque no se puede ser un friki de verdad sin una. Mi primera intención es utilizarla para reemplazar al cacharro con Android del que hablé en entradas anteriores. Las ventajas son dobles: para empezar, consigo un sistema 100% integrado, y no el híbrido frankensteniano que tenía antes; y, por otro, me queda el cacharro Android para experimentar más y perfeccionar la técnica de arranque dual.

Para no complicarme la vida escogí la distribución raspbmc. Esta distro tiene algo de truco a la hora de instalarla. Además, como es normal en la Raspberry, es necesario arrancar siempre desde la tarjeta SD, cosa que a mi no me convence porque no quiero que acabe quemada por excesivas escrituras (a fin de cuentas me gusta cacharrear). Para evitarlo, decidí mover el sistema operativo a un disco duro USB de un terabyte, y así tener también sitio donde almacenar más cosas. El procedimiento a seguir es el siguiente: primero descargamos la imagen y la volcamos a la tarjeta con el comando:

sudo dd if=raspbmc-2014-11-24.img of=/dev/sde bs=2048

Asumiendo, claro está, que nuestra tarjeta SD está en /dev/sde.

Una vez creada la tarjeta, la insertamos en nuestra Raspberry y la encendemos SIN CONECTAR A LA RED LOCAL. Lo primero que hará será redimensionar la partición para que ocupe toda la tarjeta, y luego lanzará el XBMC. Y aquí está el primer problema: si en ese momento está conectada a Internet, se bajará una actualización de éste y, por algún motivo que desconozco, fallará y la imagen quedará inutilizable, teniendo que volcar de nuevo el fichero en la tarjeta. Es necesario dejar que arranque primero y se configure, y sólo entonces conectarlo a Internet y dejar que se actualice.

Otro problema que descubrí es que, por defecto, se configura en 1080p, y mi tele no soporta ese modo. Para resolverlo tuve que conectar la placa al monitor del ordenador y allí configurar la salida a 720p.

Una vez que tenemos la tarjeta lista, vamos a volcar todo al disco duro. Si montamos la tarjeta en nuestro PC veremos que tiene dos particiones: una de 73 MB en formato FAT32, y otra en formato EXT4. La primera partición contiene el kernel y demás ficheros de arranque básico, por lo que esa, de momento, no la tocaremos, pues es necesaria para iniciar el arranque; la segunda es la que contiene el sistema Linux, y será la que vamos a mover al disco USB.

Comenzamos por crear tres particiones en nuestro disco duro: una de 20GB en formato EXT4 para el sistema, otra de 4GB de swap, y una tercera con el resto para datos. A continuación, copiamos todos los ficheros de la partición EXT4 de la tarjeta a la partición de sistema del disco duro, utilizando la opción -a de cp para que, al copiar los ficheros, conserven su tipo, propietario, permisos y demás características (de no hacerlo así, los ficheros de dispositivos se copiarían como un fichero normal con el contenido del dispositivo, en lugar de como ficheros especiales; de igual forma los enlaces simbólicos no se copiarían como tales, y más problemas y diferencias). Además, es fundamental hacerlo como root, pues si no, no podría cambiar el tipo de usuario:

sudo cp -a /media/disco_origen /media/disco_destino

Otra opción es comprimirlo en un fichero tar.gz para poder restaurar cómodamente la partición en cualquier momento, pero eso ya es a gusto del consumidor.

Una vez hecho esto vamos a modificar el arranque en la tarjeta SD para que vaya al sistema situado en el disco duro. Para ello abrimos el fichero cmdline.txt, que contiene la línea de arranque para el núcleo. En ella buscamos el parámetro root=/dev/mmcblk0p2 y lo reemplazamos por root=/dev/sda1 (o la partición del disco en el que vamos a copiar el sistema), y añadimos el parámetro rootdelay=5 para asegurarnos de que reconoce el disco antes de intentar arrancar de él.

Por último, podemos editar el fichero /etc/fstab y añadir las siguientes líneas para que monte automáticamente la partición de swap y de datos donde queramos:

/dev/sda2   none   swap   sw        0   0
/dev/sda3   /datos ext4   defaults  0   1

Y con esto tendremos listo nuestro sistema Raspbmc en disco USB. En una próxima entrada comentaré como monté una sesión chroot, porque la raspbmc está basada en Debian Wheezy y cualquier intento de actualizarla a Jessie acaba con el sistema dañado (incluso probé a utilizar pinning, pero es muy fácil que para instalar algún paquete interesante haya que actualizar la libc6, lo que hace que casque todo).

Tablet con systemd

Hace un par de días actualicé el sistema Debian que le había instalado a mi tablet, y me encontré con la desagradable sorpresa de que mi gestor de ventanas y mi driver táctil dejaron de funcionar. El motivo es que, recientemente, Debian se ha cambiado a systemd, por lo que tuve que hacer algunos cambios para adaptarlo.

El primer y más fundamental cambio fue reemplazar los scripts en bash por ficheros de configuración de systemd. Este es el fichero para lanzar el driver táctil:

[Unit]
Description=GSLx680 user-space driver launcher for systemd

[Service]
Type=simple
ExecStart=/bin/gslx680 -new_scroll /dev/i2c-1 /etc/gslx680/firmware.cfg
ExecStop=killall gslx680

[Install]
WantedBy=multi-user.target

Este fichero lanza durante el arranque el driver, y durante el apagado del sistema lo mata.

El fichero para lanzar las X es similar:

[Unit]
Description=Launch X11

[Service]
Type=simple
User=debian
ExecStart=/usr/bin/startx
ExecStop=killall xinit

[Install]
WantedBy=multi-user.target

El único cambio es que especificamos con qué usuario queremos lanzar el comando: en este caso el usuario es debian.

Otro cambio que tuve que hacer fue eliminar, en el fichero .xinitrc, el que se lance ck-launch-session, el gestor de sesiones de ConsoleKit. Este ya no es necesario porque de ello se encarga systemd.

Por último, para apagar el sistema el gestor de ventanas ya no ejecuta halt, sino systemctl poweroff, con lo que ya no es necesario que haya un comando para ello con el bit suid activo.

Un último detalle: tuve que desinstalar el demonio pulseaudio para conseguir que reprodujese vídeos. Todavía no se el motivo de que con él lanzado no funcione (el audio queda bloqueado y tanto mplayer como vlc se quedan congelados esperando a que se libere).