obj-m += ofd.o
obj-y += ofd/
$ make -j 5 ARCH=i386
$ qemu-system-i386 -hda ../ArchLinux_mc504.cow -kernel arch/x86/boot/bzImage -append "rw root=/dev/hda" -drive format=raw,file=drivers/ofd/ofd.ko
$ cat /dev/hdb > ofd.ko $ insmod ofd.ko $ lsmod ... $ rmmod ofd.ko $ dmesg | grep ofd
Neste caso, você irá compilar os drivers e depois incorporá-los dinamicamente com o comando insmod ou modprobe. Utilize, preferencialmente, uma partição dedicada só para isso ou uma máquina virtual. Caso contrário, sua implementação pode corromper o kernel.
# dnf install kernel-develPara instalar headers que combinam com o kernel instalado.
# dnf install "kernel-devel-uname-r == $(uname -r)"
CC = gcc
CONFIG_MODULE_SIG=n
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
.PHONY: build clean
build:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c
else
$(info Building with KERNELRELEASE = ${KERNELRELEASE})
obj-m := ofd.o
endif
$ insmod ofcd.ko $ lsmod ... $ dmesg | tail $ cat /proc/devices ... 252 ofcdÉ possível criar as entradas em /dev explicitamente:
$ mknod /dev/ofcd0 c 252 0 $ mknod /dev/ofcd1 c 252 1 $ mknod /dev/ofcd2 c 252 2 $ ls /devNo entanto, ainda não conseguimos utilizar estes arquivos.
$ cat /dev/ofcd0
$ insmod ofcd-null.ko $ ls /dev/ $ cat /dev/ofcd-null $ echo "teste" > /dev/ofcd-null
static char c;
static ssize_t my_read(struct file *f, char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Driver: read()\n");
buf[0] = c;
return 1;
}
static ssize_t my_write(struct file *f, const char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Driver: write()\n");
c = buf[len - 1];
return len;
}
Esta abordagem está codificada no arquivo ofcd-lastchar-bug.c.
Você pode testar com a aplicação bug-app.
static char c;
static ssize_t my_read(struct file *f, char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Driver: read()\n");
if (copy_to_user(buf, &c, 1) != 0)
return -EFAULT;
else
return 1;
}
static ssize_t my_write(struct file *f, const char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Driver: write()\n");
if (copy_from_user(&c, buf + len - 1, 1) != 0)
return -EFAULT;
else
return len;
}
Refaça os testes com a aplicação lastchar-app.
Se você procurar a documentação sobre copy_to_user e copy_from_user verá que estas funções podem dormir. Por quê?
Exercício simples (não é sugestão de tema para projeto): tente implementar uma versão deste driver que armazene a última escrita com kmalloc e kfree.
./query_app to display the driver variables ./query_app -c to clear the driver variables ./query_app -g to display the driver variables ./query_app -s to set the driver variablesVocê deve rodar este exemplo e entender o funcionamento de ioctl. Como posso alterar o tipo de argumento passado para ioctl? Esta função permite grande flexibilidade..
No capítulo 4 do Kernel Hacking: ioctls: Not writing a new system call podemos encontrar a seguinte informação:
A system call generally looks like this
asmlinkage long sys_mycall(int arg)
{
return 0;
}
First, in most cases you don't want to create a new system call. You
create a character device and implement an appropriate ioctl for
it. This is much more flexible than system calls, doesn't have to be
entered in every architecture's include/asm/unistd.h and
arch/kernel/entry.S file, and is much more likely to be accepted by
Linus.
No entanto, nem todos amam este design. Você consegue dizer algumas desvantagens?