Il ioctl
La funzione è utile per implementare un driver di dispositivo per impostare la configurazione sul dispositivo. per esempio. una stampante che ha opzioni di configurazione per controllare e impostare la famiglia di caratteri, la dimensione del carattere ecc. ioctl
potrebbe essere utilizzato per ottenere il carattere corrente e impostare il carattere su uno nuovo. Un'applicazione utente utilizza ioctl
per inviare un codice a una stampante dicendole di restituire il font corrente o di impostarne uno nuovo.
int ioctl(int fd, int request, ...)
fd
è il descrittore di file, quello restituito daopen
;request
è il codice di richiesta. ad esempioGETFONT
otterrà il font corrente dalla stampante,SETFONT
imposterà il font sulla stampante;- il terzo argomento è
void *
. A seconda del secondo argomento, il terzo può o non può essere presente, ad es. se il secondo argomento èSETFONT
, il terzo argomento può essere il nome del font come"Arial"
;
int request
non è solo una macro. È necessaria un'applicazione utente per generare un codice di richiesta e il modulo del driver del dispositivo per determinare con quale configurazione sul dispositivo deve essere riprodotto. L'applicazione invia il codice di richiesta utilizzando ioctl
e quindi utilizza il codice di richiesta nel modulo del driver del dispositivo per determinare quale azione eseguire.
Un codice di richiesta ha 4 parti principali
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 il codice della richiesta è SETFONT
per impostare il carattere su una stampante, la direzione per il trasferimento dei dati sarà dall'applicazione utente al modulo del driver del dispositivo (l'applicazione utente invia il nome del carattere "Arial"
alla stampante). Se il codice di richiesta è GETFONT
, la direzione è dalla stampante all'applicazione utente.
Per generare un codice di richiesta, Linux fornisce alcune macro simili a funzioni predefinite.
1._IO(MAGIC, SEQ_NO)
entrambi sono 8 bit, da 0 a 255, ad es. diciamo che vogliamo mettere in pausa la stampante. Ciò non richiede un trasferimento di dati. Quindi genereremmo il codice di richiesta come di seguito
#define PRIN_MAGIC 'P'
#define NUM 0
#define PAUSE_PRIN __IO(PRIN_MAGIC, NUM)
e ora usa ioctl
come
ret_val = ioctl(fd, PAUSE_PRIN);
La chiamata di sistema corrispondente nel modulo driver riceverà il codice e metterà in pausa la stampante.
__IOW(MAGIC, SEQ_NO, TYPE)
MAGIC
eSEQ_NO
sono gli stessi di sopra eTYPE
fornisce il tipo dell'argomento successivo, ricorda il terzo argomento diioctl
èvoid *
. Vincita in__IOW
indica che il flusso di dati va dall'applicazione utente al modulo driver. Ad esempio, supponiamo di voler impostare il font della stampante su"Arial"
.
#define PRIN_MAGIC 'S'
#define SEQ_NO 1
#define SETFONT __IOW(PRIN_MAGIC, SEQ_NO, unsigned long)
ulteriormente,
char *font = "Arial";
ret_val = ioctl(fd, SETFONT, font);
Ora font
è un puntatore, il che significa che è un indirizzo meglio rappresentato come unsigned long
, da qui la terza parte di _IOW
menziona il tipo in quanto tale. Inoltre, questo indirizzo del carattere viene passato alla corrispondente chiamata di sistema implementata nel modulo del driver del dispositivo come unsigned long
e dobbiamo convertirlo nel tipo corretto prima di usarlo. Lo spazio del kernel può accedere allo spazio utente e quindi funziona. altre due macro simili a funzioni sono __IOR(MAGIC, SEQ_NO, TYPE)
e __IORW(MAGIC, SEQ_NO, TYPE)
dove il flusso di dati sarà rispettivamente dallo spazio del kernel allo spazio utente e in entrambe le direzioni.
Per favore fatemi sapere se questo aiuta!
Un ioctl
, che significa "controllo input-output" è una sorta di chiamata di sistema specifica del dispositivo. Ci sono solo poche chiamate di sistema in Linux (300-400), che non sono sufficienti per esprimere tutte le funzioni uniche che i dispositivi possono avere. Quindi un driver può definire un ioctl che consente a un'applicazione in spazio utente di inviargli ordini. Tuttavia, gli ioctl non sono molto flessibili e tendono a diventare un po' confusi (dozzine di "numeri magici" che funzionano... o meno), e possono anche essere insicuri, poiché si passa un buffer nel kernel - una cattiva gestione può rompersi le cose facilmente.
Un'alternativa è il sysfs
interface, dove imposti un file sotto /sys/
e leggerlo/scriverlo per ottenere informazioni da e per il conducente. Un esempio di come impostarlo:
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 l'installazione del driver:
device_create_file(dev, &dev_attr_version);
Avresti quindi un file per il tuo dispositivo in /sys/
, ad esempio, /sys/block/myblk/version
per un driver a blocchi.
Un altro metodo per un uso più pesante è netlink, che è un metodo IPC (comunicazione tra processi) per comunicare con il tuo driver tramite un'interfaccia socket BSD. Questo viene utilizzato, ad esempio, dai driver WiFi. Quindi comunichi con esso dallo spazio utente utilizzando il libnl
o libnl3
librerie.