Monday, November 03, 2008

Hacking gspca for Logitech QuickCam E1000 support.

Hop, je fais le saut: j'achète une webcam. Pas chère (25 francs) avec oreillette inclue "für skype" ... Mon choix s'est arrêté sur la QuickCam E1000 de Logitech, dans un rayon qui semble comporter une douzaine d'autres webcam qui se ressemblent toutes, en ce qui me concerne, hormis peut-être leur prix.

Je branche ça sur mon portable linux ... et évidemment ça ne marche pas tout seul. Le CD ne comporte que des drivers pour windows, ce qui n'est pas une surprise non-plus. Haa... on a pas encore fini de changer le monde.

Bref. Un petit tour de google, et je tombe là-dessus : http://doc.kubuntu-fr.org/spca5xx "installer gspca sous ubuntu, la version facile" ... je prends. je suis les instructions à la lettre, le module (comprenez "le driveur" pour les windoziens) s'installe. c'est bien. Sauf que point de webcam.

I just blindly bought the Logitech QuickCam E1000 this week-end (since it was the cheapest webcam around) and of course realised that it did not came with any Linux driver (not that it's surprising in any way) nor does it seems to be supported by any "out-of-the-box" driver for Ubuntu gutsy (unless i'm proved wrong). Hacking around, checking lsusb and reading the sources of the gspca driver, i figured out that it was yet-another-generic-driver that has a "white list" of supported devices. I thus added the product-id to that white list and now proudly have a working webcam. See compiled driver (which might no longer work with Labtec Webcam Pro, because i've really been *hacking* instead of *patching*) and modified sources down the page.

Un petit "lsusb" me donne au moins la référence "technique" du produit : Bus 001 Device 012: ID 046d:08af Logitech, Inc.

Ces deux numéros magiques (vendor-id=046d et product-id=08af) me permettent de voir (dans les sources du driver, en bas à gauche de l'image) que si pas mal d'autres webcam logitech sont supportées, par contre, la mienne n'est visiblement pas là. Comme le "gspca" est un driver "générique" (à savoir qu'il est en fait valide avec toutes les webcams 'standard'), j'ai 9 chances sur 10 pour que le code puisse effectivement faire fonctionner la webcam si j'arrive à expliquer au driver que "si, si, je t'assure, tu la connais aussi, celle-là". Un peu comme si votre clé-télécommande devait reconnaître la plaque minéralogique de votre voiture pour pouvoir la faire démarrer.

Bref. Premier jeu, donc, ajouter dans gspca_core.c l'identité de notre caméra. Perso, je parasite n'importe quelle entrée dans device_table[], mais il serait plus propre d'y ajouter {USB_DEVICE(0x046d, 0x08af)}, /* Logitech QuickCam E1000*/ et d'éditer en conséquence clist[] et l'énumération des caméras.

Avec ça, le driver peut dire au noyau que la webcam est pour lui, mais ça ne suffit pas. La fonction spcaDetectCamera() est appelée chaque fois qu'une nouvelle webcam est branchée, histoire de lui souhaiter la bienvenue, de prendre un verre avec les autres périphériques USB ... euh ... ou pas. C'est là que l'on va retrouver un gros "switch" qui va règler les options de notre driver générique pour chaque modèle de caméra (peut-être pas si générique que ça, donc, en fait :P) Je fais bêtement le pari que ma caméra "8af" sera probablement juste une réédition de la "8ae" (QuickCam for Notebooks), et donc :

   case 0x08ae:
case 0x08af: // uber-experimental.
    spca50x->desc = QuickCamNB;
    spca50x->bridge = BRIDGE_ZC3XX;
    spca50x->sensor = SENSOR_HDCS2020;
    break;
Et hop! ça marche! J'adore linux!

le module recompilé pour gutsy : gspca_e1000.ko le fichier gspca_core modifié pour la QuickCam E1000 : gspca_core_e1000.c (soyons open-source jusqu'au bout ;)

Et pour ceux qui trouvent que "Aarhgh! Skype, c'est le mal", je propose la lecture de l'édifiant "Castle in the Skype" de Fabrice DESCLAUX, puis je leur dirai que MSN, c'est pas forcément mieux ;)

edit: cette manipulation est valable sur Gutsy uniquement. Sous Hardy Heron (et probablement les versions ultérieures), les drivers pré-compilés supportent directement la caméra.

Friday, October 24, 2008

:* idle mode *:

Le boulot en Suisse est plus trépidant que prévu ... peu de temps consacré à Bilou (entre-autres pour des bêtes détails de prise électriques non-compatibles qui rendent l'allumage de portable un peu lourd).

Mais bon, j'ai re-fini Zelda: Phantom Hourglass, et donc je vais sans doute passer plus de temps sur de la bidouille plutôt qu'à jouer en "pur consommateur" dans les prochains week-ends ...


My first weeks in Switzerland have left me much less spare time that i initially thought ... Partly due to the Generic Path idea that is drawing nearly all my attention ... and combined with the fact that i haven't set up a convenient place to code at "home" yet, nor a comfortable place to sit down and think (which should fix tonight).

Plus i could not resist the Phantom Hourglass box on the shelf and played the game once more (and confirming that it is damn too easy, way too short and stuff ... i'm going to let my nephew have it, i think)

Friday, October 10, 2008

mon routeur à un DNS qui rame ...

Bon, avec ces routeurs Wifi qui deviennent de plus en plus fréquents, j'imagine ne pas être le seul à avoir le problème : ce genre de routeur se renseigne généralement comme "serveur" DNS local (en gros, c'est lui qui se chargera de savoir qui est "google.com" et tous les autres sur le réseau), supplantant le serveur de votre ISP ... Ce qui ne serait pas bien grave si ce brave petit routeur WiFi avait la puissance et la mémoire nécessaire pour faire ce boulot ...

Résultat: une navigation ralentie par de longues attentes sur des noms par rapport à une connexion directe avec le modem de l'ISP. Que faire ?
Première chose, apprendre l'adresse du fameux serveur DNS (serveur de nom) de votre ISP. Jetez un coup d'oeil au fichier /etc/resolv.conf ... s'il renseigne "nameserver 192.168..." ou "nameserver 10.0..." (qui sont des adresses IP "locales", utilisées uniquement derrière ce genre de routeur-pare-feu), c'est qu'effectivement le routeur sert de serveur de noms. Réessayez alors sans le routeur, et vous aurez peut-être droit à quelque-chose comme "nameserver 123.56.1.1", que vous vous empresserez de copier sur un post-it (c'est le serveur de nom de votre ISP ... le vrai). (PS: vous pouvez sans doute aussi apprendre ça en interrogeant l'interface Web de votre routeur ou en retrouvant dans votre fouilli le contrat de l'ISP ;)

Rebranchons notre routeur, maintenant, et une fois le réseau prêt à fonctionner, éditons comme un grand sauvage le fichier /etc/resolv.conf (à coup de sudo, bien sûr) pour remplacer "nameserver 192.168.1.1" par "123.56.1.1", par exemple ... Est-ce que la connexion a l'air de se faire mieux ? Si oui, c'est qu'il y avait bien un soucis de DNS ...

Evidemment, ce serait trop simple que cela suffise. /etc/resolv.conf est un de ces fichiers qui est écrasé à chaque démarrage par le client DHCP (celui qui demande au routeur gentillement de lui donner une adresse et de s'ajuster au réseau et qui vous permet du coup de vous ballader du bureau à la maison sans vous poser trop de questions). On va donc devoir aller éditer cette fameuse config DHCP (dans /etc/dhcp3/dhclient.conf sur mon ubuntu), le truc étant d'utiliser la commande "prepend" pour forcer une configuration statique à prendre le pas sur la config automatique.

send host-name "";
#send dhcp-client-identifier 1:0:a0:24:ab:fb:9c;
#send dhcp-lease-time 3600;
#supersede domain-name "fugue.com home.vix.com";
prepend domain-name-servers 123.56.78.1;
prepend domain-name-servers 123.56.1.1;

et voilà. Les "sudo /etc/init.d/networking restart" n'auront plus raison de notre beau resolv.conf ...

PS: c'est geek, hein ;) oui, je sais, c'est plus simple quand il y a des cases à cocher, mais à défaut, on a un peu appris comment ça marchait ;)
PPS: oui, j'ai finalement moins bricolé ma DS pendant ma première semaine en Suisse que je ne l'aurais cru ... et je n'ai pas senti le besoin de blogger les petits progrès qui avaient quand-même été réalisés ... d'où un blog un peu plus calme, ne vous en déplaise.

Friday, September 26, 2008

Screwed up terminals: the solution

Anyone who has been doing C programming on Linux has encountered this at least one, or is a very rigourous programmer. A certain binary sequence (that you normally never encounter in ASCII text, but that might be present in that binary file you accidentally dumped on your terminal) will switch you to the "graphical and lines" character set where everything gets completely unreadable.

Si vous programmez sous Linux ce genre d'écran illisible ne vous est forcément pas inconnu. Tôt ou tard, vous (ou un de vos collègues) a forcément malencontreusement envoyé un fichier mp3 ou une image ou un programme sur la sortie du terminal et pas de chance: il contenait cette petite séquence ANSI qui force la console à passer sur le jeu de caractères graphiques, utilisé entre autres par ncurses pour dessiner ses boites et ses menus.

It was fairly easy to fix under SuSE and RedHat back in Y2K as a simple "reset" command typed on the terminal restored the display. Unfortunately, ubuntu Linux distributions do not seem to think a terminal reset should include reset the character set to its default value...

It occurred once again today. Once too much. I dug the ANSI commands set and wrote a small perl tool that let me toy with ANSI escape codes. Here it comes: ANSIHACK.PL (in uppercase so that you can recognize it even when your terminal is screwed :P)

#!/usr/bin/perl
print "\x1b($1\nMODE $1 ENABLED.\n" if $ARGV[0]=~/\-\-SET([0-9A-B])/;
print "\x1bc\x1b(A" if $ARGV[0] eq '--RESET';
shift @ARGV if $ARGV[0]=~/^\-\-/;
print "\x1b[$ARGV[0]\n";
print "done/DONE.\n";
exit(0);
Actually, the commands that trigger a charset switch are Esc(x and Esc)x. Charset A is our default, regular 'ascii' charset (used in --RESET form) but you can also toy with other charsets (B, 0, 1 and 2) with e.g. ANSIHACK.PL --SET0 (that screws things up) and ANSIHACK.PL --SETA (which restores things to normal).

C'était assez simple à résoudre du temps de SuSE et RedHat (tapez 'reset' en aveugle, puis appuyez sur ENTER les doigts croisés en sautant sur un pied un soir de pleine lune avec un baton de réglisse entre les dents). Mais sous ubuntu. Rien. reset efface le contenu du terminal, mais ne résoud pas le problème qui nous occupe. D'où ce petit script pour jouer avec les commandes ANSI. Les vieux de la vieille pourront se faire un alias reset='bin/ANSIHACK.PL --SETA' ;)

