[Kos-dev] Tty, console et klavier

Thomas Petazzoni kos-dev@enix.org
Wed, 11 Jun 2003 21:11:12 +0200


--=.yUb4F.UV?PW3wY
Content-Type: text/plain; charset=ISO-8859-15
Content-Transfer-Encoding: quoted-printable

Bonjour,

Quelques explications concernant le fonctionnement de Tty, et les
interactions avec console et klavier.

1) Interactions entre les modules

Tout d'abord, les modules klavier et console n'exportent pas
d'interfaces au sens karm. Ils ont chacun une interface sp=E9cialis=E9e qui
les lie directement =E0 tty. Ces interfaces sont d=E9finies dans
modules/tty/tty.h, sous le nom de tty_input_driver et tty_output_driver.

En revanche, chaque tty exporte une interface de type CHAR_INTERFACE,
d=E9finie dans modules/karm/interface/char.h.

Dans tty_input_driver, il n'y a qu'une fonction,
register_tty_input_handler, qui permet d'enregistrer un handler via
lequel les =E9v=E8nements d'entr=E9e (clavier) seront re=E7us.

Dans tty_output_driver, il y a plus de fonctions, permettant de cr=E9er,
supprimer une sortie, de changer de sortie (pour le switch de console),
de positionner le curseur, de changer l'etat du curseur, de changer les
attributs, de scroller, et =E9videmment d'=E9crire. Je pr=E9cise que la lis=
te
des fonctions n'a pas =E9t=E9 invent=E9e, elle sort tout droit du
fonctionnement sous le Hurd du nouveau syst=E8me de gestion des consoles
=E9crit par Marcus Brinkmann.

En bref, quand klavier re=E7oit une touche, il transmet l'=E9v=E8nement =E0=
 tty
via le handler qui a =E9t=E9 enregistr=E9, et tty prend les bonnes d=E9cisi=
ons,
et =E9ventuellement utilise le p=E9riph=E9rique de sortie pour =E9crire,
scroller, etc...

2) Fonctionnement du module klavier

Le module klavier ne fonctionne plus comme avant. Avant, il prenait un
scancode, et le convertissait directement en caract=E8res ASCII.
Maintenant, la conversion vers l'ASCII est coup=E9e en deux parties, dont
la premi=E8re se situe au niveau de klavier.=20

Les scancodes (make codes et break codes) sont soit sur 1 octet (touche
"normale"), soit sur 2 octets (touche =E9tendue avec premier scancode =E0
0xE0), soit dans un cas extreme sur 6 octets (cas de la touche Pause,
non g=E9r=E9e pour le moment). Bref, communiquer des types de donn=E9es
variables =E0 tty, c'est pas pratique : les scancodes sont donc convertis
en keycodes. Les keycodes sont stock=E9s dans un k_ui8_t (256
possibilit=E9s donc), mais en fait, seulement 128 combinaisons sont
utilis=E9es (voir klavier/klavier.h).

La conversion scancode -> keycode est tr=E8s simple :
 * si le scancode est un scancode simple entre 0x0 et 0x58, alors
keycode=3D scancode=20

* si le scancode est un scancode simple sup=E9rieur =E0 0x58, alors on
utilise la table de conversion appel=E9e regular_scancode_to_keycode,
d=E9finie au d=E9but de klavier/_klavier.c

* si le scancode est un scancode =E9tendu, on utilise la table de
conversion appel=E9e extended_scancode_to_keycode, d=E9finie elle aussi au
d=E9but de klavier/_klavier.c

L'objectif de cette conversion est donc de se ramener sur un ensemble
simple =E0 manipuler, dans lequel une touche =3D un code. C'est donc ce
keycode qu'on passe en param=E8tre au handler. En plus, on lui passe un
bool=E9en, key_up qui indique si l'=E9venement est un appui de touche
(FALSE) ou un relachement de touche (TRUE).

Enfin, la conversion scancode -> keycode, et l'appel du handler se fait
directement dans le handler d'IRQ, donc toutes les conversions ont lieu
=E0 ce moment, m=EAme les conversions intervenant dans tty. Il me semble
plus raisonnable =E0 l'avenir de laisser la conversion scancode -> keycode
(qui est simple et en O(1)) dans le handler, puis de stocker les
keycodes dans un buffer, qui seront transmis =E0 tty via un DSR.

Note: ce syst=E8me de scancode->keycode est enti=E8rement repomp=E9 sur Lin=
ux,
de m=EAme que les tables utilis=E9es.

3) Fonctionnement du module console

