
Jak to chodí v jádře aneb napište si vlastní ovladač (8)
Po trochu větším předchozím díle trochu zvolníme a podíváme se na další
souborovou operaci. Samozřejmostí je přiložený ukázkový příklad.
Minule jsem vás nabádal k položení otázek k částem, které stále nechápete.
Neuvědomil jsem si, že mezi tímto a minulým článkem je pouze víkend. Většina
z vás není na internetu, článek si tedy nepřečte a nebude moci položit
dotaz. Proto přesunu odpovědi na dotazy (pokud nějaké budou) do některého
z dalších dílů (středa nebo pátek).
Nová souborová operace
Dnes si odpočineme a ukážeme si pouze jednu novou věc. Naučíme se měnit
pozici v našem zařízení. Jistě se většina z vás v rámci programátorské praxe
setkala s funkcí lseek (viz. man lseek). My si právě
teď naimplementujeme novou část modulu, která bude umožňovat použití funkce
lseek.
Prototyp
Funkce umožňující pohyb v rámci našeho zařízení je součástí souborových
operací. Podíváme se tedy do struktury file_operations jak má
vypadat.
loff_t (*llseek) (struct file *, loff_t, int);
Jak již bývá zvykem, našim prvním parametrem je ukazatel na strukturu
file, která udává jakého souboru se požadovaná operace
týká. Druhý parametr je relativně či absolutně určená pozice souboru. Jakým
způsobem bude jeho hodnota využita určuje náš poslední parametr, který nabízí
tři možnosti:
SEEK_SET
Druhý parametr určuje absolutní pozici v souboru.
SEEK_CUR
Druhý parametr určuje relativní pozici v souboru vůči pozici
aktuální. Nová pozice se tedy vypočítá jako součet pozice aktuální
a námi uvedené hodnoty v druhém parametru. V případě záporné hodnoty se
samozřejmě budeme vracet zpět.
SEEK_END
Druhý parametr určuje relativní pozici v souboru, ale už vůči konci
souboru. Nová pozice se tedy vypočítá jako součet velikosti souboru
a námi uvedené hodnoty v druhém parametru.
Ještě se zmíníme o návratové hodnotě. V případě úspěšného provedení přesunu
je vrácena nově nastavená pozice souboru. Je-li návratová hodnota menší jako
nula, obsahuje chybový kód.
Implementace
Podívejme se postupně na jednotlivé části námi implementované funkce.
Na začátku si ze struktury file přečteme ukazatel na strukturu
otevřeného zařízení a raději zkontrolujeme jestli není NULL.
static loff_t
lz_llseek(struct file *filp, loff_t off, int whence)
{
lzdev_t *dev = (lzdev_t *)filp->private_data;
loff_t pos;
// Mame spatne zarizeni?
if (dev == NULL)
return -EBADF;
Nyní se podle třetího parametru rozhodneme co uděláme s pozicí
souboru. V případě 0 (SEEK_SET) je to absolutní pozice a tak
pouze přiřaď novou pozici.
// Jakym zpusobem se mame posunout?
switch (whence)
{
case 0: /// SEEK_SET - nastaveni absolutni pozice
pos = off;
break;
V případě 1 (SEEK_CUR) se jedná o relativní posun vůči
aktuální pozici a tak musíme novou pozici vypočítat.
case 1: /// SEEK_CUR - relativni posun
pos = filp->f_pos + off;
break;
No a v třetím případě 2 (SEEK_END) se jedná také o relativní
posun vůči konci souboru.
case 2: /// SEEK_END - pozice od konce
pos = dev->size + off;
break;
Samozřejmě zde nechybí kontrola špatně zadaného parametru.
default: // spatne zadana hodnota
return -EINVAL;
}
Raději zkontrolujeme zda-li uživatel neprovedl změnu mimo rozsah
zařízení. Pokud ano, je nutné vrátit chybový kód -EINVAL.
// Jsme mimo rozsah?
if (pos < 0)
return -EINVAL;
Nová pozice je v pořádku. Modifikujeme f_pos na novou pozici
a také ji použijeme jako návratovou hodnotu funkce llseek.
// Nastav novou pozici v zarizeni
filp->f_pos = pos;
// A zaroven ji vrat
return pos;
}
Na vlastní oči jste se přesvědčili, že tato část patří do těch
jednodušších. Pro přehlednost si raději celou funkci zopakujeme ještě jednou
bez přerušování a komentářů.
static loff_t
lz_llseek(struct file *filp, loff_t off, int whence)
{
lzdev_t *dev = (lzdev_t *)filp->private_data;
loff_t pos;
if (dev == NULL)
return -EBADF;
switch (whence)
{
case 0:
pos = off;
break;
case 1:
pos = filp->f_pos + off;
break;
case 2:
pos = dev->size + off;
break;
default:
return -EINVAL;
}
if (pos < 0)
return -EINVAL;
filp->f_pos = pos;
return pos;
}
Struktura souborových operací
Nesmíme zapomenout funkci llseek přiřadit do našich
souborových operací.
static struct file_operations lzdev_fops =
{
open: lz_open,
release: lz_release,
read: lz_read,
write: lz_write,
llseek: lz_llseek
};
Ukázka použití
V souboru example.c najdete jednoduchou ukázku
použití. Myslím, že k ní není potřeba dalšího komentáře. Snad jen to, že by
Vám při správné funkci měla vypsat toto:
[root@echelon v0jta-ldd-8]# ./example /dev/lzdev0
zarizeni.
Failed to set new rel pos -1024
[root@echelon v0jta-ldd-8]#
Závěr
Co řící závěrem? Nic chytrého mě teď bohužel nenapadá a tak jen doufám, že
se vám tento seriál líbí.
No a co příště? Trochu zabrousíme do jednoho ze způsobů jakým lze náš modul
ladit - všem známý /proc.
Soubory
Použité ukázky v dnešním díle naleznete zde.
Další části seriálu:
|