|-|oPe !7 |-|e|_p$ ;)

edit: the real explanation is that a '0x0e' character (shift out) had been issued and only a '0x0f' character (shift in) would bring the default charset. The ANSI hack almost works, but it screws up the '#' character into some £...

Tuesday, September 23, 2008

Biokid NT : countermeasures

Quelque part en 1996, mon frère Piet me pond une storyline un peu abracadabrante inspirée d'un vieux souvenir (le Stoner): votre PC est infecté par le terrible virus Terminator 007. Le seul moyen d'en venir à bout, c'est le nouvel anti-virus interactif de PPP Team Software: Biokid.

Back in 1996, my brother's mind give birth to a curious story: your PC is infected by a dangerous virus -- the Terminator 007 -- and the only thing that can get rid of it is the brand-new interactive antivirus from PPP Team Software: Biokid. To put it simpler, Biokid is a crossing over between Megaman character and Commander Keen level design where you hunt for keys and weapon upgrades in maze-like platformer. It turned into one of our best production on RSD Game Maker.

L'idée, c'était de tenter de s'approcher du gameplay d'un Megaman. Mais là, j'ai envie d'un petit jeu au principe plus simple (un shoot'm'up) que Bilou pour roder un peu mon Game Engine, et plutôt que de ressortir Bilou Sky Quest (C64) ou d'essayer un portage de Out'm'up (assembleur, Y2K), je me suis dit que j'allais ressortir Biokid dans un nouveau mode de jeu: "Counter Measures".

