Differences

This shows you the differences between two versions of the page.

Link to this comparison view

so2:laboratoare:lab05 [2018/03/18 13:48]
anda.nicolae [Obținerea unei întreruperi]
so2:laboratoare:lab05 [2018/03/18 19:00] (current)
anda.nicolae [Controller tastatură]
Line 124: Line 124:
 După ce un driver a obținut intervalul de porturi I/O dorite, trebuie să realizeze operații de citire sau scriere pe aceste porturi. Întrucât porturile fizice sunt diferențiate după numărul de biți (8, 16 sau 32 de biți), există diferite funcții de acces a porturilor în funcție de dimensiunea lor. În ''​[[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h|asm/​io.h]]''​ sunt definite următoarele funcții de acces a porturilor: După ce un driver a obținut intervalul de porturi I/O dorite, trebuie să realizeze operații de citire sau scriere pe aceste porturi. Întrucât porturile fizice sunt diferențiate după numărul de biți (8, 16 sau 32 de biți), există diferite funcții de acces a porturilor în funcție de dimensiunea lor. În ''​[[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h|asm/​io.h]]''​ sunt definite următoarele funcții de acces a porturilor:
  
-  * ''​u8 [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L360|inb]](int port)'',​ citește porturi de dimensiune un octet (8 biți) +  * ''​u8 [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L360|inb]](unsigned long addr)'',​ citește porturi de dimensiune un octet (8 biți) 
-  * ''​void [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L384|outb]](unsigned char byteint port)'',​ scrie porturi de dimensiune un octet (8 biți) +  * ''​void [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L384|outb]](u8 valueunsigned long addr)'',​ scrie porturi de dimensiune un octet (8 biți) 
-  * ''​u16 [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L368|inw]](int port)'',​ citește porturi de dimensiune doi octeți (16 biți) +  * ''​u16 [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L368|inw]](unsigned long addr)'',​ citește porturi de dimensiune doi octeți (16 biți) 
-  * ''​void [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L392|outw]](unsigned short wordint port)'',​ scrie porturi de dimensiune doi octeți (16 biți) +  * ''​void [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L392|outw]](u16 valueunsigned long addr)'',​ scrie porturi de dimensiune doi octeți (16 biți) 
-  * ''​u32 [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L376|inl]](int port)'',​ citește porturi de dimensiune patru octeți (32 biți) +  * ''​u32 [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L376|inl]](unsigned long addr)'',​ citește porturi de dimensiune patru octeți (32 biți) 
-  * ''​void [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L400|outl]](unsigned long word, int port)'',​ scrie porturi de dimensiune patru octeți (32 biți)+  * ''​void [[https://​elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L400|outl]](u32 value, ​unsigned long addr)'',​ scrie porturi de dimensiune patru octeți (32 biți)
  
 Argumentul port specifică adresa portului de unde se citește sau se scrie, iar tipul său este dependent de platformă (poate fi ''​unsigned long''​ sau ''​unsigned short''​). Argumentul port specifică adresa portului de unde se citește sau se scrie, iar tipul său este dependent de platformă (poate fi ''​unsigned long''​ sau ''​unsigned short''​).
Line 265: Line 265:
 ==== Implementarea rutinei de tratare a întreruperii ==== ==== Implementarea rutinei de tratare a întreruperii ====
  
-Să examinăm acum [[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L92|signatura funcției de tratare a întreruperii]]:​+Să examinăm acum [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L94|signatura funcției de tratare a întreruperii]]:​
  
 <code c> <code c>
-irqreturn_t (*handler)(int ​irq_no, void *dev_id);+irqreturn_t (*handler)(int ​irq, void *dev_id);
 </​code>​ </​code>​
  
-Funcția primește ca parametri numărul întreruperii pe care rutina o tratează și pointer-ul trimis la cererea de obținere a întreruperii. Rutina de tratare a întreruperii trebuie să întoarcă o valoare cu tipul ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​irqreturn.h?v=4.9#L16|irqreturn_t]]''​. Pentru versiunea curentă de kernel, există trei valori valide: ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​irqreturn.h?v=4.9#L11|IRQ_NONE]]'',​ ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​irqreturn.h?v=4.9#L12|IRQ_HANDLED]]''​ și [[http://lxr.free-electrons.com/​source/​include/​linux/​irqreturn.h?v=4.9#L13|IRQ_WAKE_THREAD]]. Device driverul trebuie să întoarcă ''​IRQ_NONE''​ dacă observă că întreruperea nu a fost generată de dispozitivul pe care îl comandă. În caz contrar, device driverul trebuie să întoarcă ''​IRQ_HANDLED''​ dacă întreruperea poate fi tratată direct din context înterupere sau ''​IRQ_WAKE_THREAD''​ pentru a planifica rularea funcției de tratare din context proces.+Funcția primește ca parametri numărul întreruperii pe care rutina o tratează și pointer-ul trimis la cererea de obținere a întreruperii. Rutina de tratare a întreruperii trebuie să întoarcă o valoare cu tipul ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​irqreturn.h#​L17|irqreturn_t]]''​. Pentru versiunea curentă de kernel, există trei valori valide: ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​irqreturn.h#​L12|IRQ_NONE]]'',​ ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​irqreturn.h#​L13|IRQ_HANDLED]]''​ și [[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​irqreturn.h#​L14|IRQ_WAKE_THREAD]]. Device driverul trebuie să întoarcă ''​IRQ_NONE''​ dacă observă că întreruperea nu a fost generată de dispozitivul pe care îl comandă. În caz contrar, device driverul trebuie să întoarcă ''​IRQ_HANDLED''​ dacă întreruperea poate fi tratată direct din context înterupere sau ''​IRQ_WAKE_THREAD''​ pentru a planifica rularea funcției de tratare din context proces.
  
 Un handler de întrerupere va avea următoarea structură: Un handler de întrerupere va avea următoarea structură:
Line 295: Line 295:
 Deoarece rutinele de tratare pentru întreruperi se execută în [[http://​elf.cs.pub.ro/​so2/​wiki/​laboratoare/​lab03#​contexte-de-executie|context întrerupere]],​ acțiunile care se pot efectua sunt limitate: nu se poate accesa memoria din user space, nu se pot apela funcții blocante, nu se poate face sincronizare doar cu spinlock-uri deoarece acest lucru ar duce la deadlock în cazul în care spinlock-ul este deja obținut de către un proces care a fost întrerupt. Deoarece rutinele de tratare pentru întreruperi se execută în [[http://​elf.cs.pub.ro/​so2/​wiki/​laboratoare/​lab03#​contexte-de-executie|context întrerupere]],​ acțiunile care se pot efectua sunt limitate: nu se poate accesa memoria din user space, nu se pot apela funcții blocante, nu se poate face sincronizare doar cu spinlock-uri deoarece acest lucru ar duce la deadlock în cazul în care spinlock-ul este deja obținut de către un proces care a fost întrerupt.
  
-Totuși, există cazuri în care device driverele trebuie să se sincronizeze cu întreruperile. În aceste situații este necesară dezactivarea întreruperilor și folosirea spinlock-urilor. Există două modalitați de dezactivarea a întreruperilor:​ dezactivarea tututor întreruperilor,​ la nivel de procesor, sau dezactivarea la nivel de întrerupere. Dezactivarea la nivel de procesor este mai rapidă și de asemenea previne deadlock-urile mai complexe și de accea este preferată. În acest scop, există funcții de locking care dezactivează,​ respectiv reactivează întreruperile:​ ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L335|spin_lock_irqsave]]'',​ ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L360|spin_unlock_irqrestore]]'',​ ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L330|spin_lock_irq]]''​ și ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#​L355|spin_unlock_irq]]''​.+Totuși, există cazuri în care device driverele trebuie să se sincronizeze cu întreruperile. În aceste situații este necesară dezactivarea întreruperilor și folosirea spinlock-urilor. Există două modalitați de dezactivarea a întreruperilor:​ dezactivarea tututor întreruperilor,​ la nivel de procesor, sau dezactivarea la nivel de întrerupere. Dezactivarea la nivel de procesor este mai rapidă și de asemenea previne deadlock-urile mai complexe și de accea este preferată. În acest scop, există funcții de locking care dezactivează,​ respectiv reactivează întreruperile:​ ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L338|spin_lock_irqsave]]'',​ ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L363|spin_unlock_irqrestore]]'',​ ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L333|spin_lock_irq]]''​ și ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#L358#​L355|spin_unlock_irq]]''​.
  
 <code c> <code c>
