Warning: Cannot modify header information - headers already sent by (output started at /home/gillesbld/www/weblog/inc/config.php:41) in /home/gillesbld/www/weblog/inc/clearbricks/common/lib.http.php on line 222

Warning: Cannot modify header information - headers already sent by (output started at /home/gillesbld/www/weblog/inc/config.php:41) in /home/gillesbld/www/weblog/inc/clearbricks/common/lib.http.php on line 224

Warning: Cannot modify header information - headers already sent by (output started at /home/gillesbld/www/weblog/inc/config.php:41) in /home/gillesbld/www/weblog/inc/public/lib.urlhandlers.php on line 65

Warning: Cannot modify header information - headers already sent by (output started at /home/gillesbld/www/weblog/inc/config.php:41) in /home/gillesbld/www/weblog/inc/clearbricks/common/lib.http.php on line 247
Embedded weblog - Tag - système 2014-05-14T10:00:05+02:00 Gilles Blanc urn:md5:b402c09b50e67198753bdd4269dc5b19 Dotclear cours de kernel Linux en ligne urn:md5:7a70003531698bf779d18631c2f9ec9b 2010-06-25T12:21:00+02:00 gblanc linux programmationsystème <p>L'annonce (sur <a hreflang="en" href="http://www.linkedin.com/groupAnswers?trk=EML_anet_qa_ttle-0Tt79xs2RVr6JBpnsJt7dBpSBA&amp;gid=87910&amp;viewQuestionAndAnswers=&amp;discussionID=22521370">linkedIn</a>) de ces quelques articles de début de cours sur la <a hreflang="en" href="http://www.crashcourse.ca/introduction-linux-kernel-programming/introduction-linux-kernel-programming">programmation kernel Linux</a> me fait rappeler que des slides de formation peuvent aussi être trouvés sur le web : <a hreflang="en" href="http://free-electrons.com/docs/kernel/">chez FreeElectron</a>, et bien entendu, <a hreflang="fr" href="http://linagora.org/contrib/embarque">chez Linagora</a> !</p> <p>Bonne lecture !&nbsp; :)</p> portage Linux 2.6.25 et 2.6.27 sur carte Embest 2440, via J-Tag urn:md5:875c0dd0a027e4e5416c2667b5978bdd 2008-10-30T15:36:00+01:00 gblanc linux embarqué embarquésbc2440système <p>Suite de nos aventures avec la <a hreflang="fr" href="http://gillesblanc.com/weblog/post/2008/08/29/J-TAG-sur-carte-Embest-SBC244">carte chinoise faisant figurer le S3C2440</a>. Ce fut long et laborieux (surtout à essayer de trouver du temps pour les tests), mais ça boote ! Et avec des logs consoles en ASCII, aussi (la précision a son importance), et un driver de carte réseau qui marche... Il n'y a peut-être pas encore tout de fonctionnel, mais en tout cas on a une console et des programmes qui tournent à la fin, c'est déjà beaucoup quand on sait d'où l'on part à la base (au début, il n'y avait rien...). Notamment une traversée des sites tous chinois, coréens et quelques uns japonais, expliquant plus ou moins comment faire marcher le bestiaux, avec une traduction google très approximative (voire franchement comique), et en réalité, il s'agit plus de la bidouille que de la vraies explications sur les problèmes rencontrés. En ayant eu marre de ramer dans ces sites-là (qu'il faut tout de même remercier, mais c'est dingue, n'y-a-til donc personne parlant Anglais ou Français et pouvant faire le même boulot ?), je me suis finalement attelé tout seul comme un grand au code noyau de A à Z, depuis l'init (problèmes de boot, il faut trouver la bonne config, puis de décompression, trouver la bonne adresse) jusqu'à celui des drivers, en passant par la console qui crachait n'importe quoi. J'ai donc tout d'abord essayé de porter un uClinux, mais ça ne marche pas ; je suis donc reparti avec le même code du 2.6.25 avec MMU, et j'ai vérifié que ce que j'ai écrit dans la suite de ce billet marche bien pour un 2.6.27 (j'ai bidouillé tellement de choses dans le kernel que même les logs font peur à voir&nbsp; :)&nbsp;&nbsp; ).</p> <p>Tout d'abord, la compilation : on ne trouve aucun .config correct sur le Net, voici donc <a href="http://gillesblanc.com/public/gblanc.blogs.linagora.com/conf/config.emb2440-2.6.25">configuration kernel pour carte Embest2440</a>. Comme vous le verrez, j'avais décidé de partir sur un uClinux, mais manifestement l'initialisation de la mémoire se vautre lamentablement (outre un bug impliqué par le numéro de machine écrasé, cf plus tard). Donc retour à l'utilisation normale de la MMU (en réalité, sur ARM, il n'est pas possible de se passer du coproc' P15, d'après une thèse lyonnaise de normal sup' que j'ai trouvée : la MMU est donc initialisée à l'identité, ie&nbsp; @phys = @log ).</p> <p>Première étape : compilation du kernel Linux. Avec le .config fourni, le kernel devrait faire à peine plus d'1,3Mo. Les flags de debug sont activés, le kernel avec symboles (environ 50Mo) se trouve à la racine ./S3C2440/linux-2.6.25/vmlinux.</p> <blockquote><p>make ARCH=arm xconfig<br />make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- uImage</p> </blockquote> <p>Notez bien la ligne de commande :</p> <blockquote><p>root=/dev/nfs nfsroot=192.168.1.1:/home/gblanc/S3C2440/root_default ip=192.168.1.2:192.168.1.1::255.255.255.0:Linagora_board::none ro init=/linuxrc console=ttySAC0,115200</p> </blockquote> <p>Ici on boote en NFS en utilisant l'IP 192.168.1.2, le serveur étant à l'IP 192.168.1.1, pour le reste, configuration standard. Avec ceci, vous risquez de voir apparaître beaucoup de garbage sur le minicom, même en réglant bien convenablement les options (qui sont celles par défaut, en fait) : c'est un problème de clock pour la carte SMDK2440 (censée être compatible avec la carte Embest, je commence à penser qu'il vaut peut-être mieux créer une nouvelle archi par copie avec un nouveau numéro... D'ailleurs un "find . -name '*sbc*'" sur le kernel fourni par Embest montre qu'ils ont patché dans tous les sens), il faut la passer en 12Mhz (c'est fait convenablement pour quasiment tous les IDs de machines similaires, mais pas pour la SMDK, or notre carte Embest est en 12Mhz). Dans le fichier <a href="http://lxr.linux.no/linux+v2.6.27/arch/arm/mach-s3c2440/mach-smdk2440.c#L162">arch/arm/mach-s3c2440/mach-smdk2440.c</a>, replacer comme suit :</p> <blockquote><p>static void __init smdk2440_map_io(void)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));<br /><strong>//</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s3c24xx_init_clocks(16934400);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>s3c24xx_init_clocks(12000000);</strong><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));<br />}</p> </blockquote> <p>Ca devrait aller mieux tout à coup (on remarque aussi que s3c24xx_init_clocks appelé avec "0" met l'horloge à la valeur par défaut, qui est... 12*1000*1000). Reste le problème de la carte réseau, la CS8900. Un driver est fourni avec la carte, mais pour le noyau 2.6.13 : depuis, tellement d'eau a coulé sous les ponts qu'il n'est pas aisé de faire un portage. Et une fois fait, le nombre de bugs et autres Oops à débugger est assez scandaleux (je dirais même que l'architecture du chargement des drivers réseau a franchement changé, il faut réécrire l'init, remettre la section privée de la structure device au bon endroit, etc). En réalité, il y a un driver générique pour les cartes Cirrus qui a été écrit, depuis : CS89x0. Sauf qu'il n'est pas activé, et pire, pas activable par défaut. Il faut donc patcher le Kconfig qui va bien, et mettre les bonnes adresses de lecture/écriture de registres (sur ports I/O) et initialiser la MMU pour qu'elle pointe au bon endroit, histoire de ne pas Oopser (@phys sur 0x19000000, c'est fichtrement plus lisible dans u-boot que dans Linux pour retrouver cette adresse ! Evidemment, rien dans les doc, sinon regarder dans./include/asm-arm/arch-s3c2410/sbc2440v3-map.h du kernel 2.6.13 fourni par Embest !). Donc tout d'abord, dans drivers/net/Kconfig, aller à la section "Config CS89x0", et rajouter "|| ARCH_S3C2410" (ne faisons pas dans le détail ! Le but est de bypasser la dépendance sur NET_PCI et les cartes associées). Puis dans le code du driver, il va falloir taper sur une adresse logique qui va bien ; celle du code de la carte QT2410 (en 0xE0000300 -- il faut ajouter "300" aux adresses pour taper dans le réseau, ne me demandez pas d'où ça sort, cékomsa) est bien, mais j'ai finalement opté pour celle du vieux driver CS8900 de Embest, à l'adresse 0xD0000000, ce qui marche sur 2.6.25, mais pas sur 2.6.27, je suis donc retourné à la 0xE0000000. Dans <a href="http://lxr.linux.no/linux+v2.6.27/arch/arm/mach-s3c2440/mach-smdk2440.c#L49">arch/arm/mach-s3c2440/mach-smdk2440.c </a>(puisque l'on décide d'adapter la SMDK2440), ajouter dans le tableau smdk2440_iodesc :</p> <blockquote><p>static struct map_desc smdk2440_iodesc[] __initdata = {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* ISA IO Space map (memory space selected by A24) */<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .virtual&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = (u32)S3C24XX_VA_ISA_WORD,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .pfn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = __phys_to_pfn(S3C2410_CS2),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0x10000,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = MT_DEVICE,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }, {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .virtual&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = (u32)S3C24XX_VA_ISA_WORD + 0x10000,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .pfn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = __phys_to_pfn(S3C2410_CS2 + (1&lt;&lt;24)),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = SZ_4M,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = MT_DEVICE,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }, {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .virtual&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = (u32)S3C24XX_VA_ISA_BYTE,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .pfn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = __phys_to_pfn(S3C2410_CS2),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0x10000,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = MT_DEVICE,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }, {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .virtual&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = (u32)S3C24XX_VA_ISA_BYTE + 0x10000,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .pfn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = __phys_to_pfn(S3C2410_CS2 + (1&lt;&lt;24)),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = SZ_4M,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = MT_DEVICE,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<strong>,</strong><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>{ 0xE0000000, __phys_to_pfn(S3C2410_CS3+0x01000000), SZ_1M, MT_DEVICE }</strong><br />};</p> </blockquote> <p>Ainsi, l'adresse 0x18000000 + 0x01000000 (soit 0x19000000) sera mappée sur 0xE0000000 (avec une taille de SZ_1M, ce qui me semble un peu grand, mais c'est ce qui est "recommandé", alors...). Petit intermède à propos de ce mapping : dans la 2.6.27, on a la macro VMALLOC_END dans <a href="http://lxr.linux.no/linux+v2.6.27/arch/arm/mach-s3c2410/include/mach/vmalloc.h#L18">arch/arm/mach-s3c2410/include/mach/vmalloc.h</a> (fichier qui n'existe pour 2.6.25) qui vaut 0xE0000000. Or, dans la mise en place des zones de I/O map, on vérifie que l'on n'écrase pas une zone allouée au mallocage : fonction&nbsp; de <a href="http://lxr.linux.no/linux+v2.6.27/arch/arm/mm/mmu.c#L527">/arch/arm/mm/mmu.c</a>, et message "BUG: mapping for 0x19000000 at 0xd0000000 overlaps vmalloc space" si l'on met 0xD0000000 à la place de 0xE0000000.</p> <p>Dans le même fichier, il faut "enregistrer" le driver réseau comme faisant partie de la configuration matérielle à activer :</p> <blockquote><p>static struct platform_device *smdk2440_devices[] __initdata = {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;s3c_device_usb,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;s3c_device_lcd,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;s3c_device_wdt,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;s3c_device_i2c,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;s3c_device_iis,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>&amp;s3c_device_cs89x0,</strong><br />};</p> </blockquote> <p>Cette structure est à déclarer dans arch/arm/plat-s3c24xx/devs.c (personnellement, j'ai inséré ce code avant la partie propre au 2440 marquée par "#ifdef CONFIG_CPU_S3C2440" autour de la <a href="http://lxr.linux.no/linux+v2.6.27/arch/arm/plat-s3c24xx/devs.c#L497">ligne 500</a>) :</p> <blockquote><p>/* CS8900 */<br /><br />static struct resource s3c_cs89x0_resource[] = {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [0] = {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .start&nbsp; = 0x19000000,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .end&nbsp;&nbsp;&nbsp; = 0x19000000 + 16,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .flags&nbsp; = IORESOURCE_MEM,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; },<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [1] = {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .start&nbsp; = IRQ_EINT9,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .end&nbsp;&nbsp;&nbsp; = IRQ_EINT9,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .flags&nbsp; = IORESOURCE_IRQ,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; },<br />};<br /><br />struct platform_device s3c_device_cs89x0 = {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = "cirrus-cs89x0",<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .num_resources&nbsp; = ARRAY_SIZE(s3c_cs89x0_resource),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .resource&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = s3c_cs89x0_resource,<br />};<br /><br />EXPORT_SYMBOL(s3c_device_cs89x0);</p> </blockquote> <p>On y trouve l'IRQ qui va bien, l'@ phys à attaquer pour les registres du device aussi (Si vous obtenez en sortie console quelque chose comme "cs89x0: request_region(0xd0000300, 0x10) failed", c'est que votre mapping s'est mal passé, et le driver ne marchera évidemment pas). Pour que l'utilisation du symbole s3c_device_cs89x0 se passe bien, il faut dans <a hreflang="fr" href="http://lxr.linux.no/linux+v2.6.27/include/asm-arm/plat-s3c24xx/devs.h#L34">include/asm/plat-s3c24xx/devs.h</a> () rajouter une ligne "<strong>extern struct platform_device s3c_device_cs89x0;</strong>". A présent dans <a href="http://lxr.linux.no/linux+v2.6.27/drivers/net/cs89x0.c#L197">drivers/net/cs89x0.c</a> (le code du driver réseau), ajouter :</p> <blockquote><p>#elif defined(CONFIG_ARCH_PNX010X)<br />// blablabla<br /><strong>#elif defined(CONFIG_ARCH_S3C2410)</strong><br style="font-weight: bold;"><strong>#include &lt;asm/arch/irqs.h&gt;</strong>&nbsp; // copie de asm-arm/arch-s3c2410/irqs.h dans 2.6.25 ; attention, dans 2.6.27 se sera &lt;mach/irqs.h&gt; qui pointe vers <a href="http://lxr.linux.no/linux+v2.6.27/arch/arm/mach-s3c2410/include/mach/irqs.h#L71">arch/arm/mach-s3c2410/include/mach/irqs.h</a> ; il faut trouver où est IRQ_EINT9<br /><strong>#include &lt;linux/irq.h&gt;</strong><br style="font-weight: bold;"><strong>static unsigned int netcard_portlist [] __initdata = { 0xD0000300, 0 };</strong><br style="font-weight: bold;"><strong>static unsigned int cs8900_irq_map[] = { IRQ_EINT9, 0, 0, 0 };</strong><br style="font-weight: bold;"><strong>#define NO_EPROM</strong><br style="font-weight: bold;">#else<br />static unsigned int netcard_portlist[] __used __initdata =<br />&nbsp;&nbsp; { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};<br />static unsigned int cs8900_irq_map[] = {10,11,12,5};<br />#endif</p> </blockquote> <p>On indique donc que le port sera sur l'adresse virtuelle 0xD0000300 (c'est la base, ensuite les additions pour trouver les bons registres de lecture/écriture/contrôle sont toujours les mêmes). On indique aussi l'IRQ (ce sera la 53). Et qu'il n'y a pas d'EEPROM ; sinon il se passera des choses étranges -- de fait, il faudra entrer l'adresse mac à la main (oui, c'est très moche) :</p> <blockquote><p>printk(" IRQ %d", dev-&gt;irq);<br /><br />dev-&gt;dev_addr[0] = 0x00;<br />dev-&gt;dev_addr[1] = 0x00;<br />dev-&gt;dev_addr[2] = 0xc0;<br />dev-&gt;dev_addr[3] = 0xff;<br />dev-&gt;dev_addr[4] = 0xee;<br />dev-&gt;dev_addr[5] = 0x08;<br />set_mac_address(dev, dev-&gt;dev_addr);</p> </blockquote> <p>A mettre dans le driver, dans la fonction cs89x0_probe1&nbsp; (qui fait l'initialisation du device), vers la ligne 930 sur le 2.6.25 (ou <a href="http://lxr.linux.no/linux%20v2.6.27/drivers/net/cs89x0.c#L831">831 pour un 2.6.27</a>), après la section macroïsé par "#ifndef MONO_IRQ_MAP" et avant celle de "#if ALLOW_DMA". A présent, ça arrêtera de Oopser. On peut aussi juste au dessus (aux environs de la ligne 910, <a href="http://lxr.linux.no/linux%20v2.6.27/drivers/net/cs89x0.c#L805">805 sur 2.6.27</a>) mettre en place la bonne méthode d'enregistrement de l'IRQ dans la structure device :</p> <blockquote> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (lp-&gt;chip_type == CS8900) {<br />#if defined(CONFIG_MACH_IXDP2351) || defined(CONFIG_ARCH_IXDP2X01) || defined(CONFIG_ARCH_PNX010X)<strong> || defined(CONFIG_ARCH_S3C2410)</strong><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i = cs8900_irq_map[0];<br />#else</p> </blockquote><p>Mais à l'exécution, ça va merder sur l'IRQ, tout de même :</p> <blockquote><p>irq 53: nobody cared (try booting with the "irqpoll" option)<br />[&lt;c0027d68&gt;] (dump_stack+0x0/0x14) from [&lt;c00602a4&gt;] (__report_bad_irq+0x38/0x88)<br />[&lt;c006026c&gt;] (__report_bad_irq+0x0/0x88) from [&lt;c00605a4&gt;] (note_interrupt+0x2b0/0x308)<br />&nbsp;r4:00000000<br />[&lt;c00602f4&gt;] (note_interrupt+0x0/0x308) from [&lt;c0061380&gt;] (handle_edge_irq+0x11c/0x1a4)<br />[&lt;c0061264&gt;] (handle_edge_irq+0x0/0x1a4) from [&lt;c0032cd4&gt;] (s3c_irq_demux_extint8+0x98/0xa8)<br />&nbsp;r8:00000000 r7:00000001 r6:00000038 r5:c02c0ee8 r4:00000000<br />[&lt;c0032c3c&gt;] (s3c_irq_demux_extint8+0x0/0xa8) from [&lt;c0023044&gt;] (asm_do_IRQ+0x44/0x60)<br />[&lt;c0023000&gt;] (asm_do_IRQ+0x0/0x60) from [&lt;c00235f8&gt;] (__irq_svc+0x38/0xb0)<br />Exception stack(0xc0345e0c to 0xc0345e54)<br />5e00:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00000000 fb000000 00000001 00000000 c02c1a80<br />5e20: 40000013 00000035 c0d223e0 c016811c c0ccb000 c0ccb000 c0345e70 c0345e30<br />5e40: c0345e54 c006074c c005fda0 a0000013 ffffffff<br />&nbsp;r6:00000020 r5:f4000000 r4:ffffffff<br />[&lt;c005fc00&gt;] (setup_irq+0x0/0x230) from [&lt;c005fed4&gt;] (request_irq+0xa4/0xd0)<br />&nbsp;r7:00000000 r6:00000000 r5:00000035 r4:c0d223e0<br />[&lt;c005fe30&gt;] (request_irq+0x0/0xd0) from [&lt;c0167af8&gt;] (net_open+0xe4/0x4b8)<br />[&lt;c0167a14&gt;] (net_open+0x0/0x4b8) from [&lt;c01d7abc&gt;] (dev_open+0x84/0xdc)<br />&nbsp;r6:00001002 r5:c0ccb02c r4:c0ccb000<br />[&lt;c01d7a38&gt;] (dev_open+0x0/0xdc) from [&lt;c01d684c&gt;] (dev_change_flags+0xb0/0x178)<br />&nbsp;r5:00001003 r4:c0ccb000<br />[&lt;c01d679c&gt;] (dev_change_flags+0x0/0x178) from [&lt;c001d0a4&gt;] (ip_auto_config+0x168/0xcb8)<br />&nbsp;r7:00001002 r6:00000001 r5:c0ccb000 r4:c02f9ecc<br />[&lt;c001cf3c&gt;] (ip_auto_config+0x0/0xcb8) from [&lt;c000890c&gt;] (kernel_init+0xb8/0x278)<br />[&lt;c0008854&gt;] (kernel_init+0x0/0x278) from [&lt;c0040524&gt;] (do_exit+0x0/0x620)<br />handlers:<br />[&lt;c016811c&gt;] (net_interrupt+0x0/0x3cc)<br />Disabling IRQ #53</p> </blockquote> <p>Oui, ça fait peur. Le résultat, c'est que l'on émet bien des paquets sur l'interface réseau, mais à la réception de la réponse, l'IRQ a été désactivé faute d'avoir trouvé un handler correct, et donc le driver n'est pas notifié de l'arrivée des nouveaux paquets : c'est un dialogue de sourds :</p> <blockquote><p>eth0: Attempting TP<br />eth0: 10Base-T (RJ-45) has no cable<br />eth0: using half-duplex 10Base-T (RJ-45)<br />cs89x0: net_open() succeeded<br />IP-Config: Complete:<br />&nbsp;&nbsp;&nbsp;&nbsp; device=eth0, addr=192.168.1.2, mask=255.255.255.0, gw=255.255.255.255,<br />&nbsp;&nbsp;&nbsp;&nbsp; host=Linagora_board, domain=, nis-domain=(none),<br />&nbsp;&nbsp;&nbsp;&nbsp; bootserver=192.168.1.1, rootserver=192.168.1.1, rootpath=<br />Looking up port of RPC 100003/2 on 192.168.1.1<br />eth0: sent 42 byte packet of type 806<br />NETDEV WATCHDOG: eth0: transmit timed out<br />eth0: transmit timed out, IRQ conflict ??<br />eth0: sent 42 byte packet of type 806<br />cs89x0: Tx buffer not free!<br />NETDEV WATCHDOG: eth0: transmit timed out<br />eth0: transmit timed out, IRQ conflict ??<br />eth0: sent 42 byte packet of type 806</p> </blockquote> <p>Il va donc falloir aussi corriger cela. Après avoir bien lutté, il s'avère que c'est la "déclaration" de l'IRQ qui manque, avant son enregistrement comme correspondant au bon handler (via request_irq). Donc dans le fichier <a hreflang="fr" href="http://lxr.linux.no/linux+v2.6.27/drivers/net/cs89x0.c#L1307">./drivers/net/cs89x0.c</a>, dans la procédure net_open(), on ajoute :</p> <blockquote><p>#if !defined(CONFIG_MACH_IXDP2351) &amp;&amp; !defined(CONFIG_ARCH_IXDP2X01) &amp;&amp; !defined(CONFIG_ARCH_PNX010X)<strong> &amp;&amp; !defined(CONFIG_ARCH_S3C2410)</strong><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (((1 &lt;&lt; dev-&gt;irq) &amp; lp-&gt;irq_map) == 0) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x ",<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dev-&gt;name, dev-&gt;irq, lp-&gt;irq_map);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = -EAGAIN;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto bad_out;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />#endif<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>set_irq_type(dev-&gt;irq, IRQ_TYPE_EDGE_RISING);</strong>&nbsp;&nbsp; // IRQT_RISING dans les vieux kernels<br />/* FIXME: Cirrus' release had this: */<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ );<br />/* And 2.3.47 had this: */<br />#if 0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);<br />#endif<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; write_irq(dev, lp-&gt;chip_type, dev-&gt;irq);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = request_irq(dev-&gt;irq, &amp;net_interrupt, 0, dev-&gt;name, dev);</p> </blockquote>Et voilà le travail !&nbsp; :)&nbsp; Si vous obtenez ceci (juste avant "Looking up port of RPC 100003/2 on 192.168.1.1"), c'est que vous vous êtes planté quelque part :<br /><blockquote><p>eth0: EEPROM is configured for unavailable media<br />IP-Config: Failed to open eth0<br />IP-Config: No network devices available.</p> </blockquote><p>Par exemple, sur la 2.6.27, ce n'est pas votre faute : la macro NO_EPROM n'existe plus, résultat, c'est la cata, plus rien ne marche. C'est très bête (euphémisme). Après le commentaire "/* First check to see if an EEPROM is attached. */" (<a href="http://lxr.linux.no/linux+v2.6.27/drivers/net/cs89x0.c#L725">ligne ~730</a>), il faut donc insérer :</p> <blockquote><p>#ifdef NO_EPROM<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (1) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_NOTICE "cs89x0: No EEPROM ");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lp-&gt;adapter_cnf = A_CNF_10B_T | A_CNF_MEDIA_10B_T;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lp-&gt;auto_neg_cnf = EE_AUTO_NEG_ENABLE | IMM_BIT;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else<br />#endif</p> </blockquote>Et tout à coup, ça va beaucoup mieux :<br /><blockquote><p>eth0: 10Base-T (RJ-45) has no cable<br />eth0: using half-duplex 10Base-T (RJ-45)<br />IP-Config: Complete:<br />&nbsp;&nbsp;&nbsp;&nbsp; device=eth0, addr=192.168.1.2, mask=255.255.255.0, gw=255.255.255.255,<br />&nbsp;&nbsp;&nbsp;&nbsp; host=Linagora_board, domain=, nis-domain=(none),<br />&nbsp;&nbsp;&nbsp;&nbsp; bootserver=192.168.1.1, rootserver=192.168.1.1, rootpath=<br />Looking up port of RPC 100003/2 on 192.168.1.1</p> </blockquote> <p>Il faut bien entendu avoir un serveur NFS correctement configuré, on peut utiliser aussi wireshark pour observer les trames passer (attention au firewall et au contrôle d'accès par wilcard -- ici il n'y en a aucun, c'est la fête, heureusement qu'on est en réseau local entre ami, mais ce sont les joies de l'embarqué, *il faut* ne pas squasher root, et avoir un bon dossier /dev avec devices console, null, zero et random au minimum, en root) :</p> <blockquote><p># cat /etc/exports<br />/home/gblanc/S3C2440/Linux/rootarm/&nbsp;&nbsp;&nbsp;&nbsp; (rw,no_root_squash,async,anonuid=0,anongid=0)<br />/home/gblanc/S3C2440/root_default&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (rw,no_root_squash,async,anonuid=0,anongid=0)</p> </blockquote> <p>Je vous ai fait une archive avec les fichiers modifiés aux bons endroits pour une 2.6.27.4 (pour la 2.6.25, il faudrait trop nettoyer de choses :)&nbsp;&nbsp; ). Le voici : <a href="http://gillesblanc.com/public/gblanc.blogs.linagora.com/conf/linux-2.6.27.4-emb2440.tar.gz">linux-2.6.27.4-emb2440.tar.gz</a>.</p> <p>Seconde étape : bien brancher son câble J-TAG Amontec-à-30€, ne pas oublier de monter l'usbfs et d'exécuter openOCD en root (ou de bidouiller son udev), sinon ça démarre mal. Bonne nouvelle : on peut faire ce que l'on veut avec le câble, j'ai même trouvé des gens sur le net qui ont fait marcher le Flash programmer, mais je ne m'en suis pas servi personnellement. En revanche, la limitation pour les breakpoints/watchpoints (qui ne marchent pas super bien, en fait) est de seulement deux, donc dès que l'on veut faire du stepping sous gdb, la limite tombe à un : comment passer son temps à faire des del et des b*0x... pour changer au fur-et-à-mesure de points d'arrêts hardware, dommage. En ce qui concerne le gdb, celui de codesourcery marche convenablement, je n'ai pas compris pourquoi celui de buildroot était buggué (dans l'accès aux registres, ça retourne des erreurs ou fait n'importe quoi), j'ai seulement trouvé que le bug avait été repéré il y a quelques années par... un développeur de codesourcery sur gdb. Bref, démarrons notre OpenOCD avec ce fichier de conf :</p> <blockquote><p>interface ft2232<br />jtag_speed 0<br />jtag_ntrst_delay 100<br />jtag_nsrst_delay 100<br /><br />ft2232_vid_pid 0x0403 0xcff8<br />ft2232_layout "jtagkey"<br />ft2232_device_desc "Amontec JTAGkey"<br /><br />jtag_device 4 0x1 0xf 0xe<br /><br />#daemon_startup attach<br />target arm920t little 0 arm920t<br /><br />reset_config trst_and_srst combined<br /><br />working_area 0 0x33F00000 0x4000 nobackup<br /><br />#run_and_halt_time 0 500<br /><br />#nand device &lt;nand_controller&gt; [controller options]<br />nand device s3c2440 0<br /><br />#script<br />init<br />reset<br />halt<br />#nand probe 0<br />#arm7_9 sw_bkpts enable<br />arm7_9 force_hw_bkpts enable</p> </blockquote> <p>On remarque une partie "script" avec "init", "reset" et "halt" ; "reset" sert à redémarrer la carte automatiquement quand on lance OpenOCD (ça évite d'appuyer sur le bouton), si l'on veut se brancher à chaud sans redémarrer la carte, il suffit de le commenter (ça marche très bien) ; "halt" met le CPU sur "pause", idem on peut s'en passer si l'on ne veut pas avoir à entrer "continue" sous gdb (on retrouvera plus tard cet état de la carte avec la commande "mon poll"). Puis on lance arm-none-linux-gnueabi-gdb :</p> <blockquote><p>target remote localhost:3333<br />load ./S3C2440/linux-2.6.25/arch/arm/boot/compressed/vmlinux 0x33D50000<br />set $r0=0<br />set $r1=362<br />set $r2=0<br />set $sp=0x33D50000<br />set $pc=0x33D50000<br />set $lr=0x33D50000</p> </blockquote> <p>Le Linux compressé (vmlinux) peut être charger à l'adresse 0x33D50000, c'est calculé à la louche (sur une base d'un kernel ne dépassant pas les 2Mo et quelques) pour ne pas être en problème de réécrasement de mémoire lorsque le kernel va se décompresser à l'adresse 0x30008000, et pour éviter d'écraser aussi le u-boot en mémoire à l'adresse 0x33F80000, sait-on jamais, il pourrait resservir. Attention : ne pas charger en 0x0, ça boote mais crashe au bout d'un court moment : la mémoire mappée en 0x0 est bien de la RAM, mais c'est normalement le SteppingStone (copie des 4 premiers Kb de la flash NAND en RAM pour booter dessus, faute d'avoir de la flash NOR, cf paragraphes 6 et 5 de la documentation du S3C2440), de telle sorte qu'au bout d'un certain temps toute la mémoire après 0x1000 est remise à 0. Donc avec un uClinux, il ne faut pas utiliser load tel quel, mais bien préciser une adresse de chargement (je me suis bien fait avoir, surtout que ça avait l'air de marcher... jusqu'à ce que ça parte n'importe où en mémoire, et que ça reboote cycliquement tout seul).</p> <p>Il ne faut pas oublier de mettre les trois premiers registres dans un état que normalement le boot loader devrait gérer, et qui seront recupérés plus tard au démarrage du noyau : r0 doit être à 0 ; r1 doit contenir l'ID de la machine (on récupère ça dans arch/arm/tools/mach-types, SMDK2440 est 1008, s3c2440 est 362, a9m2440 est 698, et QT2410 est 1108, etc) ; r2 doit pointer sur la ligne de commande, le mettre à 0 revient à utiliser celle compilée en dur par l'option CMDLINE. Il n'y a plus qu'à mettre sp, pc et lr (normalement seul sp suffit, mais bon...) pour pointer sur l'adresse mémoire de chargement du noyau compressé, lancer évidemment un minicom à côté, et entrer "c" comme "continue" dans gdb : c'est parti ! Attention : parfois l'initialisation de sp/pc/lr échoue silencieusement (et parfois bruyamment : sp peut s'être transformé en r13), si l'on retombe sur le boot loader après avoir entré "c", il faut faire un ctrl-C et réaffecter les six registres ci-dessus ; ça méritera un coup de debugging du débugger un de ces quatre... (apparemment ça arrive surtout quand on appelle gdb avec un argument -- cf en dessous)</p> <p>Pour débugger votre noyau, il faut lancer gdb comme suit :</p> <blockquote><p>arm-none-linux-gnueabi-gdb ./S3C2440/linux-2.6.25/vmlinux</p> </blockquote> <p>Il ne faut pas utiliser add-symbol file : c'est malheureusement buggué, les symboles sont décalés au bout d'un certain temps (la commande "file" semble en revanche saine). Attention : ça utilisera les bonnes adresses après chargement de la MMU. On peut s'en rendre compte avec</p> <blockquote><p>arm-none-linux-gnueabi-objdump -D vmlinux | less</p> </blockquote> <p>Tout est calé sur 0xC0008000. Pour débugger Linux avant décompression, on peut en revanche faire appel à :</p> <blockquote><p>add-symbol-file ./S3C2440/linux-2.6.25-uc/arch/arm/boot/compressed/vmlinux 0x33D50000</p> </blockquote> <p>Ca chargera les symboles (ils ne sont pas bien nombreux) avec comme référence notre adresse de chargement du dessus (attention aux doublons, notamment en terme d'initialisation d'UART !). On peut ensuite mettre des break sur les symboles, faire des print, et des disassemble ; pour arrêter le kernel on peut toujours faire Ctrl-C n'importe quand, bien entendu. On peut aussi passer directement des commandes à OpenOCD :</p> <blockquote><p>(gdb) mon poll<br />target state: halted<br />target halted in ARM state due to debug request, current mode: Supervisor<br />cpsr: 0x40000053 pc: 0x33d50000<br />MMU: disabled, D-Cache: enabled, I-Cache: enabled<br />(gdb) mon reg<br />(0) r0 (/32): 0x00000000 (dirty: 1, valid: 1)<br />(1) r1 (/32): 0x0000016a (dirty: 1, valid: 1)<br />(2) r2 (/32): 0x00000000 (dirty: 1, valid: 1)<br />//etc//</p> </blockquote> <p>"mon help" permet de toutes les obtenir. On peut ainsi modifier directement les registres ("mon reg r1 0x0", par exemple), et gérer le coprocesseur P15 (mon arm920t cp15), cependant mes tentatives pour désactiver la MMU et rebooter manuellement sans avoir à réinitialiser la carte (et donc passer par un rechargement du noyau compressé, ce qui prend un peu de temps) se sont soldés par des échecs (la carte est en carafe, impossible de la mettre en état halt). Attention aussi : le "step" de gdb en assembleur n'est parfois pas terrible (il n'exécute pas une instruction), auquel cas il vaut mieux faire appel au step de OpenOCD via "mon step" ; à ce moment-là, seul "mon reg" donne un état correct des registres, "info registers" semble connaître des problèmes de cache/synchronisation lorsqu'on n'utilise pas les routines de gdb, de telles sortes que les valeurs communiquées sont obsolètes.</p> <p>Sur le noyau 2.6.27, ttySAC0 ne donne rien comme affichage (après le message de décompression du kernel, plus rien ne s'affiche sur le minicom), en fait la console n'est pas initialisée. C'est un bon moyen de voir comment on peut débugger avec notre J-TAG. Après le chargement en mémoire, et avant de lancer le kernel, on récupère l'adresse virtuelle de printk :</p> <blockquote><p>&gt; grep " printk$" System.map<br />c003deac T printk</p> </blockquote> <p>On met alors un breakpoint dessus dans gdb :</p> <blockquote><p>(gdb) b *0xc003deac<br />Breakpoint 1 at 0xc003deac<br />(gdb)</p> </blockquote> <p>Je n'utilise pas la commande file pour charger les fichiers ici : on pourrait le faire, mais en l'occurrence un bug m'en empêche, gdb segfaulte ! (il faudra vraiment débugger le débugger à un moment :)&nbsp; ) Il faut dire qu'on le pousse là dans ses derniers retranchements. Il n'y a plus qu'à lancer le kernel avec "c". Lorsqu'on arrive sur printk, voici ce que l'on peut faire :</p> <blockquote><p>(gdb) c<br />Continuing.<br /><br />Breakpoint 1, 0xc003deac in ?? ()<br />(gdb) printf "%s",$r0<br />Linux version 2.6.27.4linagora (gblanc@deepblue) (gcc version 4.2.0 20070413 (prerelease) (CodeSourcery Sourcery G++ Lite 2007q1-10)) #4 PREEMPT Wed Oct 29 19:33:38 CET 2008<br />(gdb) c<br />Continuing.<br /><br />Breakpoint 1, 0xc003deac in ?? ()<br />(gdb) printf "%s",$r0<br />CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx<br />(gdb) c<br />Continuing.<br /><br />Breakpoint 1, 0xc003deac in ?? ()<br />(gdb) printf "%s",$r0<br />Machine: %s<br />(gdb) printf "%s",$r1<br />SMDK2440(gdb) c<br />Continuing.</p> </blockquote> <p>Ceci s'affiche avant même l'initialisation de la console ! (cf la fonction register_console dans printk.c, pour ça) On peut ainsi voir où l'on en est : en l'occurrence, on trouve que les printk sautent ce qui devrait normalement s'afficher,</p> <blockquote><p>Console: colour dummy device 80x30<br />selected clock c02c9bf0 (pclk) quot 26, calc 117187<br />console [ttySAC0] enabled</p> </blockquote> <p>Le "selected clock" et le "enabled" sont absents. Voilà qui est gênant. Un breakpoint sur la fonction strcmp utilisée dans register_console pour trouver la bonne console à activer montre que "ttySAC" n'est jamais testé ! On cherche donc dans notre ancien kernel 2.6.25 comment cela marchait avant, ie où était inscrit en dur la châine de caractère "ttySAC" : on trouve une macro dans./drivers/serial/s3c2410.c&nbsp; et quelque chose dans ./drivers/serial/Kconfig : regardons dans 2.6.27, la macro n'existe plus, mais on a quelque chose dans le Kconfig. On fait un xconfig et on active : SERIAL_SAMSUNG et SERIAL_SAMSUNG_CONSOLE, ainsi que SERIAL_S3C2440, tous marqués comme "(NEW)". Tadam, ça marche !&nbsp; :)</p> <blockquote><p>Uncompressing Linux..............................................................................................<br />done, booting the kernel.<br />Linux version 2.6.27.4linagora (gblanc@deepblue) (gcc version 4.2.0 20070413 (prerelease) (CodeSourcery Sourcery G<br />++ Lite 2007q1-10)) #14 PREEMPT Thu Oct 30 15:14:11 CET 2008<br />CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177<br />Machine: SMDK2440<br />Warning: bad configuration page, trying to continue<br />Memory policy: ECC disabled, Data cache writeback<br />CPU S3C2440A (id 0x32440001)<br />S3C244X: core 405.000 MHz, memory 101.250 MHz, peripheral 50.625 MHz<br />S3C24XX Clocks, (c) 2004 Simtec Electronics<br />CLOCK: Slow mode (1.500 MHz), fast, MPLL on, UPLL on<br />CPU0: D VIVT write-back cache<br />CPU0: I cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets<br />CPU0: D cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets<br />Built 1 zonelists in Zone order, mobility grouping off.&nbsp; Total pages: 4064<br />Kernel command line: root=/dev/nfs nfsroot=192.168.1.1:/home/gblanc/S3C2440/Linux/rootarm ip=192.168.1.2:192.168.1<br />.1::255.255.255.0:Linagora_board::none rw init=/bin/sh console=ttySAC0,115200<br />irq: clearing pending ext status 00000200<br />irq: clearing subpending status 00000002<br />PID hash table entries: 64 (order: 6, 256 bytes)<br />timer tcon=00500000, tcnt a4ca, tcfg 00000200,00000000, usec 00001e57<br />Console: colour dummy device 80x30<br />console [ttySAC0] enabled<br />Dentry cache hash table entries: 2048 (order: 1, 8192 bytes)<br />Inode-cache hash table entries: 1024 (order: 0, 4096 bytes)<br />Memory: 16MB = 16MB total<br />Memory: 13100KB available (2688K code, 277K data, 112K init)<br />Calibrating delay loop... 201.93 BogoMIPS (lpj=504832)<br />Mount-cache hash table entries: 512<br />CPU: Testing write buffer coherency: ok<br />net_namespace: 288 bytes<br />NET: Registered protocol family 16<br />S3C2410 Power Management, (c) 2004 Simtec Electronics<br />S3C2440: Initialising architecture<br />S3C2440: IRQ Support<br />S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics<br />DMA channel 0 at c1800000, irq 33<br />DMA channel 1 at c1800040, irq 34<br />DMA channel 2 at c1800080, irq 35<br />DMA channel 3 at c18000c0, irq 36<br />S3C244X: Clock Support, DVS off<br />usbcore: registered new interface driver usbfs<br />usbcore: registered new interface driver hub<br />usbcore: registered new device driver usb<br />NET: Registered protocol family 2<br />IP route cache hash table entries: 1024 (order: 0, 4096 bytes)<br />TCP established hash table entries: 512 (order: 0, 4096 bytes)<br />TCP bind hash table entries: 512 (order: -1, 2048 bytes)<br />TCP: Hash tables configured (established 512 bind 512)<br />TCP reno registered<br />NET: Registered protocol family 1<br />NetWinder Floating Point Emulator V0.97 (double precision)<br />JFFS2 version 2.2. (NAND) �© 2001-2006 Red Hat, Inc.<br />msgmni has been set to 25<br />io scheduler noop registered<br />io scheduler anticipatory registered (default)<br />io scheduler deadline registered<br />io scheduler cfq registered<br />Console: switching to colour frame buffer device 30x40<br />fb0: s3c2410fb frame buffer device<br />Serial: 8250/16550 driver4 ports, IRQ sharing enabled<br />s3c2440-uart.0: s3c2410_serial0 at MMIO 0x50000000 (irq = 70) is a S3C2440<br />s3c2440-uart.1: s3c2410_serial1 at MMIO 0x50004000 (irq = 73) is a S3C2440<br />s3c2440-uart.2: s3c2410_serial2 at MMIO 0x50008000 (irq = 76) is a S3C2440<br />brd: module loaded<br />loop: module loaded<br />nbd: registered device at major 43<br />cs89x0:cs89x0_probe(0x0)<br />cs89x0.c: v2.4.3-pre1 Russell Nelson &lt;nelson@crynwr.com&gt;, Andrew Morton &lt;andrewm@uow.edu.au&gt;<br />eth0: cs8900 rev K found at 0xe0000300<br />cs89x0: No EEPROM<br />cs89x0 media RJ-45, IRQ 53eth0: Setting MAC address to c0:ff:ee:08:00:00.<br />, programmed I/O, MAC c0:ff:ee:08:00:00<br />cs89x0_probe1() successful<br />cs89x0:cs89x0_probe(0x0)<br />cs89x0: request_region(0xe0000300, 0x10) failed<br />cs89x0: no cs8900 or cs8920 detected.&nbsp; Be sure to disable PnP with SETUP<br />Uniform Multi-Platform E-IDE driver<br />S3C24XX NAND Driver, (c) 2004 Simtec Electronics<br />s3c2440-nand s3c2440-nand: Tacls=3, 29ns Twrph0=7 69ns, Twrph1=3 29ns<br />NAND device: Manufacturer ID: 0xec, Chip ID: 0x76 (Samsung NAND 64MiB 3,3V 8-bit)<br />Scanning device for bad blocks<br />Creating 8 MTD partitions on "NAND 64MiB 3,3V 8-bit":<br />0x00000000-0x00004000 : "Boot Agent"<br />0x00000000-0x00200000 : "S3C2410 flash partition 1"<br />0x00400000-0x00800000 : "S3C2410 flash partition 2"<br />0x00800000-0x00a00000 : "S3C2410 flash partition 3"<br />0x00a00000-0x00e00000 : "S3C2410 flash partition 4"<br />0x00e00000-0x01800000 : "S3C2410 flash partition 5"<br />0x01800000-0x03000000 : "S3C2410 flash partition 6"<br />0x03000000-0x04000000 : "S3C2410 flash partition 7"<br />aoe: AoE v47 initialised.<br />usbmon: debugfs is not available<br />s3c2410-ohci s3c2410-ohci: S3C24XX OHCI<br />s3c2410-ohci s3c2410-ohci: new USB bus registered, assigned bus number 1<br />s3c2410-ohci s3c2410-ohci: irq 42, io mem 0x49000000<br />usb usb1: configuration #1 chosen from 1 choice<br />hub 1-0:1.0: USB hub found<br />hub 1-0:1.0: 2 ports detected<br />mice: PS/2 mouse device common for all mice<br />S3C24XX RTC, (c) 2004,2006 Simtec Electronics<br />i2c /dev entries driver<br />s3c2440-i2c s3c2440-i2c: slave address 0x10<br />s3c2440-i2c s3c2440-i2c: bus frequency set to 98 KHz<br />s3c2440-i2c s3c2440-i2c: i2c-0: S3C I2C adapter<br />S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics<br />s3c2410-wdt s3c2410-wdt: watchdog inactive, reset disabled, irq enabled<br />Registered led device: led4<br />Registered led device: led5<br />Registered led device: led6<br />Registered led device: led7<br />Advanced Linux Sound Architecture Driver Version 1.0.17.<br />ALSA device list:<br />&nbsp; No soundcards found.<br />TCP cubic registered<br />RPC: Registered udp transport module.<br />RPC: Registered tcp transport module.<br />drivers/rtc/hctosys.c: unable to open rtc device (rtc0)<br />eth0: using half-duplex 10Base-T (RJ-45)<br />IP-Config: Complete:<br />&nbsp;&nbsp;&nbsp;&nbsp; device=eth0, addr=192.168.1.2, mask=255.255.255.0, gw=255.255.255.255,<br />&nbsp;&nbsp;&nbsp;&nbsp; host=Linagora_board, domain=, nis-domain=(none),<br />&nbsp;&nbsp;&nbsp;&nbsp; bootserver=192.168.1.1, rootserver=192.168.1.1, rootpath=<br />Looking up port of RPC 100003/2 on 192.168.1.1<br />Looking up port of RPC 100005/1 on 192.168.1.1<br />VFS: Mounted root (nfs filesystem).<br />Freeing init memory: 112K<br />sh-3.00#<br />sh-3.00#<br />sh-3.00# ls<br />bin&nbsp; etc&nbsp;&nbsp; lib&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lost+found&nbsp; opt&nbsp;&nbsp; root&nbsp; tmp&nbsp; var<br />dev&nbsp; home&nbsp; linuxrc&nbsp; mnt&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proc&nbsp; sbin&nbsp; usr<br />sh-3.00# bin<br />bin/&nbsp; bind<br />sh-3.00# bin/<br />addgroup&nbsp;&nbsp; chmod&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ed&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hostname&nbsp;&nbsp; mv&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vi<br />adduser&nbsp;&nbsp;&nbsp; chown&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; egrep&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kill&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; netstat&nbsp;&nbsp;&nbsp; stty&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zcat<br />arch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; false&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ln&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pidof&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; su&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zcmp<br />ash&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; date&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fgrep&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; login&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ping&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sync&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zdiff<br />bash&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g++&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ls&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ps&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tar&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zegrep<br />bashbug&nbsp;&nbsp;&nbsp; delgroup&nbsp;&nbsp; gcc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mkdir&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pwd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; touch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zfgrep<br />busybox&nbsp;&nbsp;&nbsp; deluser&nbsp;&nbsp;&nbsp; getopt&nbsp;&nbsp;&nbsp;&nbsp; mknod&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; true&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zforce<br />c++&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; df&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; grep&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mktemp&nbsp;&nbsp;&nbsp;&nbsp; rmdir&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; umount&nbsp;&nbsp;&nbsp;&nbsp; zgrep<br />cat&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dir&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gunzip&nbsp;&nbsp;&nbsp;&nbsp; more&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; run-parts&nbsp; uname&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zless<br />cc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dmesg&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gzexe&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mount&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sed&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; usleep&nbsp;&nbsp;&nbsp;&nbsp; zmore<br />chgrp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gzip&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mt&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sh&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vdir&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; znew<br />sh-3.00# bin/echo toto<br />toto<br />sh-3.00# ps<br />Error, do this: mount -t proc none /proc<br />sh-3.00# mount -t proc none /proc<br />sh-3.00# ps<br />&nbsp; PID TTY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TIME CMD<br />&nbsp;&nbsp;&nbsp; 1 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:01 sh<br />&nbsp;&nbsp;&nbsp; 2 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 kthreadd<br />&nbsp;&nbsp;&nbsp; 3 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 ksoftirqd/0<br />&nbsp;&nbsp;&nbsp; 4 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 watchdog/0<br />&nbsp;&nbsp;&nbsp; 5 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 events/0<br />&nbsp;&nbsp;&nbsp; 6 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 khelper<br />&nbsp;&nbsp; 64 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 kblockd/0<br />&nbsp;&nbsp; 69 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 ksuspend_usbd<br />&nbsp;&nbsp; 75 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 khubd<br />&nbsp;&nbsp; 78 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 kseriod<br />&nbsp;&nbsp; 85 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 kmmcd<br />&nbsp; 108 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 pdflush<br />&nbsp; 109 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 pdflush<br />&nbsp; 110 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 kswapd0<br />&nbsp; 111 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 aio/0<br />&nbsp; 112 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 nfsiod<br />&nbsp; 796 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 mtdblockd<br />&nbsp; 852 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 kpsmoused<br />&nbsp; 878 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 rpciod/0<br />&nbsp; 891 ?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 00:00:00 ps<br />sh-3.00#</p> </blockquote> <p>Ceci est l'ensemble des logs obtenus avec un noyau 2.6.27. On remarque que certaines choses ne marchent pas encore, comme la carte son (à moins de se planter dans le mapping mémoire, ce qui fait sortir de la carte un son extrêmement strident de ce qui s'avère être un buzzer, mais ça n'a rien à voir avec la carte son), ou la RTC (qui sur un 2.6.27, fait ramer le démarrage, alors que ça se passe sans problème sur le 2.6.25) ; le LCD n'a pas encore été testé. Ici, j'utilise un autre <a href="http://gillesblanc.com/public/gblanc.blogs.linagora.com/conf/config.emb2440-2.6.27eabi">config.emb2440-2.6.27eabi</a> avec l'EABI d'activé : ça marche ! :) Il faudra aussi penser à virer le mapping de la flash en dur (berk), et mettre quelque chose de propre avec mtdparts, par exemple (en réalité, on s'en moque, le but maintenant va être de pouvoir écrire sur la flash où l'on veut quand on veut depuis le réseau et non le J-TAG tout lent ou le u-boot tout imprédictible). A voir dans un prochain épisode, certainement (si j'ai le temps...).</p> Linux Device Driver en ligne urn:md5:aed82bbff15c6b50f814e8000f1da8ca 2008-10-29T10:32:00+01:00 gblanc linux embarqué système <p>Je ne le savais point jusqu'à hier, <a hreflang="fr" href="http://www.xml.com/ldd/chapter/book/">c'est ici</a>, et c'est sous GNU/FDL. C'est la seconde édition de Alessandro Rubini et Jonathan Corbet, juin 2001, ça date un peu, mais ça évite toujours de transporter son propre exemplaire (et pour faire des recherches, c'est plus rapide aussi :)&nbsp; ).</p>