Le module console est nouveau, il s'occupe de g=E9rer tout ce qui est
affichage mat=E9riel sur un =E9cran texte VGA. Il est tout =E0 fait possible
de coder un autre module genre textfb, pour afficher du texte sur un
framebuffer. Du moment qu'il impl=E9mente l'interface tty_output_driver,
c'est bon !

Il n'y a pas grand chose de sp=E9cial =E0 dire sur ce module, mais je vais
d=E9crire quand m=EAme brievement ce que fait chacune des fonctions.

Je rappelle que la m=E9moire vid=E9o texte est mapp=E9e =E0 l'adresse 0xB80=
00, =E0
partir de laquelle on dispose de 8 pages =E9cran (voire 16 sur certaines
cartes ?). J'utilise pour l'instant la possibilit=E9 de switcher entre des
pages =E9crans en utilisant directement des commandes de la carte, mais je
pense revenir =E0 une solution du type memcpy, qui me semble moins voodoo,
et qui permet d'avoir autant de console qu'on veut.

La fonction create() est charg=E9e de cr=E9er une console. Si c'est la
premiere console, on sait que c'est la console KERNEL, et donc on
r=E9utilise le mapping qui a d=E9j=E0 =E9t=E9 fait par le loader, qui permet
d'=E9crire =E0 l'adresse physique 0xB8000. Pour les autres consoles, on
utilise a chaque fois une page =E9cran diff=E9rente, on alloue une page
virtuelle sans demander de mapping (dernier parametre de kvalloc =E0
FALSE), puis on le mappe =E0 la main sur l'adresse physique qui va bien.

La fonction delete() n'est pas impl=E9ment=E9e pour le moment ;-)

La fonction change_console() fait du rite voodoo avec les registres de
la carte VGA pour changer le num=E9ro du banc.

La fonction set_cursor() est cens=E9e faire le voodoo qui va bien pour
positionner le curseur, mais pour le moment, ca a pas l'air de trop
marcher ;-)

La fonction set_attribute() change l'attribut, mais de toute facon c'est
pas utilis=E9 et pas test=E9.

La fonction scroll() scrolle sur n lignes ;)

La fonction write() =E9crit au bon endroit. A noter que cette fonction ne
s'occupe pas du scrolling, elle suppose qu'elle peut =E9crire la ou elle
est. La fonction create() nous retourne la taille de la console (en
caracteres), et c'est donc tty qui s'occupe de g=E9rer ce bazar.

4) Le fonctionnement du module tty

Un tty est un p=E9riph=E9rique, qui repose sur une entr=E9e et une sortie, =
et
auquel on peut acc=E9der via une interface de type p=E9riph=E9rique caracte=
re.

Au demarrage de KOS, le module tty est appel=E9 relativement tot, et sa
fonction d'initialisation au niveau 0 est appell=E9e. Elle r=E9cup=E8re un
pointeur sur le p=E9riph=E9rique de sortie (pour l'instant c'est hardcod=E9,
mais on peut r=E9fl=E9chir plus tard =E0 un meilleur moyen), puis elle cr=
=E9e un
premier ktty. Attention, c'est un tty comme un autre, mais pour
l'instant, il est en position bancale : il n'a pas de p=E9riph=E9rique
d'entr=E9e, et son initialisation est partielle. On cr=E9e juste une console
avec create(), et comme par magie c'est la premi=E8re console, donc tout
va bien, pas besoin d'allouer quoi que ce soit. Et on peut donc avoir de
l'affichage =E0 l'=E9cran assez tot dans l'initialisation du systeme.
La fonction printk est en fait ktty_printk qui s'occupe d'afficher sur
ce tty. Quand l'initialisation de tty n'est pas termin=E9e, on utilise une
fonction perso, mais ensuite quand tout est propre, on utilise le chemin
"normal" pour afficher.

En level 3, on termine l'initialisation de tty. On r=E9cupere donc le
p=E9riph=E9rique d'entr=E9e, on appelle une fonction qui termine
l'initialisation du ktty (l'entr=E9e notamment) et qui cr=E9e plein d'autres
ttys. On cr=E9=E9 autant de tty que possible (jusqu'a ce que
console->create() =E9choue) dans la limite de MAX_NR_TTYs.

Une fois qu'un tty est cr=E9=E9, on peut utiliser les fonctions read() et
write().=20

 a) Fonctionnement de l'=E9criture