Line 307: Line 307:
 </​code>​ </​code>​
  
-Funcția ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L335|spin_lock_irqsave]]''​ dezactivează întreruperile pentru procesorul local înainte de a obține spinlock-ul;​ starea anterioara a întreruperilor este salvată în ''​flags''​. În cazul în care sunteți siguri că întreruperile pe procesorul curent nu au fost deja dezactivate de altcineva (deci sunteți siguri că trebuie să activați întreruperile când eliberați spinlock-ul),​ puteți folosi funcția ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L330|spin_lock_irq]]''​.+Funcția ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L338|spin_lock_irqsave]]''​ dezactivează întreruperile pentru procesorul local înainte de a obține spinlock-ul;​ starea anterioara a întreruperilor este salvată în ''​flags''​. În cazul în care sunteți siguri că întreruperile pe procesorul curent nu au fost deja dezactivate de altcineva (deci sunteți siguri că trebuie să activați întreruperile când eliberați spinlock-ul),​ puteți folosi funcția ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L333|spin_lock_irq]]''​.
  
 Pentru spinlock-urile de tip read / write există funcții similare: ​ Pentru spinlock-urile de tip read / write există funcții similare: ​
-  * ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​rwlock.h?v=4.9#L69|read_lock_irqsave]]''​ +  * ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​rwlock.h#​L75|read_lock_irqsave]]''​ 
-  * ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​rwlock.h?v=4.9#L104|read_unlock_irqrestore]]''​ +  * ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​rwlock.h#​L110|read_unlock_irqrestore]]''​ 
-  * ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​rwlock.h?v=4.9#L95|read_lock_irq]]''​ +  * ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​rwlock.h#​L101|read_lock_irq]]''​ 
-  * ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​rwlock.h?v=4.9#L101|read_unlock_irq]]''​ +  * ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​rwlock.h#​L107|read_unlock_irq]]''​ 
-  * ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​rwlock.h?v=4.9#L74|write_lock_irqsave]]''​ +  * ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​rwlock.h#​L80|write_lock_irqsave]]''​ 
-  * ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​rwlock.h?v=4.9#​L111|write_unlock_irqrestore]]''​ +  * ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​rwlock.h#L117#​L111|write_unlock_irqrestore]]''​ 
-  * ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​rwlock.h?v=4.9#L97|write_lock_irq]]''​ +  * ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​rwlock.h#​L103|write_lock_irq]]''​ 
-  * ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​rwlock.h?v=4.9#L102|write_unlock_irq]]''​.+  * ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​rwlock.h#​L108|write_unlock_irq]]''​.
    
  
-Dacă dorim să ne sincronizăm la nivel de întrerupere (nerecomandat pentru că este dezactivarea unei întreruperi este mai lentă, nu se pot dezactiva întreruperile partajate, pot apărea deadlock-uri în situații în care lucrăm cu mai multe întreruperi) o putem face cu ajutorul funcțiilor ''​[[http://lxr.free-electrons.com/​source/​kernel/​irq/​manage.c?v=4.9#L476|disable_irq]]'',​ ''​[[http://lxr.free-electrons.com/​source/​kernel/​irq/​manage.c?v=4.9#L459|disable_irq_nosync]]''​ și ''​[[http://lxr.free-electrons.com/​source/​kernel/​irq/​manage.c?v=4.9#L543|enable_irq]]''​. Dezactivarea are loc la nivelul tuturor procesoarelor din sistem și apelurile pot să fie imbricate: dacă se apelează de două ori ''​disable_irq''​ vor fi necesare tot atâtea apeluri ''​enable_irq''​ pentru activarea ei. Diferența dintre ''​disable_irq''​ și ''​disable_irq_nosync''​ e că prima din ele va aștepta terminarea handler-elor aflate în execuție. Din această cauză ''​disable_irq_nosync''​ este în general mai rapidă.+Dacă dorim să ne sincronizăm la nivel de întrerupere (nerecomandat pentru că este dezactivarea unei întreruperi este mai lentă, nu se pot dezactiva întreruperile partajate, pot apărea deadlock-uri în situații în care lucrăm cu mai multe întreruperi) o putem face cu ajutorul funcțiilor ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​kernel/​irq/​manage.c#​L475|disable_irq]]'',​ ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​kernel/​irq/​manage.c#​L458|disable_irq_nosync]]''​ și ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​kernel/​irq/​manage.c#​L548|enable_irq]]''​. Dezactivarea are loc la nivelul tuturor procesoarelor din sistem și apelurile pot să fie imbricate: dacă se apelează de două ori ''​disable_irq''​ vor fi necesare tot atâtea apeluri ''​enable_irq''​ pentru activarea ei. Diferența dintre ''​disable_irq''​ și ''​disable_irq_nosync''​ e că prima din ele va aștepta terminarea handler-elor aflate în execuție. Din această cauză ''​disable_irq_nosync''​ este în general mai rapidă.
  
 Spre exemplu, următoarea secvență dezactivează și apoi activează întreruperea pentru portul serial ''​COM1'':​ Spre exemplu, următoarea secvență dezactivează și apoi activează întreruperea pentru portul serial ''​COM1'':​
Line 331: Line 331:
 </​code>​ </​code>​
  
-Este posibilă și dezactivarea tuturor întreruperilor pentru procesorul curent. Dezactivarea tuturor întreruperilor de către device drivere pentru sincronizare este neadecvată. Funcțiile care dezactivează / reactivează întreruperile pe procesorul local sunt ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​irqflags.h?v=4.9#L91|local_irq_disable]]''​ și ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​irqflags.h?v=4.9#L89|local_irq_enable]]''​. Nu puteți folosi aceste funcții în mod singular pentru sincronizare din cauza problemelor ce apar în sisteme multiprocesor.+Este posibilă și dezactivarea tuturor întreruperilor pentru procesorul curent. Dezactivarea tuturor întreruperilor de către device drivere pentru sincronizare este neadecvată. Funcțiile care dezactivează / reactivează întreruperile pe procesorul local sunt ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​irqflags.h#​L104|local_irq_disable]]''​ și ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​irqflags.h#​L102|local_irq_enable]]''​. Nu puteți folosi aceste funcții în mod singular pentru sincronizare din cauza problemelor ce apar în sisteme multiprocesor.
  
  
Line 372: Line 372:
 </​code>​ </​code>​
  
-Funcția ''​my_access''​ de mai sus rulează în context proces. Atunci când facem sincronizare dezactivăm întreruperile și folosim spinlock-ul ''​lock'',​ adică funcțiile ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L335|spin_lock_irqsave]]''​ și ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L360|spin_unlock_irqrestore]]''​.+Funcția ''​my_access''​ de mai sus rulează în context proces. Atunci când facem sincronizare dezactivăm întreruperile și folosim spinlock-ul ''​lock'',​ adică funcțiile ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L338|spin_lock_irqsave]]''​ și ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L363|spin_unlock_irqrestore]]''​.
  
