Eingabe-Subsystem-Framework Das Linux-Eingabesubsystem wird von oben nach unten durch drei Schichten implementiert, nämlich: die Ereignisverarbeitungsschicht des Eingabesubsystems (EventHandler), die Kernschicht des Eingabesubsystems (InputCore) und die Gerätetreiberschicht des Eingabesubsystems. Ein Eingabeereignis wie eine Mausbewegung, das Drücken einer Tastaturtaste, eine Joystickbewegung usw. erreicht den Benutzerbereich über Eingabetreiber -> Eingabekern -> Ereignishandler -> Benutzerbereich und wird an die Anwendung übergeben. 【Hinweis】 keyboard.c generiert keine Knoten unter /dev/input, dient aber als Eingabe für das TTYN-Terminal (ausgenommen das serielle Port-Terminal). Treiberebene Für die Gerätetreiberschicht des Eingabesubsystems implementiert es hauptsächlich Lese- und Schreibzugriff auf Hardwaregeräte sowie Interrupteinstellungen, konvertiert die von der Hardware generierten Ereignisse in von der Kernschicht definierte Spezifikationen und übermittelt sie an die Ereignisverarbeitungsschicht. Wandeln Sie Hardwareeingaben auf niedriger Ebene in ein einheitliches Ereignisformat um und melden Sie sie an den Input Core. Kernschicht des Eingabesubsystems Für die Kernschicht stellt es Spezifikationen und Schnittstellen für die Gerätetreiberschicht bereit. Die Gerätetreiberschicht muss sich nur darum kümmern, wie die Hardware gesteuert und Hardwaredaten (z. B. Daten zu gedrückten Tasten) abgerufen werden. Anschließend ruft sie die von der Kernschicht bereitgestellte Schnittstelle auf. Die Kernschicht übermittelt die Daten automatisch an die Ereignisverarbeitungsschicht. Es stellt der Treiberschicht Registrierungs- und Betriebsschnittstellen für Eingabegeräte wie input_register_device bereit, benachrichtigt die Ereignisverarbeitungsschicht über die Verarbeitung von Ereignissen und generiert entsprechende Geräteinformationen unter /Proc. Ereignisverarbeitungsebene Für die Ereignisverarbeitungsschicht ist es die Benutzerprogrammierschnittstelle (Geräteknoten) und verarbeitet die von der Treiberschicht übermittelten Daten. Es interagiert hauptsächlich mit dem Benutzerbereich (unter Linux werden alle Geräte im Benutzerbereich als Dateien behandelt. Da in allgemeinen Treibern FOP-Schnittstellen bereitgestellt werden und entsprechende Gerätedateien nod unter /dev generiert werden, werden diese Vorgänge von der Ereignisverarbeitungsschicht im Eingabesubsystem ausgeführt). Das Verzeichnis /dev/input zeigt die Geräteprogrammierschnittstellen an, die im Kernel registriert wurden. Benutzer öffnen diese Gerätedateien, um verschiedene Eingabegeräte für Hardwareoperationen zu öffnen. Die Ereignisverarbeitungsschicht bietet Benutzerzugriffs- und Verarbeitungsschnittstellen für verschiedene Hardwaretypen. Wenn wir beispielsweise das Gerät /dev/input/mice öffnen, wird der Maushandler der Ereignisverarbeitungsschicht aufgerufen, um das Eingabeereignis zu verarbeiten. Dies bedeutet auch, dass sich die Gerätetreiberschicht nicht um den Betrieb der Gerätedatei kümmern muss, da der Maushandler bereits über die entsprechende Ereignisverarbeitungsmethode verfügt. Das Eingabesubsystem besteht aus dem Kernelcode drivers/input/input.c. Seine Existenz schützt die Interaktionsdetails zwischen dem Benutzer und dem Gerätetreiber und bietet eine einheitliche Schnittstelle für die Kommunikation zwischen der Gerätetreiberschicht und der Ereignisverarbeitungsschicht. Die obige Abbildung zeigt die von der Kernschicht des Eingabesubsystems bereitgestellte Unterstützung und wie Ereignisse an die Eingabeereignistreiber gemeldet werden. Als Treiberentwickler für ein Eingabegerät müssen Sie die folgenden Schritte ausführen:
/// ... Der Eingabekern stellt die vom zugrunde liegenden Eingabegerätetreiber benötigten APIs bereit, beispielsweise zum Zuweisen/Freigeben eines Eingabegeräts:
/** * input_allocate_device – reserviert Speicher für neues Eingabegerät * * Gibt vorbereitete Struktur input_dev oder NULL zurück. * * HINWEIS: Verwenden Sie input_free_device() um Geräte freizugeben, die noch nicht * registriert; input_unregister_device() sollte für bereits * registrierte Geräte. */ Struktur input_dev *input_allocate_device(void) { Struktur input_dev *dev; /*Eine input_dev-Struktur zuweisen und auf 0 initialisieren*/ dev = kzalloc(Größe von(Struktur input_dev), GFP_KERNEL); wenn (dev) { dev->dev.type = &input_dev_type;/*Gerätetyp initialisieren*/ dev->dev.class = &input_class; /*Auf Eingabegeräteklasse setzen*/ device_initialize(&dev->dev);/*Gerätestruktur initialisieren*/ mutex_init(&dev->mutex); /*Mutex initialisieren*/ spin_lock_init(&dev->event_lock); /*Ereignis-Spinlock initialisieren*/ INIT_LIST_HEAD(&dev->h_list);/*Initialisiere die verknüpfte Liste*/ INIT_LIST_HEAD(&dev->node); /*Initialisiere die verknüpfte Liste*/ __module_get(THIS_MODULE);/*Modulreferenztechnologie plus 1*/ } Rückgabewert; } Die Schnittstelle zum Registrieren/Abmelden von Eingabegeräten sieht wie folgt aus:
/** * input_register_device – Gerät beim Eingabekern registrieren * @dev: zu registrierendes Gerät * * Diese Funktion registriert das Gerät beim Input Core. Das Gerät muss * zugewiesen mit input_allocate_device() und allen seinen Fähigkeiten * vor der Registrierung einrichten. * Wenn die Funktion fehlschlägt, muss das Gerät mit input_free_device() freigegeben werden. * Sobald das Gerät erfolgreich registriert wurde, kann es abgemeldet werden * mit input_unregister_device(); input_free_device() sollte nicht * in diesem Fall genannt. */ int input_register_device(Struktur input_dev *dev) { //Definieren Sie einige lokale Variablen, die in einigen Funktionen verwendet werden static atomic_t input_no = ATOMIC_INIT(0); Struktur input_handler *handler; const char *Pfad; int-Fehler; //Legen Sie den von input_dev unterstützten Ereignistyp fest, der durch das evbit-Mitglied dargestellt wird. Die spezifischen Typen werden später zusammengefasst. /* Jedes Eingabegerät generiert EV_SYN/SYN_REPORT-Ereignisse. */ __set_bit(EV_SYN, dev->evbit); /* KEY_RESERVED darf nicht an den Benutzerbereich übertragen werden. */ __clear_bit(SCHLÜSSEL_RESERVIERT, dev->keybit); /* Stellen Sie sicher, dass die in dev->evbit nicht erwähnten Bitmasken sauber sind. */ input_cleanse_bitmasks(Gerät); // Initialisieren Sie den Timer, um wiederholte Tastenklicks zu verarbeiten. (Entprellung) /* * Wenn Verzögerung und Dauer vom Fahrer voreingestellt werden, dann erfolgt die automatische Wiederholung * wird vom Treiber selbst gehandhabt und wir tun dies nicht in input.c. */ init_timer(&dev->timer); //Wenn rep[REP_DELAY] und [REP_PERIOD] nicht festgelegt sind, weisen Sie Standardwerte zu. Zum Entprellen. wenn (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { dev->timer.data = (lang) dev; dev->timer.function = Eingabewiederholungstaste; dev->rep[REP_DELAY] = 250; dev->rep[REP_PERIOD] = 33; } //Überprüfen Sie, ob die folgenden beiden Funktionen definiert sind. Wenn nicht, weisen Sie Standardwerte zu. wenn (!dev->getkeycode) dev->getkeycode = input_default_getkeycode; //Holen Sie sich den Schlüsselwert an der angegebenen Position, wenn (!dev->setkeycode) dev->setkeycode = input_default_setkeycode; //Setze den Schlüsselwert der angegebenen Position //Setze den Namen des Geräts in input_dev auf inputN //Wird input0 input1 input2 im Sysfs-Dateisystem angezeigt dev_set_name(&dev->dev, "input%ld", (vorzeichenloses Long) atomic_inc_return(&input_no) - 1); //Registrieren Sie die in input->dev enthaltene Gerätestruktur im Linux-Gerätemodell. Fehler = Gerät hinzufügen(&dev->dev); wenn (Fehler) Rückgabefehler; //Gerätepfad drucken und Debuginformationen ausgeben path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); printk(KERN_INFO "Eingabe: %s als %s\n", dev->name ? dev->name : „Nicht angegebenes Gerät“, Pfad ? Pfad : „N/A“); kfree(Pfad); Fehler = mutex_lock_interruptible(&input_mutex); if (Fehler) { Gerät_del(&dev->dev); Rückgabefehler; } //Input_dev zur verknüpften Liste input_dev_list hinzufügen (diese verknüpfte Liste enthält alle Eingabegeräte) list_add_tail(&dev->Knoten, &Eingabedev_Liste); Liste_für_jeden_Eintrag(Handler, &Eingabehandlerliste, Knoten) //Rufen Sie die Funktion input_attatch_handler() auf, um Handler und input_dev abzugleichen. //Diese Funktion ist sehr wichtig und wird später separat analysiert. input_attach_handler(Gerät, Handler); input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); gebe 0 zurück; } Für alle Eingabeereignisse verwendet der Kernel eine einheitliche Datenstruktur, um sie zu beschreiben. Diese Datenstruktur ist input_event /* * Die Veranstaltungsstruktur selbst */ Struktur input_event { struct timeval time; //<Der Zeitpunkt, zu dem das Eingabeereignis auftritt__u16 type; //<Der Typ des Eingabeereignisses__u16 code; //<Der Code unter dem Eingabeereignis type__s32 value; //<Der Wert von code}; Eingabeereignistyp –input_event.type /* * Veranstaltungsarten */ #define EV_SYN 0x00 //< Synchrones Ereignis #define EV_KEY 0x01 //< Tastenereignis #define EV_REL 0x02 //< Relative Koordinaten (z. B. Mausbewegung, meldet den Versatz relativ zur letzten Position) #define EV_ABS 0x03 //< Absolute Koordinaten (wie Touchscreen oder Joystick, die absolute Koordinatenpositionen melden) #define EV_MSC 0x04 //< Andere#define EV_SW 0x05 //< Schalter#define EV_LED 0x11 //< Taste/Gerätelicht#define EV_SND 0x12 //< Ton/Alarm#define EV_REP 0x14 //< Wiederholen#define EV_FF 0x15 //< Force Feedback#define EV_PWR 0x16 //< Stromversorgung#define EV_FF_STATUS 0x17 //< Force Feedback-Status#define EV_MAX 0x1f //< Maximale Anzahl von Ereignistypen und Bitmaskenunterstützung#define EV_CNT (EV_MAX+1) Das Linux-Eingabesubsystem stellt der Gerätetreiberschicht eine Funktion zum Melden von Eingabeereignissen zur Verfügung. Die Schnittstelle zum Melden von Eingabeereignissen sieht wie folgt aus: /* Eingabeereignisse des angegebenen Typs und Codes melden*/ void input_event(struct input_dev *dev, vorzeichenloser int-Typ, vorzeichenloser int-Code, int-Wert); /* Schlüsselwert melden */ statisches Inline-Void input_report_key (Struktur input_dev *dev, vorzeichenloser int-Code, int-Wert) { Eingabeereignis (Gerät, EV_KEY, Code, !!Wert); } /* Relative Koordinaten melden */ statisches Inline-Void input_report_rel (Struktur input_dev *dev, vorzeichenloser int-Code, int-Wert) { Eingabeereignis (Gerät, EV_REL, Code, Wert); } /* Absolute Koordinaten melden */ statisches Inline-Void input_report_abs (Struktur input_dev *dev, vorzeichenloser int-Code, int-Wert) { Eingabeereignis (Gerät, EV_ABS, Code, Wert); } ... Nachdem Sie das vom Eingabegerät generierte Eingabeereignis übermittelt haben, müssen Sie die folgende Funktion aufrufen, um das Eingabesubsystem zu benachrichtigen, dass das vollständige vom Gerät generierte Ereignis verarbeitet werden soll: void input_sync(Struktur input_dev *dev); [Beispiel] Treiberimplementierung - Meldung des Endes Die Synchronisierung input_sync() wird verwendet, um dem Eingabekernsubsystem mitzuteilen, dass die Meldung abgeschlossen ist. Im Touchscreen-Gerätetreiber läuft der gesamte Meldevorgang eines Klicks wie folgt ab: input_reprot_abs(input_dev,ABS_X,x); //x Koordinateinput_reprot_abs(input_dev,ABS_Y,y); //y Koordinateinput_reprot_abs(input_dev,ABS_PRESSURE,1); input_sync(input_dev); //Synchronisierung beendet [Beispiel] Tastenunterbrechungsprogramm //Button-Initialisierung static int __init button_init(void) {//Interrupt anfordern, wenn (request_irq (BUTTON_IRQ, button_interrupt, 0, "button", NUll)) Rückgabe –EBUSY; set_bit(EV_KEY,button_dev.evbit); //Unterstützt EV_KEY-Ereignis set_bit(BTN_0,button_dev.keybit); //Unterstützt zwei Tasten des Geräts set_bit(BTN_1,button_dev.keybit); // input_register_device(&button_dev); //Eingabegerät registrieren} /*Ereignis bei Tastenunterbrechung melden*/ Statischer void button_interrupt(int irq,void *dummy,struct pt_regs *fp) { input_report_key(&button_dev,BTN_0,inb(BUTTON_PORT0));//Lesen Sie den Wert des Registers BUTTON_PORT0 input_report_key(&button_dev,BTN_1,inb(BUTTON_PORT1)); Eingabesynchronisierung(&Schaltfläche_dev); } [Zusammenfassung] Das Eingabesubsystem ist immer noch ein Zeichengerätetreiber, aber die Codemenge ist deutlich reduziert. Das Eingabesubsystem muss nur zwei Aufgaben erfüllen: Initialisierung und Ereignisberichterstattung (hier in Linux wird dies durch Interrupts erreicht). Analyse der Ereignishandlerebene Datenstrukturdiagramm des Eingabesubsystems Input_handler-Struktur Struktur input_handle; /** * struct input_handler - implementiert eine der Schnittstellen für Eingabegeräte * @private: treiberspezifische Daten * @event: Eventhandler. Diese Methode wird vom Eingabekern aufgerufen mit * Interrupts deaktiviert und dev->event_lock Spinlock gehalten und so * es schläft möglicherweise nicht * @filter: ähnlich wie @event; trennt normale Eventhandler von * "Filter". * @match: wird aufgerufen, nachdem die Geräte-ID mit der ID-Tabelle des Handlers verglichen wurde * um eine differenzierte Zuordnung zwischen Gerät und Handler durchzuführen * @connect: wird aufgerufen, wenn ein Handler an ein Eingabegerät angeschlossen wird * @disconnect: trennt einen Handler vom Eingabegerät * @start: startet den Handler für den angegebenen Handle. Diese Funktion wird aufgerufen von * Eingabekern direkt nach der connect()-Methode und auch wenn ein Prozess * das ein Gerät "gepackt" hat, gibt es frei * @fops: Dateioperationen, die dieser Treiber implementiert * @minor: Anfang des Bereichs von 32 Minor-Geräten für diesen Treiber * kann bieten * @name: Name des Handlers, der in /proc/bus/input/handlers angezeigt werden soll * @id_table: Zeiger auf eine Tabelle mit input_device_ids, die dieser Treiber * handhaben * @h_list: Liste der mit dem Handler verknüpften Eingabe-Handles * @node: zum Platzieren des Treibers auf input_handler_list * * Eingabehandler werden an Eingabegeräte angeschlossen und erstellen Eingabehandles. * sind wahrscheinlich mehrere Handler an einem bestimmten Eingabegerät angeschlossen * zur gleichen Zeit. Alle erhalten ihre Kopie des Eingabeereignisses, das generiert wird von * das Gerät. * * Dieselbe Struktur wird zur Implementierung von Eingabefiltern verwendet. Eingabekern * ermöglicht die Ausführung von Filtern und übergibt Ereignisse nicht an reguläre Handler * wenn einer der Filter angibt, dass das Ereignis gefiltert werden soll (durch * Rückgabe von %true von ihrer Filter()-Methode). * * Beachten Sie, dass der Eingabekern Aufrufe von connect() und disconnect() serialisiert. * Methoden. */ Struktur input_handler { ungültig *privat; void (*Ereignis)(Struktur input_handle *Handle, vorzeichenloser int-Typ, vorzeichenloser int-Code, int-Wert); bool (*Filter)(Struktur input_handle *Handle, vorzeichenloser int-Typ, vorzeichenloser int-Code, int-Wert); bool (*match)(Struktur input_handler *handler, Struktur input_dev *dev); int (*verbinden)(Struktur input_handler *handler, Struktur input_dev *dev, const Struktur input_device_id *id); void (*trennen)(Struktur input_handle *handle); void (*start)(Struktur input_handle *handle); Konstantenstruktur file_operations *fops; int-Moll; Konstantenzeichen *Name; Konstantenstruktur input_device_id *id_table; Struktur list_head h_list; Struktur list_head-Knoten; }; [Beispiel] Nehmen Sie evdev_handler in evdev.c als Beispiel: statische Struktur input_handler evdev_handler = { .event = evdev_event, //<Meldet Eingabeereignisse an das System, das von der Lesemethode gelesen wird.connect = evdev_connect, //<Ruft Connect zum Erstellen auf, nachdem es mit input_dev abgeglichen wurde.disconnect = evdev_disconnect, .fops = &evdev_fops, //<Ereignisgerätedatei-Operationsmethode.minor = EVDEV_MINOR_BASE, //<Basiswert der Nebengerätenummer.name = "evdev", .id_table = evdev_ids, //<Übereinstimmungsregel}; Einfaches Beispiel für einen Eingabegerätetreiber Die Datei „documentation/input/input-programming.txt“ erläutert die wichtigsten Schritte zum Schreiben von Eingabegerätetreibern. Programmieren von Eingabetreibern ~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Erstellen eines Eingabegerätetreibers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1.0 Das einfachste Beispiel ~~~~~~~~~~~~~~~~~~~~~~~~ Hier ist ein sehr einfaches Beispiel für einen Eingabegerätetreiber. Das Gerät hat nur eine Taste und die Taste ist über den E/A-Port BUTTON_PORT erreichbar. Wenn gedrückt oder losgelassen wird, tritt ein BUTTON_IRQ auf. Der Treiber könnte so aussehen: #include <linux/input.h> #include <linux/module.h> #include <linux/init.h> #include <asm/irq.h> #include <asm/io.h> statische Struktur input_dev *button_dev; statischer irqreturn_t button_interrupt(int irq, void *dummy) { input_report_key(Schaltflächengerät, BTN_0, inb(BUTTON_PORT) & 1); Eingabesynchronisierung(Schaltflächengerät); IRQ_HANDLED zurückgeben; } statische int __init button_init(void) { int-Fehler; wenn (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) { printk(KERN_ERR "button.c: IRQ %d\n kann nicht zugewiesen werden", button_irq); Rückgabe -EBUSY; } button_dev = Eingabe_zuweisen_Gerät(); wenn (!button_dev) { printk(KERN_ERR "button.c: Nicht genügend Speicher\n"); Fehler = -ENOMEM; gehe zu err_free_irq; } button_dev->evbit[0] = BIT_MASK(EV_KEY); button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); Fehler = Eingaberegistergerät(Button_Device); if (Fehler) { printk(KERN_ERR "button.c: Gerät konnte nicht registriert werden\n"); gehe zu err_free_dev; } gebe 0 zurück; err_free_dev: Eingabefreies_Gerät(Schaltflächengerät); err_free_irq: free_irq(BUTTON_IRQ, button_interrupt); Rückgabefehler; } statischer void __exit button_exit(void) { Eingabe_unregister_device(Schaltflächengerät); free_irq(BUTTON_IRQ, button_interrupt); } modul_init(schaltfläche_init); modul_beenden(Schaltfläche_beenden); 1.1 Was das Beispiel bewirkt ~~~~~~~~~~~~~~~~~~~~~~~~~ Zuerst muss die Datei <linux/input.h> eingebunden werden, die eine Schnittstelle zum Eingabesubsystem. Dies stellt alle benötigten Definitionen bereit. In der Funktion _init, die entweder beim Laden des Moduls oder beim Beim Booten des Kernels greift er auf die erforderlichen Ressourcen zu (er sollte auch prüfen, für die Anwesenheit des Gerätes). Anschließend weist es mit input_allocate_device() eine neue Eingabegerätestruktur zu. und richtet Eingabebitfelder ein. Auf diese Weise teilt der Gerätetreiber dem anderen mit Teile der Eingabesysteme, was es ist - welche Ereignisse können generiert werden oder Wird von diesem Eingabegerät akzeptiert. Unser Beispielgerät kann nur EV_KEY generieren Typereignisse und davon nur der Ereigniscode BTN_0. Daher setzen wir nur diese zwei Bits. Wir hätten verwenden können set_bit(EV_KEY, button_dev.evbit); set_bit(BTN_0, button_dev.keybit); Auch, aber bei mehr als einzelnen Bits ist der erste Ansatz tendenziell kürzer. Anschließend registriert der Beispieltreiber die Eingabegerätestruktur durch den Aufruf Eingabe_Registergerät(&Button_Gerät); Dadurch wird die button_dev-Struktur zu den verknüpften Listen des Eingabetreibers hinzugefügt und ruft Gerätehandlermodule _connect-Funktionen auf, um ihnen einen neuen Eingang mitzuteilen Gerät ist erschienen. input_register_device() kann schlafen und muss daher darf nicht durch eine Unterbrechung oder bei gehaltenem Spinlock aufgerufen werden. Während der Nutzung ist die einzige genutzte Funktion des Treibers button_interrupt() die bei jedem Interrupt des Buttons ihren Zustand prüft und meldet über die Eingabeberichtsschlüssel() Aufruf des Eingabesystems. Es muss nicht geprüft werden, ob der Interrupt Die Routine meldet nicht zwei Ereignisse mit gleichem Wert (z. B. drücken, drücken) an das Eingabesystem, da die input_report_*-Funktionen prüfen, ob selbst. Dann gibt es noch die Eingabe_Sync () Rufen Sie an, um den Empfängern der Ereignisse mitzuteilen, dass wir einen vollständigen Bericht gesendet haben. Dies scheint im Ein-Knopf-Fall nicht wichtig zu sein, ist aber ziemlich wichtig zum Beispiel Mausbewegungen, bei denen die X- und Y-Werte nicht gesondert zu interpretieren, da dies eine andere Bewegung zur Folge hätte. 1.2 dev->open() und dev->close() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Falls der Treiber das Gerät immer wieder abfragen muss, weil es nicht es kommt ein Interrupt davon und das Polling ist zu teuer, um durchgeführt zu werden die ganze Zeit, oder wenn das Gerät eine wertvolle Ressource (z. B. Interrupt) verwendet, kann den Open- und Close-Callback verwenden, um zu wissen, wann die Abfrage beendet werden kann oder den Interrupt freigeben und wann er die Abfrage fortsetzen oder den Interrupt erfassen muss nochmal. Dazu würden wir unserem Beispieltreiber Folgendes hinzufügen: statische int button_open(Struktur input_dev *dev) { wenn (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) { printk(KERN_ERR "button.c: IRQ %d\n kann nicht zugewiesen werden", button_irq); Rückgabe -EBUSY; } gebe 0 zurück; } statischer void button_close(Struktur input_dev *dev) { freier_irq(IRQ_AMIGA_VERTB, button_interrupt); } statische int __init button_init(void) { ... button_dev->öffnen = Schaltfläche_öffnen; button_dev->schließen = button_schließen; ... } Beachten Sie, dass der Eingabekern die Anzahl der Benutzer für das Gerät verfolgt und stellt sicher, dass dev->open() nur aufgerufen wird, wenn sich der erste Benutzer verbindet an das Gerät und dass dev->close() aufgerufen wird, wenn der allerletzte trennt die Verbindung. Aufrufe beider Rückrufe werden serialisiert. Der open()-Callback sollte im Erfolgsfall eine 0 oder einen beliebigen Wert ungleich Null zurückgeben. im Fehlerfall. Der close()-Rückruf (der ungültig ist) muss immer erfolgreich sein. 1.3 Grundlegende Ereignistypen ~~~~~~~~~~~~~~~~~~~~~ Der einfachste Ereignistyp ist EV_KEY, der für Tasten und Schaltflächen verwendet wird. Die Meldung an das Eingabesystem erfolgt über: input_report_key(Struktur input_dev *dev, int-Code, int-Wert) Die zulässigen Codewerte (von 0 bis KEY_MAX) finden Sie unter linux/input.h. Der Wert wird als Wahrheitswert interpretiert, d. h. jeder Wert ungleich Null bedeutet Schlüssel gedrückt, Nullwert bedeutet Taste losgelassen. Der Eingabecode erzeugt nur Ereignisse falls der Wert anders ist als vorher. Neben EV_KEY gibt es zwei weitere grundlegende Ereignistypen: EV_REL und EV_ABS. Sie werden für relative und absolute Werte verwendet, die vom Gerät. Ein relativer Wert kann beispielsweise eine Mausbewegung in der X-Achse sein. Die Maus meldet es als relative Differenz zur letzten Position, weil es kein absolutes Koordinatensystem gibt, in dem gearbeitet werden kann. Absolut Events sind nämlich für Joysticks und Digitizer - Geräte, die in einem absolute Koordinatensysteme. Das Gerät mit EV_REL-Tasten zu verbinden ist genauso einfach wie mit EV_KEY, einfach Setzen Sie die entsprechenden Bits und rufen Sie den input_report_rel(Struktur input_dev *dev, int-Code, int-Wert) Funktion. Ereignisse werden nur für Werte ungleich Null generiert. Allerdings erfordert EV_ABS etwas besondere Aufmerksamkeit. Vor dem Aufruf input_register_device, müssen Sie zusätzliche Felder im input_dev ausfüllen Struktur für jede absolute Achse Ihres Geräts. Wenn unser Button-Gerät auch die ABS_X-Achse: button_dev.absmin[ABS_X] = 0; button_dev.absmax[ABS_X] = 255; button_dev.absfuzz[ABS_X] = 4; button_dev.absflat[ABS_X] = 8; Oder Sie sagen einfach: input_set_abs_params(button_dev, ABS_X, 0, 255, 4, 8); Diese Einstellung ist für die X-Achse eines Joysticks geeignet, mit einem Minimum von 0, maximal 255 (was der Joystick *erreichen* können muss, kein Problem, wenn es meldet manchmal mehr, aber es muss immer in der Lage sein, die min zu erreichen und max-Werte), mit Rauschen in den Daten bis zu +- 4 und mit einer flachen Mitte Position der Größe 8. Wenn Sie absfuzz und absflat nicht benötigen, können Sie sie auf Null setzen, was bedeutet dass das Ding präzise ist und immer genau in die Mittelposition zurückkehrt (falls vorhanden). 1.4 BITS_TO_LONGS(), BIT_WORD(), BIT_MASK() ~~~~~~~~~~~~~~~~~~~~~~~~~~ Diese drei Makros aus bitops.h helfen bei einigen Bitfeldberechnungen: BITS_TO_LONGS(x) - gibt die Länge eines Bitfeld-Arrays in Longs zurück für x Bits BIT_WORD(x) - gibt den Index im Array in Longs für Bit x zurück BIT_MASK(x) - gibt den Index in einer langen Form für Bit x zurück 1.5 Die Felder id* und name ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Der dev->name sollte vor der Registrierung des Eingabegeräts durch den Eingabe Gerätetreiber. Es ist eine Zeichenfolge wie 'Allgemeines Tastengerät' mit einem benutzerfreundlicher Name des Geräts. Die id*-Felder enthalten die Bus-ID (PCI, USB, ...), die Vendor-ID und die Geräte-ID des Gerätes. Die Bus-IDs sind in input.h definiert. Die Hersteller- und Geräte-IDs sind in pci_ids.h, usb_ids.h und ähnlichen Include-Dateien definiert. Diese Felder sollte vor der Registrierung vom Treiber des Eingabegeräts festgelegt werden. Das Feld idtype kann für spezifische Informationen zum Eingabegerät verwendet werden Treiber. Die ID- und Namensfelder können über die evdev-Schnittstelle an das Userland übergeben werden. 1.6 Die Felder keycode, keycodemax, keycodesize ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diese drei Felder sollten von Eingabegeräten verwendet werden, die über dichte Tastaturbelegungen verfügen. Der Schlüsselcode ist ein Array, das zum Zuordnen von Scancodes zu den Schlüsselcodes des Eingabesystems verwendet wird. Der keycode max sollte die Größe des Arrays enthalten und keycodesize die Größe jedes Eintrags darin (in Bytes). Der Userspace kann aktuelle Scancode-Keycode-Mappings abfragen und ändern mit EVIOCGKEYCODE- und EVIOCSKEYCODE-ioctls auf der entsprechenden evdev-Schnittstelle. Wenn ein Gerät alle 3 oben genannten Felder ausgefüllt hat, kann der Fahrer verlassen Sie sich auf die Standardimplementierung des Kernels zum Setzen und Abfragen von Schlüsselcodes Zuordnungen. 1.7 dev->getkeycode() und dev->setkeycode() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ getkeycode() und setkeycode() Callbacks ermöglichen Treibern das Überschreiben von Standard Vom Input-Core bereitgestellter Zuordnungsmechanismus für Keycode/Keycodesize/Keycodemax und implementieren Sie spärliche Keycode-Maps. 1.8 Tasten-Autorepeat ~~~~~~~~~~~~~~~~~~ ... ist einfach. Es wird vom input.c-Modul gehandhabt. Hardware-Autorepeat ist nicht verwendet, da es in vielen Geräten nicht vorhanden ist und selbst dort, wo es vorhanden ist vorhanden, es ist manchmal defekt (bei Tastaturen: Toshiba Notebooks). Um zu aktivieren Autorepeat für Ihr Gerät, setzen Sie einfach EV_REP in dev->evbit. Alle werden vom Eingabesystem verarbeitet. 1.9 Andere Ereignistypen, Behandlung von Ausgabeereignissen ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Die weiteren bisherigen Veranstaltungsarten sind: EV_LED – wird für die Tastatur-LEDs verwendet. EV_SND – wird für Tastatursignale verwendet. Sie sind sehr ähnlich zu beispielsweise Schlüsselereignissen, aber sie gehen in die andere Richtung - vom System zum Eingabegerätetreiber. Wenn Ihr Eingabegerät Damit der Treiber diese Ereignisse verarbeiten kann, muss er die entsprechenden Bits in evbit setzen, *und* auch die Callback-Routine: : button_dev->Ereignis = Schaltflächenereignis; int button_event(Struktur input_dev *dev, vorzeichenloser int-Typ, vorzeichenloser int-Code, int-Wert); { wenn (Typ == EV_SND und Code == SND_BELL) { outb(Wert, BUTTON_BELL); gebe 0 zurück; } Rückgabe -1; } Diese Callback-Routine kann von einem Interrupt oder einem BH aufgerufen werden (obwohl das ist keine Regel), darf daher nicht schlafen und darf nicht zu lange zum Fertigstellen brauchen. Eingabe-Programmierung.txt Der in diesem Beispiel bereitgestellte Beispielcode beschreibt ein Tastengerät. Die generierten Ereignisse werden über den BUTTON_PORT-Pin abgerufen. Beim Drücken/Loslassen wird BUTTON_IRQ ausgelöst. Nachfolgend sehen Sie den Quellcode des Treibers: #include <linux/input.h> #include <linux/module.h> #include <linux/init.h> #include <asm/irq.h> #include <asm/io.h> statische Struktur input_dev *button_dev; /*Eingabegerätestruktur*/ /*Funktion zur Interrupt-Behandlung*/ statischer irqreturn_t button_interrupt(int irq, void *dummy) { /*Schlüsselereignisse an das Eingabesubsystem melden*/ input_report_key(Schaltflächengerät, BTN_0, inb(BUTTON_PORT) & 1); /*Benachrichtigen Sie den Empfänger, dass ein Bericht gesendet wurde*/ Eingabesynchronisierung(Schaltflächengerät); IRQ_HANDLED zurückgeben; } /*Funktion laden*/ statische int __init button_init(void) { int-Fehler; /*Interrupt-Verarbeitungsfunktion anfordern*/ //Gibt 0 zurück, wenn erfolgreich, -INVAL, wenn ungültig, if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) { /*Wenn die Anwendung fehlschlägt, drucken Sie die Fehlermeldung aus*/ printk(KERN_ERR "button.c: IRQ %d\n kann nicht zugewiesen werden", button_irq); Rückgabe -EBUSY; } /* Eine Gerätestruktur zuweisen */ //Erstellt eine Geräteattributdatei unter sys/class/input/input-n button_dev = input_allocate_device(); if (!button_dev) { /*Beurteilen, ob die Zuweisung erfolgreich war*/ printk(KERN_ERR "button.c: Nicht genügend Speicher\n"); Fehler = -ENOMEM; gehe zu err_free_irq; } button_dev->evbit[0] = BIT_MASK(EV_KEY); /*Schlüsselinformationen festlegen*/ button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); Fehler = input_register_device(button_dev); /*Ein Eingabegerät registrieren*/ if (Fehler) { printk(KERN_ERR "button.c: Gerät konnte nicht registriert werden\n"); gehe zu err_free_dev; } gebe 0 zurück; /*Das Folgende ist die Fehlerbehandlung*/ err_free_dev: Eingabefreies_Gerät(Schaltflächengerät); err_free_irq: free_irq(BUTTON_IRQ, button_interrupt); Rückgabefehler; } /*Deinstallationsfunktion*/ statischer void __exit button_exit(void) { input_unregister_device(button_dev); /*Button-Gerät abmelden*/ free_irq(BUTTON_IRQ, button_interrupt);/*Gibt die vom Button belegte Interruptleitung frei*/ } modul_init(schaltfläche_init); modul_beenden(Schaltfläche_beenden); Dies kann man an diesem einfachen Beispiel sehen.
Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, er wird für jedermanns Studium hilfreich sein. Ich hoffe auch, dass jeder 123WORDPRESS.COM unterstützen wird. Das könnte Sie auch interessieren:
|
<<: Detaillierte Erklärung der beiden Modi des Router-Routings in Vue: Hash und Verlauf
>>: Eine kurze Diskussion über drei Methoden der asynchronen Replikation in MySQL 8.0
1|0MySQL (MariaDB) 1|11. Beschreibung Das Datenba...
Wenn Docker einen Container erstellt, verwendet e...
Inhaltsverzeichnis Fallstricke bei Zeitstempelver...
Einführung: Im Internet gibt es zahlreiche Inform...
Die JSON-Daten müssen im HTML-Format zurückgegeben...
In diesem Artikel wird der spezifische Code der J...
Vorwort Der Befehl apt-get ist ein Paketverwaltun...
Inhaltsverzeichnis 1. Array-Deduplizierung 2. Ded...
Vorwort: Integer ist einer der am häufigsten verw...
Eine At-Regel ist eine Deklaration, die Anweisung...
1. Fügen Sie package.json hinzu "Haupt"...
Trennen Sie Front- und Backend und lösen Sie domä...
Wenn wir lernen, Webseiten zu entwickeln, ist das...
Inhaltsverzeichnis Erläuterung des unidirektional...
Dieser Artikel gibt Ihnen den spezifischen Code v...