Differences

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

Link to this comparison view

so2:laboratoare:lab05 [2018/03/18 12:08]
anda.nicolae [Operații de scriere și citire a porturilor I/O]
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''​).
  
-Anumite platforme pot avea probleme atunci când procesorul încearcă să transfere date prea rapid către și de la dispozitiv. Soluția este inserarea unei întârzieri după fiecare instrucțiune de I/O, în cazul în care urmează o altă instrucțiune de același tip. În cazul în care dispozitivul pierde date, se pot folosi funcții care introduc această întârziere;​ numele acestora este similar cu cele descrise mai sus, cu deosebirea că se termină în ''​_p'':​ ''​[[http://lxr.free-electrons.com/​source/​include/​asm-generic/​io.h?v=4.9#​L408|inb_p]]'',​ ''​[[http://lxr.free-electrons.com/​source/​include/​asm-generic/​io.h?v=4.9#​L432|outb_p]]''​ etc.+Anumite platforme pot avea probleme atunci când procesorul încearcă să transfere date prea rapid către și de la dispozitiv. Soluția este inserarea unei întârzieri după fiecare instrucțiune de I/O, în cazul în care urmează o altă instrucțiune de același tip. În cazul în care dispozitivul pierde date, se pot folosi funcții care introduc această întârziere;​ numele acestora este similar cu cele descrise mai sus, cu deosebirea că se termină în ''​_p'':​ ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L408|inb_p]]'',​ ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​asm-generic/​io.h#​L432|outb_p]]''​ etc.
  
 Spre exemplu, următoarea secvență scrie un octet pe portul serial ''​COM1''​ și apoi îl citește: Spre exemplu, următoarea secvență scrie un octet pe portul serial ''​COM1''​ și apoi îl citește:
Line 174: Line 174:
 La fel ca și în cazul celorlalte resurse, un driver trebuie să obțină accesul la o linie de întreruperi înainte de a o putea utiliza și să o elibereze la finalul execuției. La fel ca și în cazul celorlalte resurse, un driver trebuie să obțină accesul la o linie de întreruperi înainte de a o putea utiliza și să o elibereze la finalul execuției.
  
-În Linux, cererea de obținere și respectiv eliberare a unei întreruperi se face cu ajutorul funcțiilor ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L143|request_irq]]''​ și ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L158|free_irq]]'':​+În Linux, cererea de obținere și respectiv eliberare a unei întreruperi se face cu ajutorul funcțiilor ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L145|request_irq]]''​ și ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L169|free_irq]]'':​
  
 <code c> <code c>
Line 181: Line 181:
 typedef irqreturn_t (*irq_handler_t)(int,​ void *); typedef irqreturn_t (*irq_handler_t)(int,​ void *);
  
-int request_irq(unsigned int irq_no, irq_handler_t handler, +int request_irq(unsigned int irq, irq_handler_t handler, 
-                unsigned long flags, const char *dev_name, void *dev_id);+                unsigned long flags, const char *name, void *dev);
  
-void free_irq(unsigned int irq_no, void *dev_id);+const void *free_irq(unsigned int irq, void *dev_id)
 </​code>​ </​code>​
  