-În rutina de tratare a întreruperii,​ folosim funcțiile ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L300|spin_lock]]''​ și ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L345|spin_unlock]]''​ pentru acces la resursa partajată.+În rutina de tratare a întreruperii,​ folosim funcțiile ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L308|spin_lock]]''​ și ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L348|spin_unlock]]''​ pentru acces la resursa partajată.
  
 <note important>​ <note important>​
-Atunci când folosim argumentul ''​flags''​ în cazul funcțiilor ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L335|spin_lock_irqsave]]''​ și ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L360|spin_unlock_irqrestore]]'',​ acesta este trimis prin valoare. Funcția ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​spinlock.h?v=4.9#L335|spin_lock_irqsave]]''​ modifică valoarea flag-ului, dar această funcție este de fapt un macro și poate folosi flag-ul trimis prin valoare.+Atunci când folosim argumentul ''​flags''​ în cazul funcțiilor ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L338|spin_lock_irqsave]]''​ și ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L363|spin_unlock_irqrestore]]'',​ acesta este trimis prin valoare. Funcția ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​spinlock.h#​L338|spin_lock_irqsave]]''​ modifică valoarea flag-ului, dar această funcție este de fapt un macro și poate folosi flag-ul trimis prin valoare.
 </​note>​ </​note>​
  
