This is an old revision of the document!


Laborator 12: Exerciții

Pregătirea laboratorului

Pentru rezolvarea laboratorului, vom lucra în același director din care pornim mașina virtuală (~/so2/linux/tools/labs).

Pașii de rezolvare sunt următorii:

  • pregătirea scheletului de laborator
  • compilarea modulelor de Kernel
  • copierea modulelor pe mașina virtuală
  • pornirea mașinii virtuale și testarea modulelor

Pregătirea scheletului de laborator

Scheletul de laborator este generat din sursele din directorul tools/labs/templates. Putem genera scheletele pentru toate laboratoarele folosind următoarea comanda:

tools/labs $ make skels

Pentru a genera scheletul pentru un singur laborator, vom folosi variabila de mediu LABS:

tools/labs $ make clean
tools/labs $ LABS=<lab name> make skels

Numele laboratorului curent este device_model.

Similar, putem genera și scheletul pentru un singur exercițiu, atribuind valoarea <lab_name>/<task_name> variabilei LABS.

Scheletul este generat în directorul tools/labs/skels.

Compilarea modulelor

Comanda make build compilează toate modulele din directorul skels.

student@eg106:~/so2/linux/tools/labs$ make build
echo "# autogenerated, do not edit " > skels/Kbuild
echo "ccflags-y += -Wno-unused-function -Wno-unused-label -Wno-unused-variable " >> skels/Kbuild
for i in ./device_model; do echo "obj-m += $i/" >> skels/Kbuild; done
...

Copierea modulelor pe mașina virtuală

Putem copia modulele generate pe mașina virtuală folosind target-ul copy al comenzii make, atunci când mașina virtuală este oprită.

student@eg106:~/so2/linux/tools/labs$ make copy
student@eg106:~/so2/linux/tools/labs$ make boot

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 Interacțiunea cu mașina virtuală.

Testarea modulelor

Modulele generate sunt copiate pe mașina virtuală în directorul /home/root/skels/<lab_name>.

root@qemux86:~/skels/device_model# ls
bex.ko       bex_misc.ko

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:

root@qemux86:~# insmod skels/<lab_name>/<task_name>/<module_name>.ko
root@qemux86:~# rmmod skels/<lab_name>/<task_name>/<module_name>.ko

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:

  1. Î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.
  2. Î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.
  3. În al treilea tab de terminal accesăm directorul ~/so2/linux/ cu sursele nucleului unde putem folosi Vim și cscope pentru parcurgerea codului sursă.
    student@eg106-pc:~$ netcat -lup 6666

Exerciții

Î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.

[0.5p] Intro

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”

Vă mulțumim! 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.

Completare formular de feedback

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 formularul de feedback de pe cs.curs.pub.ro (trebuie să fiți autentificați și înrolați în cadrul cursului). Vă mulțumim!

[6.5p] Linux Device Model

Î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.

În rezolvarea acestui exerciţiu vă veţi întâlni cu:

  • 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

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. Observaţi că la încărcarea modulului se înregistrează atât o structură bus_type, reprezentând tipul de magistrală, cât și o structură device, reprezentând dispozitivul efectiv al magistralei.

Compilați și inserați modulul. 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.

Recitiți secțiunile Magistrale și Dispozitive din laborator.

Pentru verificare trebuie să se fi creat pe mașina virtuală în /sys intrările aferente magistralei (virtualbus) și dispozitivului părinte (virtualbus0):

# ls /sys/bus/virtualbus/
# ls /sys/devices/virtualbus0/

2. [2p] Conectare driver la magistrală

Intrați în directorul virtual_bus/mydriver, unde găsiți implementarea unui device driver de tip caracter.

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.

Urmăriți comentariile TODO 2 din cod și exemplele de înregistrare dispozitiv/driver din laborator: înregistrare dispozitiv, înregistrare driver.

Pentru a vă conecta la magistrala virtualbus, va trebui să folosiți funcțiile și tipurile de driver/device exportate de aceasta.

Î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.

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.