-Se observă că pentru obținerea unei întreruperi dezvoltatorul apelează funcția ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L143|request_irq]]''​. În cadrul funcției ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L143|request_irq]]''​ trebuie să specifice **numărul întreruperii** (''​irq_no''​),​ un **handler** ce va fi chemat în momentul generării întreruperii (''​handler''​),​ **flag**-uri ce vor instrui kernelul despre comportamentul dorit (''​flags''​),​ **numele dispozitivului** ce folosește această întrerupere (''​dev_name''​),​ și un pointer ce poate fi configurat de către utilizator la orice valoare, și care nu are semnificație globală (''​dev_id''​). De cele mai multe ori, ''​dev_id''​ va fi configurat la pointerul către datele private ale dispozitivului. În schimb, la eliberarea întreruperii,​ folosind funcția ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L158|free_irq]]''​ dezvoltatorul trebuie să transmită aceeași valoare a pointerului (''​dev_id''​),​ împreună cu numărul întreruperii (''​irq_no''​). Numele dispozitivului (''​dev_name''​) este folosit pentru afișarea de statistici în ''/​proc/​interrupts''​.+Se observă că pentru obținerea unei întreruperi dezvoltatorul apelează funcția ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L145|request_irq]]''​. În cadrul funcției ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L145|request_irq]]''​ trebuie să specifice **numărul întreruperii** (''​irq''​),​ un **handler** ce va fi chemat în momentul generării întreruperii (''​handler''​),​ **flag**-uri ce vor instrui kernelul despre comportamentul dorit (''​flags''​),​ **numele dispozitivului** ce folosește această întrerupere (''​name''​),​ și un pointer ce poate fi configurat de către utilizator la orice valoare, și care nu are semnificație globală (''​dev''​). De cele mai multe ori, ''​dev''​ va fi configurat la pointerul către datele private ale dispozitivului. În schimb, la eliberarea întreruperii,​ folosind funcția ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L169|free_irq]]''​ dezvoltatorul trebuie să transmită aceeași valoare a pointerului (''​dev''​),​ împreună cu numărul întreruperii (''​irq''​). Numele dispozitivului (''​name''​) este folosit pentru afișarea de statistici în ''/​proc/​interrupts''​.
  
-Valoarea pe care o întoarce ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L143|request_irq]]''​ este 0 în cazul în care înregistrarea s-a efectuat cu succes sau un cod de eroare negativ care indică motivul eșecului. O valoare uzuală este ''​-EBUSY''​ care este întoarsă atunci când întreruperea este ocupată deja de un alt echipament.+Valoarea pe care o întoarce ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L145|request_irq]]''​ este 0 în cazul în care înregistrarea s-a efectuat cu succes sau un cod de eroare negativ care indică motivul eșecului. O valoare uzuală este ''​-EBUSY''​ care este întoarsă atunci când întreruperea este ocupată deja de un alt echipament.
  
-Există situații în care deși un dispozitiv folosește întreruperi nu putem citi regiștrii dispozitivului într-un mod non-blocant (de exemplu un senzor conectat la un bus I2C sau SPI al cărui driver nu garantează că operațiile de read / write pe bus sunt non-blocante). În această situație în întrerupere trebuie să planificăm o acțiune amânabilă ce rulează în context proces (work queue, kernel thread) pentru a putea accesa regiștrii dispozitivului. Pentru că o astfel de situație este relativ comună, kernel-ul pune la dispoziție funcția ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L138|request_threaded_irq]]''​ pentru întregistra rutine de tratare a întreruperilor ce rulează în două faze: o fază în context proces și o fază în context întrerupere:​+Există situații în care deși un dispozitiv folosește întreruperi nu putem citi regiștrii dispozitivului într-un mod non-blocant (de exemplu un senzor conectat la un bus I2C sau SPI al cărui driver nu garantează că operațiile de read / write pe bus sunt non-blocante). În această situație în întrerupere trebuie să planificăm o acțiune amânabilă ce rulează în context proces (work queue, kernel thread) pentru a putea accesa regiștrii dispozitivului. Pentru că o astfel de situație este relativ comună, kernel-ul pune la dispoziție funcția ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L140|request_threaded_irq]]''​ pentru întregistra rutine de tratare a întreruperilor ce rulează în două faze: o fază în context proces și o fază în context întrerupere:​
  
 <code c> <code c>