Plus question d'assurer passivement la défense de votre réseau: Biokid prend les devant et part vaillamment à l'assaut du plus grand Botnet qu'il vous a jamais été donné de combattre.

I was thinking of giving my game engine for DS a try in a different setup : a shoot-m-up. I could have reused "Out'm'Up", our brain-free shooter that won the 100K game competition at Inscene Y2K. Or I could have revived "Bilou Sky Quest", the test game I made on C64 with the "Shoot-Em-Up Construction Kit". For some reason I started dreaming of a shooter featuring Biokid who'd now have to protect your corporate network proactively and fight the largest and scariest Botnet ever fought. (hmm. I could have been "inspired" by the Bionet EU project a colleague of mine is working on, btw).

edit: bon, comme vous le voyez, côté graphique, il y aura du boulot ... je voudrais recréer une ambiance "newschool" inspirée de Matrix, Tron et autres Space Invaders Extreme ... on verra ce que ça donne ...

Monday, September 15, 2008

Programmatically surrealistic


Earlier in the day, i had an argument with the update-manager of my test machine who was refusing to update my Dapper Drake because of some "hash mismatch" at the mirror. For some reason, i didn't trust the ubunutu forums that state that "this occur when you try to update while mirrors are synchronizing" and finally discovered that the way hashes (that confirm your softwares' authenticity) are managed has changed with Hardy Heron ... Doh. Fortunately enough, someone is still thinking that some user prefer to update from a text console rather than having a "user-friendly" graphical interface but only at the cost of burning a new ISO distribution and moving your feets two level down in the lab.