Pour le moment, c'est assez simple : tty_write() appelle _tty_putchar()
dans _vt100.c, qui se charge de l'affichage, de scroller quand il faut,
et de faire les backspaces comme il faut. Plus tard, ce fichier
contiendra un interpr=E9teur VT100. Vu que c'est assez casse-pieds =E0 faire
(grosse machine =E0 =E9tat), j'ai laiss=E9 =E7a de cot=E9 pour le moment.

 b) Fonctionnement de la lecture

Lorsqu'une touche est press=E9e (ou relach=E9e), elle passe donc par
klavier, est convertie en scancode, puis le handler tty_in_handler()
(_tty.c) est appel=E9. Il appelle keymap_handle_key() pour g=E9rer l'appui.
Cette fonction est dans _keymap.c.=20

La gestion du keymap a aussi =E9t=E9 repomp=E9e de Linux, et c'est donc tout
de suite un peu plus complexe que ce que nous avions avant. Mais =E7a
permet d'utiliser directement les tables qui sont dans defkeymap.c, et
qu'on peut g=E9nerer en utilisant l'outil 'loadkeys' avec l'option
--mktable.=20

Voici le fonctionnement g=E9n=E9ral. Dans defkeymap.c, on a plusieurs
tables, correspondant aux touches de "modification" qui sont appuy=E9es.
Un keymap quand aucune touche de modification n'est appuy=E9e, un keymap
pour Shift, pour Alt, etc... On maintient un current_keymap, qui est le
keymap courant. A la r=E9ception d'un keycode, on va directement dans le
keymap courant chercher le code qui correspond =E0 la touche. Ce code est
sur 16 bits. Les 8 bits de poids forts sont le type de la touche, qui
d=E9terminent l'action =E0 effectuer, et les 8 bits de poids faible un
param=E8tre (le code ASCII pour les touches normales, le num=E9ro de la
touche de fonction pour les touches Fx, etc...).

On r=E9cup=E8re donc les 8 bits de poids forts, auxquels on retranche 0xf0
(pour une raison inconnue .. c'est comme =E7a sous Linux), et ensuite on
appelle le handler qui correspond =E0 ce type de touche en passant en
parametre ces fameux 8 bits de poids faible.

Tous les handlers de touches sont d=E9finis dans le tableau keyhandlers,
au d=E9but du fichier. Chaque handler prend en parametre les 8 bits de
poids faible, et le bool=E9en qui indique si c'est un appui ou un
relachement de touche (ce qui est une information importante pour g=E9rer
l'appui sur Shift, Ctrl, Alt et le relachement, et donc le keymap
courant).

Certains de ces handlers effectuent des actions (changer de console,
changer le keymap courant), tandis que d'autres ajoutent des caracteres
au buffer de la console courante, en utilisant _tty_add_key.
Eventuellement, certains handlers font quelques conversions (par exemple
pour les fleches de direction ou les touches de fonctions).

Le tty_read() quand a lui se sert dans ce buffer, et bloque lorsqu'il
n'y a plus de touches dans le buffer.

5) Conclusion

Voila, je crois avoir d=E9crit de mani=E8re assez pr=E9cise le fonctionneme=
nt
de tty, de klavier et de console. Les id=E9es pour la conversion des
scancodes sont directement pomp=E9es sur Linux, comme je l'ai dit plus
haut.

6) Am=E9liorations

Globalement :
 * Faire la synchro. Ca devrait pas etre trop compliqu=E9 avec une
s=E9maphore par tty, et eventuellement de temps en temps de quoi prot=E9ger
des donn=E9es globales.

Au niveau console :
 * Gestion du curseur
 * Gestion des couleurs
 * Utilisation de memcpy() plutot que de voodoo VGA pour le changement
de console

Au niveau tty :
 * Impl=E9mentation VT100
 * Impl=E9mentation des actions manquantes pour les touches

Au niveau klavier :
 * Utilisation d'un DSR avec buffer temporaire de keycodes.

Bonne soir=E9e,

Thomas
--=20
PETAZZONI Thomas - thomas.petazzoni@enix.org - UIN : 34937744
http://www.enix.org/~thomas/
KOS: http://kos.enix.org/ - Lolut: http://lolut.utbm.info
Fingerprint : 0BE1 4CF3 CEA4 AC9D CC6E  1624 F653 CB30 98D3 F7A7

--=.yUb4F.UV?PW3wY
Content-Type: application/pgp-signature

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.2 (GNU/Linux)

iD8DBQE+537T9lPLMJjT96cRAvd+AJ9ypspAXxzV1HXVo/FhEENVUoApSQCfbIcR
04NQ6XfQAwGY3AWNWvnfVGA=
=trLN
-----END PGP SIGNATURE-----

--=.yUb4F.UV?PW3wY--