Parcurgeți secțiunea Drivere din laborator. Urmăriți exemplele de cod.

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 a transmite primul argument (magistrala) funcției bus_find_device folosiți o expresie de forma mydriver.driver.bus.

Î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.

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ă struct device. Fiind un câmp din datele private ale dispozitivului, pentru a accesa dev->p->driver_data e recomandat să folosiți funcțiile (interfața) dev_set_drvdata, dev_get_drvdata.

Parcurgeți secțiunea Dispozitive din laborator. Urmăriți exemplele de cod.

Compilaţi modulul şi copiaţi-l pe maşina virtuală, împreună cu modulul de la exerciţiul anterior.

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.

Pentru testare urmăriți intrările de dispozitiv (echo) și de driver (echo) în /sys pe mașina virtuală:

# ls /sys/bus/virtualbus/devices/
# ls /sys/bus/virtualbus/drivers/
# ls /sys/devices/virtualbus0/

2.1. [1.5p] Informații device-uri de pe magistrală

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.

Pentru căutarea unui dispozitiv folosiți funcția 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.

Numele device-ului întors de funcția bus_find_device_by_name se poate obține cu funcția dev_name.

Pentru verificare la inserarea modulului se va afișa mesaj cu numele dispozitivului.

3. [2p] Atribute device drivere

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.

Urmăriți comentariile TODO 3 din cod.

Va trebui să declarați și să înregistrați o structură device_attribute. Puteți folosi macro-ul DEVICE_ATTR ce va crea o structură cu numele dev_attr_##_name, unde ##_name este numele atributului. Pentru folosirea macro-ului va trebui să specificaţi, în această ordine:

  • numele atributului
  • permisiunile la acces la intrarea din sysfs aferentă atributului; folosiţi valoarea 0444.
  • o funcție show ce afișează valoarea atributului pornind de la informațiile din struct device.
  • o funcţie store, care în cazul vostru poate fi NULL.

În funcţia show, puteți folosi macrodefinițiile MAJOR și 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 dev_get_drvdata.

Țineți cont să adăugați/eliminați atributul la inițializarea/dezactivarea modulului cu ajutorul funcțiilor device_create_file, device_remove_file.

Parcurgeți secțiunile Magistrale și Dispozitive din laborator.

Pentru testare rulați comanda:

# cat /sys/devices/virtualbus0/echo/myattr

Această comanda va conduce la rularea funcției my_show.

[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. 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.

Înregistrați/deînregistrați structura pnp_driver la încărcarea/descărcarea modulului cu ajutorul funcțiilor pnp_register_driver, respectiv pnp_unregister_driver.

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 Magistrala PNP și Operații plug and play din laborator.

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.

Pentru verificare, urmăriți conținutul din directorul aferent din sysfs:

# ls /sys/bus/pnp/drivers/parallel

5. [2p] Clase

Pornind de la modulul anterior, adăugați informațiile pentru o nouă clasă parclass, de care aparține modulul paralel.

Definiți o structură class și o structură device.

Structura 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 class_register şi class_unregister.

În plus, pentru fiecare dispozitiv trebuie inițializată o structură device și înregistrată cu device_create (la execuția funcției parallel_pnp_probe). La apelul funcţiei device_create, folosiți pentru al patrulea parametru construcția &dev->dev. Este câmpul de tipul struct device al structurii struct pnp_dev.

La înlăturarea dispozitivului (în funcţia parallel_pnp_remove) folosiți funcția device_destroy.

Recitiți secțiunea Clase din laborator.

Pentru verificare, urmăriți conținutul directorului aferent din sysfs:

# ls /sys/class/parclass/

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.

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.

usb 3-2: New USB device found, idVendor=1e3d, idProduct=6025

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?

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.

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 Hotplug din laborator. Cu modulul descărcat, reconectați device-ul USB. Ce observați la rularea comenzilor dmesg și lsmod?

Soluții

so2/laboratoare/lab12/exercitii.1526418883.txt.gz · Last modified: 2018/05/16 00:14 by razvan.deaconescu
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