Line 427: Line 427:
 ==== Port paralel ==== ==== Port paralel ====
  
-  * [[http://​elf.cs.pub.ro/​so2/​res/​lab06/​Port_paralel.pdf|Documentație port paralel]] 
-  * [[http://​elf.cs.pub.ro/​so2/​res/​lab06/​Par_control_lin.zip|Program de test pentru portul paralel (Linux)]] 
   * [[http://​www.beyondlogic.org/​spp/​parallel.htm|Interfacing the Standard Parallel Port]]   * [[http://​www.beyondlogic.org/​spp/​parallel.htm|Interfacing the Standard Parallel Port]]
-  * [[http://​www.lvr.com/​parport.htm|Parallel Port Central]] 
-  * [[ftp://​ftp.armory.com/​pub/​user/​rstevew/​LPT/​|Resurse suplimentare port paralel]] 
  
 ==== Controller tastatură ==== ==== Controller tastatură ====
  
   * [[http://​en.wikipedia.org/​wiki/​Intel_8042|Intel 8042]]   * [[http://​en.wikipedia.org/​wiki/​Intel_8042|Intel 8042]]
-  * [[http://gunnarwrobel.de/​wiki/​Linux-and-the-keyboard.html|Linux and the keyboard]] +  * [[https://elixir.bootlin.com/linux/v4.15/source/​drivers/​input/​serio/​i8042.c|drivers/​input/​serio/​i8042.c]] 
-  * [[http://lxr.linux.no/linux+v2.6.32/​drivers/​input/​serio/​i8042.c|drivers/​input/​serio/​i8042.c]] +  * [[https://elixir.bootlin.com/linux/v4.15/source/​drivers/​input/​keyboard/​atkbd.c|drivers/​input/​keyboard/​atkbd.c]]
-  * [[http://lxr.linux.no/linux+v2.6.32/​drivers/​input/​keyboard/​atkbd.c|drivers/​input/​keyboard/​atkbd.c]]+
  
 ==== Linux ==== ==== Linux ====
so2/laboratoare/lab05.1521373724.txt.gz · Last modified: 2018/03/18 13:48 by anda.nicolae
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0