Line 204: Line 204:
  
  
-[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L38|Flag-urile]] ce pot fi transmise la obținerea unei întreruperi sunt: +[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L40|Flag-urile]] ce pot fi transmise la obținerea unei întreruperi sunt: 
-  *''​[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L65|IRQF_SHARED]]''​ anunță kernelul că întreruperea poate fi partajată cu alte dispozitive. Dacă acest flag nu este setat, atunci, dacă există deja un handler asociat cu întreruperea cerută, cererea de obținere a unei întreruperi va eșua. O întrerupere partajată este tratată prin execuția tuturor rutinelor înregistrate. Această abordare duce la o situație interesantă:​ cum își poate da seama un device driver dacă rutina de tratare a întreruperii a fost activată de o întrerupere generată de dispozitivul pe care îl gestionează?​ Toate dispozitivele care oferă suport pentru întreruperi au asociate un registru de stare, care poate fi interogat în rutina de tratare pentru a afla dacă întreruperea a fost sau nu generată de dispozitiv (în cazul portului serial, acest registru de stare este ''​IIR''​ - //Interrupt Information Register//​). La cererea unei întreruperi partajate cu ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L143|request_irq]]'',​ argumentul ''​dev_id''​ trebuie să fie unic în kernel; poate fi setat la un pointer către datele private ale modulului, dar nu poate fi ''​NULL''​. +  *''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L86|IRQF_SHARED]]''​ anunță kernelul că întreruperea poate fi partajată cu alte dispozitive. Dacă acest flag nu este setat, atunci, dacă există deja un handler asociat cu întreruperea cerută, cererea de obținere a unei întreruperi va eșua. O întrerupere partajată este tratată prin execuția tuturor rutinelor înregistrate. Această abordare duce la o situație interesantă:​ cum își poate da seama un device driver dacă rutina de tratare a întreruperii a fost activată de o întrerupere generată de dispozitivul pe care îl gestionează?​ Toate dispozitivele care oferă suport pentru întreruperi au asociate un registru de stare, care poate fi interogat în rutina de tratare pentru a afla dacă întreruperea a fost sau nu generată de dispozitiv (în cazul portului serial, acest registru de stare este ''​IIR''​ - //Interrupt Information Register//​). La cererea unei întreruperi partajate cu ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L145|request_irq]]'',​ argumentul ''​dev''​ trebuie să fie unic în kernel; poate fi setat la un pointer către datele private ale modulului, dar nu poate fi ''​NULL''​. 
-  *''​[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L71|IRQF_ONESHOT]]''​ întreruperea va fi reactivată ​ după rularea rutinei de tratare din context proces; fără acest flag, întreruperea va fi reactivată dupa rularea rutinei de tratare din context întrerupere ​+  *''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L73|IRQF_ONESHOT]]''​ întreruperea va fi reactivată ​ după rularea rutinei de tratare din context proces; fără acest flag, întreruperea va fi reactivată dupa rularea rutinei de tratare din context întrerupere.
  
 Obținerea întreruperii se poate realiza fie la inițializarea driver-ului,​ în funcția ''​init_module'',​ fie atunci când dispozitivul este deschis prima dată, în funcția ''​open''​. Obținerea întreruperii se poate realiza fie la inițializarea driver-ului,​ în funcția ''​init_module'',​ fie atunci când dispozitivul este deschis prima dată, în funcția ''​open''​.
Line 238: Line 238:
  
 <note warning> <note warning>
-La cererea ​ unei întreruperi partajate (''​IRQF_SHARED''​) cu ''​[[http://lxr.free-electrons.com/​source/​include/​linux/​interrupt.h?v=4.9#L143|request_irq]]'',​ argumentul ''​dev_id''​ **nu** poate fi ''​NULL''​.+La cererea ​ unei întreruperi partajate (''​IRQF_SHARED''​) cu ''​[[https://elixir.bootlin.com/​linux/​v4.15/​source/​include/​linux/​interrupt.h#​L145|request_irq]]'',​ argumentul ''​dev''​ **nu** poate fi ''​NULL''​.
 </​note>​ </​note>​
  
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.1521367709.txt.gz · Last modified: 2018/03/18 12:08 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