So my thanks fly to the maintainers of the update-manager-core package and the sudo do-release-upgrade -m desktop trick that did the whole upgrade without pop'ing any window and without silly hashing bugs.

You thought that would have been the weirdest thing around? You were wrong.

With the machine updated, i'm now able to test the new release of that European project i'm working on ... The software has a thing called "the minmex" which spawns thread for various sub-tasks and i thought it would be handy to catch those "logfile " lines it produces on stdout, and process them in a script that would automatically start following each new log. Easy as (s)hell :

# invoke this as:
# sudo bin/minmex | perl myscript.pl
while ($_=) {
print;  # just echo back the filename
chomp;
sleep 1; # just let the minmex actually create the file
system "tail -f $1 &" if (/logfile (.*)/);
print "waiting for more ...\n";
}


Except it didn't worked. Only the "master thread" displayed something. Of course, running a plain "sudo bin/minmex" show you all you wish to know on the console :-/
And of course, as usual with the latest libc6, you don't even see any write(1, ...) in the output of an strace bin/minmex. My colleague Cyril supposed something odd with termcaps, but there was absolutely nothing that sophisticated in the minmex. Just a very plain printf("logfile %s"); ...

Fairly puzzling, don't you think ?

Well, now it started getting funny when i realised that i actually was facing this in the 'master' thread:

