Respostas:
An ioctl
, que significa "controle de entrada e saída", é um tipo de chamada de sistema específica ao dispositivo. Existem apenas algumas chamadas de sistema no Linux (300-400), que não são suficientes para expressar todas as funções exclusivas que os dispositivos podem ter. Assim, um driver pode definir um ioctl que permite que um aplicativo no espaço do usuário envie pedidos. No entanto, os ioctls não são muito flexíveis e tendem a ficar um pouco confusos (dezenas de "números mágicos" que simplesmente funcionam ... ou não), e também podem ser inseguros, ao passar um buffer para o kernel - o mau manuseio pode quebrar coisas facilmente.
Uma alternativa é a sysfs
interface, na qual você configura um arquivo /sys/
e lê / grava para obter informações de e para o driver. Um exemplo de como configurar isso:
static ssize_t mydrvr_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", DRIVER_RELEASE);
}
static DEVICE_ATTR(version, S_IRUGO, mydrvr_version_show, NULL);
E durante a configuração do driver:
device_create_file(dev, &dev_attr_version);
Você teria um arquivo para o seu dispositivo /sys/
, por exemplo, /sys/block/myblk/version
para um driver de bloco.
Outro método para uso mais pesado é o netlink, que é um método IPC (comunicação entre processos) para conversar com seu driver por uma interface de soquete BSD. Isso é usado, por exemplo, pelos drivers WiFi. Você então se comunica com ele no espaço do usuário usando as bibliotecas libnl
ou libnl3
.
A ioctl
função é útil para implementar um driver de dispositivo para definir a configuração no dispositivo. por exemplo, uma impressora que possua opções de configuração para verificar e definir a família da fonte, o tamanho da fonte etc. ioctl
pode ser usada para obter a fonte atual e definir a fonte para uma nova. Um aplicativo de usuário usa ioctl
para enviar um código para uma impressora solicitando que ela retorne a fonte atual ou defina a fonte para uma nova.
int ioctl(int fd, int request, ...)
fd
é o descritor de arquivo, aquele retornado por open
;request
é um código de solicitação. por exemplo GETFONT
, obterá a fonte atual da impressora, SETFONT
definirá a fonte na impressora;void *
. Dependendo do segundo argumento, o terceiro pode ou não estar presente, por exemplo, se o segundo argumento for SETFONT
, o terceiro argumento pode ser o nome da fonte, como "Arial"
;int request
não é apenas uma macro. É necessário um aplicativo de usuário para gerar um código de solicitação e o módulo do driver do dispositivo para determinar com qual configuração o dispositivo deve ser reproduzido. O aplicativo envia o código de solicitação usando ioctl
e, em seguida, usa o código de solicitação no módulo do driver de dispositivo para determinar qual ação executar.
Um código de solicitação possui 4 partes principais
1. A Magic number - 8 bits
2. A sequence number - 8 bits
3. Argument type (typically 14 bits), if any.
4. Direction of data transfer (2 bits).
Se o código de solicitação for SETFONT
definir fonte em uma impressora, a direção da transferência de dados será do aplicativo do usuário para o módulo do driver de dispositivo (o aplicativo do usuário envia o nome da fonte "Arial"
para a impressora). Se o código de solicitação for GETFONT
, a direção é da impressora para o aplicativo do usuário.
Para gerar um código de solicitação, o Linux fornece algumas macros predefinidas semelhantes a funções.
1. _IO(MAGIC, SEQ_NO)
ambos são 8 bits, 0 a 255, por exemplo, digamos que queremos pausar a impressora. Isso não requer uma transferência de dados. Então, geraríamos o código de solicitação como abaixo
#define PRIN_MAGIC 'P'
#define NUM 0
#define PAUSE_PRIN __IO(PRIN_MAGIC, NUM)
e agora use ioctl
como
ret_val = ioctl(fd, PAUSE_PRIN);
A chamada do sistema correspondente no módulo do driver receberá o código e pausará a impressora.
__IOW(MAGIC, SEQ_NO, TYPE)
MAGIC
e SEQ_NO
são os mesmos que acima, e TYPE
fornece o tipo do próximo argumento, lembre-se do terceiro argumento de ioctl
é void *
. W in __IOW
indica que o fluxo de dados é do aplicativo do usuário para o módulo do driver. Como exemplo, suponha que desejamos definir a fonte da impressora para "Arial"
.#define PRIN_MAGIC 'S'
#define SEQ_NO 1
#define SETFONT __IOW(PRIN_MAGIC, SEQ_NO, unsigned long)
mais distante,
char *font = "Arial";
ret_val = ioctl(fd, SETFONT, font);
Agora font
é um ponteiro, o que significa que é um endereço melhor representado como unsigned long
, portanto, a terceira parte das _IOW
menções digita como tal. Além disso, esse endereço de fonte é passado para a chamada de sistema correspondente implementada no módulo do driver de dispositivo unsigned long
e precisamos convertê-lo no tipo apropriado antes de usá-lo. O espaço do kernel pode acessar o espaço do usuário e, portanto, isso funciona. existem outras duas macros semelhantes a funções __IOR(MAGIC, SEQ_NO, TYPE)
e __IORW(MAGIC, SEQ_NO, TYPE)
onde o fluxo de dados será do espaço do kernel para o espaço do usuário e nos dois sentidos, respectivamente.
Por favor, deixe-me saber se isso ajuda!