This shows you the differences between two versions of the page.
so2:laboratoare:lab12:exercitii [2017/05/15 17:32] octavian.purdila [3. [2p] Atribute device drivere] |
so2:laboratoare:lab12:exercitii [2019/05/20 14:32] (current) razvan.deaconescu [1. [2p] Implementare bus] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Laborator 12: Exerciții ====== | ====== Laborator 12: Exerciții ====== | ||
- | Pentru desfășurarea laboratorului pornim de la [[http://elf.cs.pub.ro/so2/res/laboratoare/lab12-tasks.zip|arhiva de sarcini a laboratorului]]. Descărcăm și decomprimăm arhiva în directorul ''so2/'' din directorul home al utilizatorului ''student'' de pe sistemul de bază (stația ''mjolnir''):<code bash> | + | ===== Pregătirea laboratorului ===== |
- | student@mjolnir:~$ cd so2/ | + | |
- | student@mjolnir:~/so2$ wget http://elf.cs.pub.ro/so2/res/laboratoare/lab12-tasks.zip | + | |
- | student@mjolnir:~/so2$ unzip lab12-tasks.zip | + | |
- | student@mjolnir:~/so2$ tree lab12-tasks | + | |
- | </code> | + | |
- | În cadrul directorului ''lab12-tasks/'' se găsesc resursele necesare pentru dezvoltarea exercițiilor de mai jos: fișiere schelet de cod sursă, fișiere Makefile și Kbuild, scripturi și programe de test. | + | |
- | Vom dezvolta exercițiile pe sistemul de bază (stația ''mjolnir'') și apoi le vom testa pe [[:so2:resurse:masini-virtuale|mașina virtuală QEMU]]. După editarea și compilarea unui modul de kernel îl vom copia în directorul dedicat pentru mașina virtuală QEMU folosind o comandă de forma<code bash> | + | Pentru rezolvarea laboratorului, vom lucra în același director din care pornim mașina virtuală (''~/so2/linux/tools/labs''). |
- | student@mjolnir:~/so2$ cp /path/to/module.ko ~/so2/qemu-so2/fsimg/root/modules/ | + | |
- | </code> unde ''/path/to/module.ko'' este calea către fișierul obiect aferent modulului de kernel. Apoi vom porni, din directorul ''~/so2/qemu-so2/'' mașina virtuală QEMU folosind comanda<code bash> | + | |
- | student@mjolnir:~/so2/qemu-so2$ make | + | |
- | </code> | + | |
- | După pornirea mașinii virtuale QEMU vom putea folosi comenzi în fereastra QEMU pentru a încărca și descărca modulul de kernel:<code> | + | Pașii de rezolvare sunt următorii: |
- | # insmod modules/module-name.ko | + | * pregătirea scheletului de laborator |
- | # rmmod module/module-name | + | * compilarea modulelor de Kernel |
- | </code> unde ''module-name'' este numele modulului de kernel. | + | * copierea modulelor pe mașina virtuală |
+ | * pornirea mașinii virtuale și testarea modulelor | ||
- | <note> | + | ==== Pregătirea scheletului de laborator ==== |
- | Pentru dezvoltarea laboratorului, este recomandat să folosim trei terminale sau, mai bine, trei tab-uri de terminal. Pentru a deschide un nou tab de terminal folosim combinația de taste ''Ctrl+Alt+t''. Cele trei tab-uri de terminal îndeplinesc următoarele roluri: | + | |
- | - În primul tab de terminal dezvoltăm modulul de kernel: editare, compilare, copiere în directorul dedicat pentru mașina virtaulă QEMU. Lucrăm în directorul aferent rezultat în urma decomprimării arhivei de sarcini a laboratorului. | + | |
- | - În al doilea tab de terminal pornim mașina virtuală QEMU și apoi testăm modulul de kernel: încărcare/descărcare modul, rulare teste. Lucrăm în directorul aferent mașinii virtuale: ''~/so2/qemu-so2/''. | + | |
- | - În al treilea tab de terminal pornim [[:so2:laboratoare:lab02#minicom|minicom]] sau un server UDP care să primească [[:so2:laboratoare:lab02#netconsole|mesajele de netconsole]]. Nu contează în ce director ne aflăm. Folosim comanda<code bash> | + | |
- | student@mjolnir:~$ netcat -lup 6666 | + | |
- | </code> | + | |
- | </note> | + | |
- | ===== [0.5p] Intro ===== | + | Scheletul de laborator este generat din sursele din directorul ''tools/labs/templates''. Putem genera scheletele pentru toate laboratoarele folosind următoarea comanda: |
- | <note warning> | + | <code bash> |
- | Bonus - 1 punct: Vă invităm să evaluați activitatea echipei de SO2 și să precizați punctele tari și punctele slabe și sugestiile voastre de îmbunătățire a materiei. Feedback-ul vostru este foarte important pentru noi să creștem calitatea materiei în anii următori și să îmbunătățim materiile pe care le veți face în continuare. | + | tools/labs $ make skels |
- | Găsiți formularul de feedback în partea dreaptă a paginii principale de SO2 de pe cs.curs.pub.ro într-un frame numit “FEEDBACK” | + | </code> |
- | </note> | + | |
- | Vă mulțumim! | + | Pentru a genera scheletul pentru un singur laborator, vom folosi variabila de mediu ''LABS'': |
- | Găsiți definițiile următoarelor simboluri în nucleul Linux: | + | |
- | * funcțiile ''dev_name'', ''dev_set_name''. | + | |
- | * funcțiile ''pnp_device_probe'', ''pnp_bus_match'', ''pnp_register_driver'' și variabila ''pnp_bus_type''. | + | |
- | ===== [1p] BONUS: Completare formular de feedback ===== | + | <code bash> |
+ | tools/labs $ make clean | ||
+ | tools/labs $ LABS=<lab name> make skels | ||
+ | </code> | ||
- | Apreciem opinia voastră legată de activitățile cursului de SO2. Ne ajută să îmbunătățim cursul și să facem materia cât mai accesibilă și interesantă. Pentru această vă rugăm să completați [[http://cs.curs.pub.ro/2016/blocks/feedbackacs/view.php?courseid=150&blockid=3269|formularul de feedback de pe cs.curs.pub.ro]] (trebuie să fiți autentificați și înrolați în cadrul cursului). Vă mulțumim! | + | <note important> |
- | ===== [6.5p] Linux Device Model ===== | + | Numele laboratorului curent este ''device_model''. |
+ | </note> | ||
- | În prima parte ne propunem să ne familiarizăm cu modelul ''Linux Device Model'' și reflectarea acestuia în userspace prin intermediul sistemului virtual de fișiere ''sysfs''. Vom analiza implementarea magistralelor și modul în care device driverele se conectează la o magistrală. Pentru început, vom lucra cu o magistrală virtuală, definită în modulul ''mybus.ko'' din subdirectorul ''virtual_bus/mybus/'' la care vom atașa driverul ''mydriver.ko'' din subdirectorul ''virtual_bus/mydriver''. | + | Similar, putem genera și scheletul pentru un singur exercițiu, atribuind valoarea ''<lab_name>/<task_name>'' variabilei ''LABS''. |
- | <note tip> | + | <note> |
- | În rezolvarea acestui exerciţiu vă veţi întâlni cu: | + | Scheletul este generat în directorul ''tools/labs/skels''. |
- | * ''my_bus_type''(mybus.c): variabilă globală pentru tipul de magistrală | + | |
- | * ''my_bus_device''(mybus.c): variabilă globală pentru dispozitivul magistralei | + | |
- | * ''my_device''(virtual_bus.h): structură folosită pentru dispozitivele ce se conectează la magistrală | + | |
- | * ''my_driver''(virtual_bus.h): structură folosită pentru driverul care lucrează cu dispozitivele ce se conectează la magistrală | + | |
- | * ''dev_data''(mydriver.c): variabilă globală ce menţine datele necesare driverului | + | |
</note> | </note> | ||
- | ==== 1. [1p] Implementare magistrală ==== | ||
- | Intrați în directorul ''virtual_bus/mybus'', unde găsiți implementarea unei magistrale ''mybus'', așa cum este descrisă în laborator. Analizați conținutul fișierelor ''mybus/mybus.c'' și ''include/virtual_bus.h''. | + | ==== Compilarea modulelor ==== |
- | Observaţi că la încărcarea modulului se înregistrează atât o structură [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L109|bus_type]], reprezentând tipul de magistrală, cât și o structură [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L782|device]], reprezentând dispozitivul efectiv al magistralei. | + | |
- | Compilați și inserați modulul. | + | Comanda ''make build'' compilează toate modulele din directorul ''skels''. |
- | Verificaţi că tipul de magistrală apare în /sys/bus, iar dispozitivul în /sys/devices. | + | |
- | Scoateți modulul şi observaţi că intrările din sysfs sunt înlăturate. | + | |
- | Modificați sursa astfel încât intrările pentru magistrală și dispozitivul asociat să fie ''virtualbus'', respectiv ''virtualbus0''. | + | <code bash> |
- | + | student@eg106:~/so2/linux/tools/labs$ make build | |
- | <note tip> | + | echo "# autogenerated, do not edit " > skels/Kbuild |
- | Recitiți secțiunile [[:so2:laboratoare:lab12#magistrale|Magistrale]] și [[:so2:laboratoare:lab12#dispozitive|Dispozitive]] din laborator. | + | echo "ccflags-y += -Wno-unused-function -Wno-unused-label -Wno-unused-variable " >> skels/Kbuild |
- | </note> | + | for i in ./device_model; do echo "obj-m += $i/" >> skels/Kbuild; done |
- | + | ... | |
- | Pentru verificare trebuie să se fi creat pe mașina virtuală în ''/sys'' intrările aferente magistralei (''virtualbus'') și dispozitivului părinte (''virtualbus0''):<code> | + | |
- | # ls /sys/bus/virtualbus/ | + | |
- | # ls /sys/devices/virtualbus0/ | + | |
</code> | </code> | ||
- | ==== 2. [2p] Conectare driver la magistrală ==== | + | ==== Copierea modulelor pe mașina virtuală ==== |
- | Intrați în directorul ''virtual_bus/mydriver'', unde găsiți implementarea unui device driver de tip caracter. | + | Putem copia modulele generate pe mașina virtuală folosind target-ul ''copy'' al comenzii make, atunci când mașina virtuală este oprită. |
- | Modificați sursa astfel încât să respecte modelul ''Linux Device Model''. Dispozitivul ''echo'' se va conecta la magistrala virtualbus de la exercițiul anterior, având un driver asociat ''echo''. Numele driverului și al dispozitivului trebuie să fie identice. | + | <code bash> |
+ | student@eg106:~/so2/linux/tools/labs$ make copy | ||
+ | student@eg106:~/so2/linux/tools/labs$ make boot | ||
+ | </code> | ||
- | Urmăriți comentariile ''TODO 2'' din cod și exemplele de înregistrare dispozitiv/driver din laborator: [[:so2:laboratoare:lab12#dispozitive|înregistrare dispozitiv]], [[:so2:laboratoare:lab12#drivere|înregistrare driver]]. | + | Alternativ, putem copia fișierele prin ''scp'', pentru e evita repornirea mașinii virtuale. Pentru detalii despre folosirea interacțiunea prin rețea cu mașina virtuală citiți [[https://ocw.cs.pub.ro/courses/so2/resurse/masini-virtuale#interactiunea_cu_masina_virtuala|Interacțiunea cu mașina virtuală]]. |
- | Pentru a vă conecta la magistrala ''virtualbus'', va trebui să folosiți funcțiile și tipurile de driver/device exportate de aceasta. | + | ==== Testarea modulelor ==== |
- | În structura ''struct my_device_data'' adăugați un câmp de tipul ''struct my_device'' (tip de date definit în ''include/virtual_bus.h''). Câmpurile structurii ''my_device'' vor fi inițializate în funcția ''my_init''. | + | Modulele generate sunt copiate pe mașina virtuală în directorul ''/home/root/skels/<lab_name>''. |
- | Definiți o variabilă de tipul ''struct my_driver'' şi inițializați pentru aceasta modulul şi numele driver-ului. Înregistraţi driverul în funcţia ''my_init''. | + | <code bash> |
+ | root@qemux86:~/skels/device_model# ls | ||
+ | bex.ko bex_misc.ko | ||
+ | </code> | ||
- | <note tip> | + | După pornirea mașinii virtuale QEMU vom putea folosi comenzi în fereastra QEMU (sau în ''minicom'') pentru a încărca și descărca modulul de kernel:<code> |
- | Parcurgeți secțiunea [[:so2:laboratoare:lab12#drivere|Drivere]] din laborator. Urmăriți exemplele de cod. | + | root@qemux86:~# insmod skels/<lab_name>/<task_name>/<module_name>.ko |
- | </note> | + | root@qemux86:~# rmmod skels/<lab_name>/<task_name>/<module_name>.ko |
+ | </code> | ||
- | <note tip> | + | <note> |
- | Trebuie să înregistrați / deînregistrați variabilele de tipul ''struct my_device'' și ''struct my_driver'' în funcția de init / exit a modulului. Pentru aceasta folosiți funcțiile ''my_register_driver'' / ''my_unregister_driver'', respectiv ''my_register_device'' / ''my_unregister_device'', definite în fișierul ''mybus.c''. | + | Pentru dezvoltarea laboratorului, este recomandat să folosim trei terminale sau, mai bine, trei tab-uri de terminal. Pentru a deschide un nou tab de terminal folosim combinația de taste ''Ctrl+Shift+t''. Cele trei tab-uri de terminal îndeplinesc următoarele roluri: |
- | </note> | + | - În primul tab de terminal dezvoltăm modulul de kernel: editare, compilare, copiere în directorul dedicat pentru mașina virtuală QEMU. Lucrăm în directorul aferent rezultat în urma decomprimării arhivei de sarcini a laboratorului. |
+ | - În al doilea tab de terminal pornim mașina virtuală QEMU și apoi testăm modulul de kernel: încărcare/descărcare modul, rulare teste. Lucrăm în directorul aferent mașinii virtuale: ''~/so2/linux/tools/labs''. | ||
+ | - În al treilea tab de terminal accesăm directorul ''~/so2/linux/'' cu sursele nucleului unde putem folosi [[:so2:laboratoare:lab01#cscope|Vim și cscope]] pentru parcurgerea codului sursă.<code> | ||
+ | student@eg106-pc:~$ netcat -lup 6666 | ||
+ | </code> | ||
- | <note tip> | ||
- | Pentru a transmite primul argument (magistrala) funcției ''bus_find_device'' folosiți o expresie de forma ''mydriver.driver.bus''. | ||
</note> | </note> | ||
- | În cadrul funcţiei ''my_init'', iniţializaţi dispozitivul şi înregistraţi-l. Vor trebui completate cel puțin câmpurile ''name'' și ''driver''. | + | ===== Exerciții ===== |
- | De asemenea, puteți stoca un back pointer către ''struct my_device_data'' în câmpul ''%%dev->p->driver_data%%''. Acest câmp e util pentru a putea accesa datele private (''my_device_data'') ale dispozitivului și din locurile în care aveți acces doar la structura generică [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L782|struct device]]. Fiind un câmp din [[http://elixir.free-electrons.com/linux/v3.13/source/drivers/base/base.h#L73|datele private]] ale dispozitivului, pentru a accesa ''%%dev->p->driver_data%%'' e recomandat să folosiți funcțiile (interfața) [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L920|dev_set_drvdata]], [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L915|dev_get_drvdata]]. | + | <note important> |
- | + | Înainte de începerea rezolvării laboratorului, rulați comanda ''%%git pull --rebase%%'' in directorul ''~/so2/linux'', pentru a obține ultima versiune a scheletului de laborator. | |
- | <note tip> | + | |
- | Parcurgeți secțiunea [[:so2:laboratoare:lab12#dispozitive|Dispozitive]] din laborator. Urmăriți exemplele de cod. | + | |
</note> | </note> | ||
- | Compilaţi modulul şi copiaţi-l pe maşina virtuală, împreună cu modulul de la exerciţiul anterior. | + | ===== [0.5p] Intro ===== |
<note warning> | <note warning> | ||
- | Pentru a elimina warning-urile legate de nedefinirea funcțiilor de înregistrare/deînregistrare de dispozitiv, compilați ambele module din directorul părinte. Aveți în vedere să inserați modulul de la pasul anterior (''mybus.ko'') înaintea inserării modulului curent. | + | Bonus - 1 punct: Vă invităm să evaluați activitatea echipei de SO2 și să precizați punctele tari și punctele slabe și sugestiile voastre de îmbunătățire a materiei. Feedback-ul vostru este foarte important pentru noi să creștem calitatea materiei în anii următori și să îmbunătățim materiile pe care le veți face în continuare. |
+ | Găsiți formularul de feedback în partea dreaptă a paginii principale de SO2 de pe cs.curs.pub.ro într-un frame numit “FEEDBACK” | ||
</note> | </note> | ||
- | Pentru testare urmăriți intrările de dispozitiv (''echo'') și de driver (''echo'') în ''/sys'' pe mașina virtuală: | + | Vă mulțumim! |
- | <code> | + | Găsiți definițiile următoarelor simboluri în nucleul Linux: |
- | # ls /sys/bus/virtualbus/devices/ | + | * funcțiile ''dev_name'', ''dev_set_name''. |
- | # ls /sys/bus/virtualbus/drivers/ | + | * funcțiile ''pnp_device_probe'', ''pnp_bus_match'', ''pnp_register_driver'' și variabila ''pnp_bus_type''. |
- | # ls /sys/devices/virtualbus0/ | + | |
- | </code> | + | |
- | ==== 2.1. [1.5p] Informații device-uri de pe magistrală ==== | + | ===== Completare formular de feedback ===== |
- | La sfârșitul inițilizării modulului ''mydriver'' (TODO 2.1), verificați dacă device-ul a fost atașat la magistrală. Pornind de la magistrala asociată, parcurgeți device-urile atașate și căutați-l pe cel cu numele ''echo''. Afișați numele device-ului, dacă acesta a fost găsit. | + | Apreciem opinia voastră legată de activitățile cursului de SO2. Ne ajută să îmbunătățim cursul și să facem materia cât mai accesibilă și interesantă. Pentru această vă rugăm să completați [[https://acs.curs.pub.ro/2018/course/view.php?id=295|formularul de feedback de pe cs.curs.pub.ro]] (trebuie să fiți autentificați și înrolați în cadrul cursului). Vă mulțumim! |
- | <note tip> | + | ==== 1. [2p] Implementare bus ==== |
- | Pentru căutarea unui dispozitiv folosiți funcția [[http://elixir.free-electrons.com/linux/v4.9/source/drivers/base/bus.c#L371|bus_find_device_by_name]]. Transmiteți ''NULL'' ca al doilea argument al funcției. | + | |
- | Obțineți structura de tip magistrală (''struct bus_type'') printr-o construcție de forma ''my_driver.driver.bus''. | + | Analizați conținutul fișierului ''bex.c'', care conține implementarea unui driver de magistrală. Urmăriți comentariile marcate cu ''TODO 1'' pentru a implementa părțile ce lipsesc: înregistrați driverul de magistrală și adăugați un nou device, numit ''root'', cu tipul ''none'' și versiunea ''1''. |
- | Numele device-ului întors de funcția [[http://elixir.free-electrons.com/linux/v4.9/source/drivers/base/bus.c#L371|bus_find_device_by_name]] se poate obține cu funcția [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L868|dev_name]]. | + | <note tip> |
+ | Aruncați o privire pe implementarea funcției ''bex_add_dev()''. | ||
</note> | </note> | ||
- | Pentru verificare la inserarea modulului se va afișa mesaj cu numele dispozitivului. | + | <note tip> |
+ | Înregistrarea și deînregistrarea o faceți folosind respectiv funcțiile ''bus_register()'' și ''bus_unregister()''. | ||
+ | </note> | ||
- | ==== 3. [2p] Atribute device drivere ==== | + | Compilați, copiați modulul pe mașina virtuală, încărcați-l în kernel și verificați faptul că dispozitivul de tip magistrală este vizibil în ''/sys/bus''. De asemenea, verificați că dispozitivul este vizibil în ''/sys/bus/bex/devices.''. |
- | Extindeți driverul echo de la exercițiul anterior prin adăugarea unui atribut ''myattr'' pentru dispozitivul creat, ce va conține majorul și minorul dispozitivului (''major:minor''). Acest atribut va fi expus prin interfaţa ''sysfs'' din directorul device-ului ''echo''. | + | Eliminați modulul și observați că intrările din ''sysfs'' sunt eliminate. |
- | Urmăriți comentariile ''TODO 3'' din cod. | + | ==== 2. [2p] Adăugare atribute pentru tip și versiune ==== |
+ | |||
+ | Adăugați două atribute read-only: ''type'' și ''version''. Urmăriți comentariile marcate cu ''TODO 2''. | ||
<note tip> | <note tip> | ||
- | Va trebui să declarați și să înregistrați o structură [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L549| device_attribute]]. Puteți folosi macro-ul [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L575|DEVICE_ATTR]] ce va crea o structură cu numele ''dev_attr_##_name'', unde ''##_name'' este numele atributului. | + | Va trebui să adăugați cele două atribute ''type'' și ''version'' în structura ''bex_dev_attrs'' pe modelul: |
- | Pentru folosirea macro-ului va trebui să specificaţi, în această ordine: | + | <code> |
- | * numele atributului | + | &dev_attr_<insert-attribute-type-here>.attr, |
- | * permisiunile la acces la intrarea din sysfs aferentă atributului; folosiţi valoarea ''0444''. | + | </code> |
- | * o funcție [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L551|show]] ce afișează valoarea atributului pornind de la informațiile din [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L782|struct device]]. | + | </note> |
- | * o funcţie [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L553|store]], care în cazul vostru poate fi ''NULL''. | + | |
- | În funcţia [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L303|show]], puteți folosi macrodefinițiile [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/kdev_t.h#L9|MAJOR]] și [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/kdev_t.h#L9|MINOR]] pentru aflarea majorului și minorului. Aceste funcții primesc ca argument câmpul ''dev'' al structurii ''struct cdev''. Câmpul de tipul ''struct cdev'' îl găsiți în structura de tipul ''struct my_device_data''. Pentru a obține structura de tipul ''struct my_device_data'', atunci când știți adresa unei variabile de tipul ''struct device'', puteți folosi funcția [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L915|dev_get_drvdata]]. | + | Observați că cele două atribute noi sunt vizibile în ''/sys/bus/bex/devices/root''. Verificați conținutul acestor atribute. |
- | Țineți cont să adăugați/eliminați atributul la inițializarea/dezactivarea modulului cu ajutorul funcțiilor [[http://elixir.free-electrons.com/linux/v4.9/source/drivers/base/core.c#L587|device_create_file]], [[http://elixir.free-electrons.com/linux/v4.9/source/drivers/base/core.c#L611|device_remove_file]]. | + | O implementare pentru funcția de afișare de type este |
- | Parcurgeți secțiunile [[:so2:laboratoare:lab12#magistrale|Magistrale]] și [[:so2:laboratoare:lab12#dispozitive|Dispozitive]] din laborator. | + | <code c> |
- | </note> | + | static ssize_t |
+ | type_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
+ | { | ||
+ | struct bex_device *bex_dev = to_bex_device(dev); | ||
- | Pentru testare rulați comanda: | + | return sprintf(buf, "%s\n", bex_dev->type); |
- | <code> | + | } |
- | # cat /sys/devices/virtualbus0/echo/myattr | + | DEVICE_ATTR_RO(type); |
</code> | </code> | ||
- | Această comanda va conduce la rularea funcției ''my_show''. | + | ==== 3. [2p] Adăugare atribute del și add ==== |
- | + | ||
- | ===== [4p] Plug and Play ===== | + | |
- | + | ||
- | ==== 4. [2p] Conectarea la magistrala PNP ==== | + | |
- | Intrați în directorul ''parallel'', unde găsiți implementarea unui driver simplu pentru portul paralel. | + | Adăugați două atribute write-only, numite ''del'' și ''add''. ''del'' așteaptă un nume de device pentru a îl elimina, iar ''add'' așteaptă numele, tipul și versiunea pentru a crea un device nou. Urmăriți comentariile marcate cu ''TODO 3''. |
- | Analizați conținutul fișierului ''parallel.c''. Modificați sursa astfel încât să respecte modelul ''Linux Device Model'' și ''plug and play''. Dispozitivul se va conecta la magistrala ''PNP''. | + | |
<note tip> | <note tip> | ||
- | Înregistrați/deînregistrați structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/pnp.h#L380|pnp_driver]] la încărcarea/descărcarea modulului cu ajutorul funcțiilor [[http://elixir.free-electrons.com/linux/v4.9/source/drivers/pnp/driver.c#L266|pnp_register_driver]], respectiv [[http://elixir.free-electrons.com/linux/v4.9/source/drivers/pnp/driver.c#L274|pnp_unregister_driver]]. | + | Va trebui să adăugați atributele ''del'' și ''add'' în structura ''bex_bus_attr'' pe modelul: |
+ | <code> | ||
+ | &bus_attr_<insert-attribute-name-here>.attr, | ||
+ | </code> | ||
- | Pentru a fi un driver ''plug and play'', inițializarea dispozitivelor trebuie făcută în momentul în care acestea apar în sistem (la execuția funcției ''parallel_pnp_probe''), iar deînregistrarea în momentul în care dispar din sistem (la execuția funcției ''parallel_pnp_remove''). | ||
- | |||
- | Recitiți secțiunile [[:so2:laboratoare:lab12#magistrala_pnp|Magistrala PNP]] și [[:so2:laboratoare:lab12#operatii_pnp|Operații plug and play]] din laborator. | ||
</note> | </note> | ||
<note tip> | <note tip> | ||
- | Pentru înregistrarea și deînregistrarea unui dispozitiv folosiți funcțiile ''register_parallel_dev'' respectiv ''unregister_parallel_dev'' definite în cadrul scheletului de cod de laborator. | + | Folosiți ''sscanf'' pentru a parsa inputul de la ''sysfs'' și ''bex_del_dev()'' and ''bex_add_dev()'' pentru a șterge și crea un device nou. |
</note> | </note> | ||
- | Pentru verificare, urmăriți conținutul din directorul aferent din sysfs: | + | Un exemplu de funcție de tipul ''store'' este: |
- | <code> | + | |
- | # ls /sys/bus/pnp/drivers/parallel | + | |
- | </code> | + | |
- | ==== 5. [2p] Clase ==== | + | <code c> |
+ | static ssize_t | ||
+ | add_store(struct bus_type *bt, const char *buf, size_t count) | ||
+ | { | ||
+ | char type[32], name[32]; | ||
+ | int version; | ||
+ | int ret; | ||
- | Pornind de la modulul anterior, adăugați informațiile pentru o nouă clasă ''parclass'', de care aparține modulul ''paralel''. | + | ret = sscanf(buf, "%31s %31s %d", name, type, &version); |
+ | if (ret != 3) | ||
+ | return -EINVAL; | ||
- | <note tip> | + | return bex_add_dev(name, type, version) ? : count; |
- | Definiți o structură [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L388|class]] și o structură [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L782|device]]. | + | } |
+ | BUS_ATTR(add, S_IWUSR, NULL, add_store); | ||
+ | </code> | ||
- | Structura [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L388|class]] trebuie inițializată odată cu resursele driver-ului (la execuția funcției ''parallel_init'') și eliminată la ieșirea driverului (''parallel_exit''). Va trebui să folosiţi funcţiile [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L424|class_register]] şi [[http://elixir.free-electrons.com/linux/v4.9/source/drivers/base/class.c#L212|class_unregister]]. | + | Creați un device nou și verificați dacă este vizibil în ''/sys/bus/bex/devices''. Eliminați-l și verificați că a dispărut din ''sysfs''. |
- | În plus, pentru fiecare dispozitiv trebuie inițializată o structură [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L782|device]] și înregistrată cu [[http://elixir.free-electrons.com/linux/v4.9/source/drivers/base/core.c#L1798|device_create]] (la execuția funcției ''parallel_pnp_probe''). La apelul funcţiei [[http://elixir.free-electrons.com/linux/v4.9/source/drivers/base/core.c#L1798|device_create]], folosiți pentru al patrulea parametru construcția ''%%&dev->dev%%''. Este câmpul de tipul [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/device.h#L782|struct device]] al structurii [[http://elixir.free-electrons.com/linux/v4.9/source/include/linux/pnp.h#L244|struct pnp_dev]]. | + | <note tip> |
+ | Folosiți ''echo'' pentru a scrie în atributele magistralei: | ||
- | La înlăturarea dispozitivului (în funcţia ''parallel_pnp_remove'') folosiți funcția [[http://elixir.free-electrons.com/linux/v4.9/source/drivers/base/core.c#L1870|device_destroy]]. | + | <code> |
+ | $ echo "name type 1" > /sys/bus/bex/add | ||
+ | $ echo "name" > /sys/bus/bex/del | ||
+ | </code> | ||
+ | </note> | ||
- | Recitiți secțiunea [[:so2:laboratoare:lab12#clase|Clase]] din laborator. | + | ==== 4. [2p] Înregistrarea driver-ului bex misc ==== |
+ | Modificați fișierul ''bex-misc.c'' pentru a înregistra driver-ul la magistrala ''bex''. | ||
+ | Inserați modulul bex_misc.ko și creați un nou device de tip ''bex'' din ''sysfs'', cu numele //Test//, tipul //misc// și versiunea //2//. Urmăriți comentariile marcate cu ''TODO 4''. | ||
+ | Verificați dacă noul driver este vizibil în ''/sys/bus/bex/drivers''. | ||
+ | De ce nu este apelată funcția ''probe''? | ||
+ | |||
+ | <note tip> | ||
+ | Observați faptul că funcția de ''match'' pentru magistrală nu este implementată în ''bex.c''. | ||
</note> | </note> | ||
- | Pentru verificare, urmăriți conținutul directorului aferent din sysfs:<code> | + | Implementați funcția pentru match în fișierul ''bex.c''. Urmăriți comentariile marcate cu ''TODO 5''. |
- | # ls /sys/class/parclass/ | + | Încercați din nou adăugarea unui dispozitiv de tip ''bex'' și observați că de data aceasta funcția de ''probe'' este apelată. |
- | </code> | + | |
- | ===== Extra ===== | + | |
- | == (+3 karma) USB Hotplug == | + | |
- | În subdirectorul ''usb_extra'' găsiți o implementare minimală a unui driver USB. Analizați sursa ''usb.c'' și observați implementarea mecanismului de ''Hotplug'' si conectarea la magistrala ''USB''. Observați asemănările cu interfața dintre magistralei ''PNP'' studiată în laborator și driverele asociate: ''struct usb_driver'' / ''struct pnp_driver'', implementarea funcției ''probe'' (''skel_probe''), tabela ''skel_table'' cu care se inițializează câmpul ''id_table'' pentru a identifica device-urile compatibile, etc. | + | ==== 5. [1.5p] Înregistrarea unui device bex misc în funcția probe ==== |
- | Conectați un device USB pe mașina fizică. Apelați ''dmesg'' sau ''lsusb'' pentru a identifica vendorId-ul și productId-ul device-ul atașat. | + | Modificați ''bex_misc.c'' astfel încât operația ''probe'' să eșueze dacă ''version > 1''. Înregistrați device-ul misc în funcția ''bex_misc_probe()'' și deînregistrați-l în ''bex_misc_remove''. Urmăriți comentariile marcate cu ''TODO 6''. |
- | <code bash> | + | <note tip> |
- | usb 3-2: New USB device found, idVendor=1e3d, idProduct=6025 | + | Folosiți ''misc_register()'' and ''misc_deregister()''. |
- | </code> | + | </note> |
- | Modificați codul din ''usb.c'' pentru a crea un driver compatibil cu device-ul vostru. Compilați modulul și inserați-l pe mașina fizică (sau pe o mașină virtuală cu udev și acces la USB-ul gazdei). Reconectați device-ul USB. Ce observați la rularea comenzii dmesg? | + | Creați un device nou cu numele //test//, tipul //misc// și versiunea //2// și verificați că operația ''probe'' eșuează. |
+ | Creați un nou device cu numele //test//, tipul //misc// și versiunea //1// și verificați că operația se termină cu succes. | ||
- | Cel mai probabil device-ul va fi preluat de alt driver usb din sistem (e.x. usb_storage). În acest caz, puteți descărca temporar modulul concurent (rmmod usb_storage). Dacă device-ul se conectează la driverului ''usb.ko'', puteți observa mesajul "USB Skeleton device now attached to USBSkel-0", mesaj afișat de funcția ''skel_probe''. | + | Verificați conținutul fișierului ''/sys/bus/bex/devices/test'' și observați că există o nouă intrare. Identificați majorul și minorul pentru device-ul ''misc'', creați un device node de tip caracter pentru acest dispozitiv (''Hint'': mknod) și încercați operații de citire și scriere pe fișierul creat, pentru a accesa buffer-ul dispozitivului. |
+ | |||
+ | <note tip> | ||
+ | Indicatorii ''major'' și ''minor'' sunt vizibili în atributul ''dev'' al dispozitivului de tip ''misc''. | ||
+ | </note> | ||
- | Descărcați modulul. Creați o regulă ''udev'', care la identificarea dispozitivului (după ''ATTRS{idVendor}'' și ''ATTRS{idProduct}''), să încarce driverul ''usb.ko''. Recitiți secțiunea [[:so2:laboratoare:lab12#hotplug | Hotplug]] din laborator. Cu modulul descărcat, reconectați device-ul USB. Ce observați la rularea comenzilor ''dmesg'' și ''lsmod''? | + | ==== 6. [1p] Monitorizarea notificărilor uevent ==== |
+ | Folosiți comanda ''udevadm'' și observați ce se întâmplă când: | ||
+ | * modulele ''bex.ko'' and ''bex_misc.ko'' sunt inserate | ||
+ | * un nou device cu tipul //test// este creat | ||
+ | * un nou device cu tipul //misc// și versiunea //2// este creat | ||
+ | * un nou device cu tipul //misc// și versiunea //1// este creat | ||
+ | * toate de mai sus sunt eliminate | ||
- | ===== Soluții ===== | ||
- | * [[http://elf.cs.pub.ro/so2/res/laboratoare/lab12-sol.zip|Soluții exerciții laborator 12]] |