mkdir("/tmp/log", 0777) = -1 EEXIST (File exists)
fstat64(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbf97285c) = -1 ENOTTY (Inappropriate ioctl for device)
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f7f000
write(1, "logfile /tmp/log/26274_"..., 71logfile /tmp/log/26274_anaMinmex
) = 71
brk(0)                                  = 0x806b000
brk(0x808c000)                          = 0x808c000
open("/tmp/log/26274_anaMinmex", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
.

I'm not very comfortable with those termcaps things, but that ENOTTY error clearly suggested that Cyril was right and that the source code was wrong 0_o ... Using plain write(1,...) instead of printf(...) let me see all the stuff i wanted, but that can hardly be called a solution. After a bit crawling in the sourcecode of libc6, i could not find any direct relationship between tcgetattr (the function using that TCGETS argument) and printf, but some reference to glibc-2.6.1/misc/getpass.c did ring a bell in my mind ...
What if the problem was actually coming from the sudo not liking me to redirect the program's output ? what if, for some reason, it *had* to ensure the access to a terminal (e.g. to prompt me for a password) and failed to properly restore the output-to-next process ?

And indeed, running the whole chain in a regular root session has now shown a perfectly regular behaviour, in other terms:
  • either you shouldn't try to redirect stdout when using printf
  • or you shouldn't try to build multitasking programs
  • or you shouldn't try to run those programs as a sudoer
  • and you certainly shouldn't do all these three things "for chaos and maddness awaits thou at thee end" ...
For your mind's sanity, i just hope that you'll never ever find such a post useful. But in case you happen to struggle with such a debugging nightmare, i hope i've got a sufficiently high pagerank to get ahead all these mailing list that discuss about whether a bug is a bug or not and that you'll get a clear understanding of the situation faster than i did...

HAve a nice day,
/Pype, the crazy coder.

edit: the problem seems to be tcsh-specific (e.g. doing the plain sudo bin/minmex | perl myscript.pl in bash will work) and there are two possible (and both equally surrealistic) work-around for those who don't want to switch to bash:
  • sudo -c tcsh "bin/minmex | perl myscript.pl"
  • sudo -c tcsh "bin/minmex" | perl myscript.pl
I'm accepting any explanation for this, and myself strongly suspecting tcsh to do something wrong when building the pipes between minmex and perl...

Friday, September 12, 2008

Model, View, Controller

Tout à commencé par ED, l'éditeur le plus imbuvable que l'on puisse imaginer (à moins que l'on ne soit un VIMeur auquel cas le pire éditeur possible est évidement EMACS :).

Au commencement, il n'y avait rien que des 1 et des 0
et le souffle d'ED qui planait au-dessus du tout qui n'était rien.
Puis ED dit "# Que le Code soit"
Et le Code fut.

ED sépara les Données du Code;
Il appela le Code ".text" et les données ".stack"
Et il vit que c'était bon.

Bref. Tout ça m'a amené à télécharger la chanson Model View Controller sur le blog de Peteris Krumins. Non seulement c'était bien drôle, mais je crois que c'est l'explication la plus claire du modèle "MVC" qui m'ait jamais été donnée de lire (et écouter). Au départ, j'avais plus ou moins tenté de respecter ce modèle dans la construction de SEDS, quoi que la plupart du temps le "modèle" n'était que des données brutes ... un tableau de bytes ou de shorts dont le rendu est assuré par les widgets.
Model objects represent your applications raison d’tre.
Custom classes that contain data logic and et cetra.
You create custom classes in your app’s problem domain,
then you can choose to reuse them with all the views,
but the model objects stay the same.

Les "fenêtres", elles, assuraient la "glu": c'était mes contrôleurs. Mais là, ça ne va plus du tout. Je me retrouve avec ma fenêtre "éditeur en grille" qui commence a avoir des méthodes "remplir_grille(un_sprite)" ou "remplace_couleurs(une_couleur, autre_couleur)" sans parler de "affiche_tiles_utilisant(une_couleur)" etc.

Bref, il serait sans doute temps de promouvoir le tableau affiché par la Grille en un objet à part entière, et idem pour la palette ... Ca m'éviterait, soit dit-en passant, de copier/coller le code de "affiche_tiles_utilisant(une_couleur)" pour avoir la "méthode" "tue_les_couleurs_inutiles()" sur l'éditeur de palette.

edit: en fait, non. La majorité de SEDS n'a rien à voir avec du MVC. Ce serait presque l'inverse, en fait ^^"
Il faut dire que (paradoxalement), la programmation d'interface graphiques dans les règles de l'art, j'ai toujours trouvé ça chiant comme la pluie >_<

Et pour ceux qui se demanderaient toujours dans quel cerveau dérangé un outil aussi anti-ergonomique que ED a bien pu germer ... regardez donc cette antiquité, encore commune au temps du dévelopement d'Unix: le terminal teletype. Remis à jour pour s'interfacer par RS-232 avec Linux.

On ne sait pas "effacer" de caractère avec ce truc. On ne sait même pas se déplacer de haut en bas dans l'écran. La seule interface, c'est la ligne en cours. On aurait aussi bien pu fonctionner avec des numéros de lignes, façons MS-BASIC...