【Linux】輸入系統應用

# 前置知識

(1)輸入子系統分為三層,分別是事件處理層、核心層、設備驅動層;
(2)鼠標移動、鍵盤按鍵按下等輸入事件都需要通過設備驅動層→核心層→事件處理層→用戶空間,層層上報,直到應用程序;

?事件處理層

(1)事情處理層主要是負責將輸入事件上報到應用程序;對于向內核輸入子系統注冊的輸入設備,在sysfs中創建設備節點,應用程序通過操作設備節點來獲取輸入事件;
(2)事件處理層將輸入事件劃分為幾大類,比如:通用事件(event)、鼠標事件(mouse)、搖桿事件(js)等等,每個輸入類設備在注冊時需要指定自己屬于哪個類;
(3)通用事件是能和任何輸入設備匹配上的,意味著只要注冊一個輸入類設備就會sysfs就會創建對應的/dev/input/eventn設備節點;

核心層?

(1)核心層是起到承上啟下的作用,負責協調輸入事件在事件處理層和設備驅動層之間的傳遞;
(2)核心層負責管理事件處理層和設備驅動層,核心層提供相關的接口函數,事件處理層和設備驅動層都必須先向核心層注冊,然后才能工作;
(3)核心層負責設備驅動層和事件處理層的匹配問題,設備驅動根據硬件特性是各種各樣的,事件處理層也是分為好幾種類型,具體硬件驅動和哪一類或者哪幾類事件處理類型匹配,需要核心層去做判斷;

設備驅動層?

(1)設備驅動層分為兩大部分:硬件特性部分 + 核心層注冊接口;
(2)設備驅動層的硬件特性部分是具體操作硬件的,不同的硬件差異很大,且不屬于內核,這也是我們移植驅動的重點;
(3)核心層注冊接口:輸入子系統提供的輸入設備向內核注冊的接口,屬于內核代碼部分,我們需要理解和會使用這些接口,接口的使用都是模式化的,降低了編寫驅動的難度;

?示例

假設用戶程序直接訪問/dev/input/event0 設備節點,或者使用 tslib 訪問設備節點,數據的流程如下:

  1. APP 發起讀操作,若無數據則休眠;
  2. 用戶操作設備,硬件上產生中斷;
  3. 輸入系統驅動層對應的驅動程序處理中斷:讀取到數據,轉換為標準的輸入事件,向核心層匯報。所謂輸入事件就是一個“ struct input_event”結構體。
  4. 核心層可以決定把輸入事件轉發給上面哪個 handler 來處理:從 handler 的名字來看,它就是用來處輸入操作的。有多種 handler,比如: evdev_handler、 kbd_handler、 joydev_handler 等等。最常用的是 evdev_handler:它只是把 input_event 結構體保存在內核buffer 等, APP 來讀取時就原原本本地返回。它支持多個 APP 同時訪問輸入設備,每個 APP 都可以獲得同一份輸入事件。當 APP 正在等待數據時, evdev_handler 會把它喚醒,這樣 APP 就可以返回數據。

APP 對輸入事件的處理:

  1. APP 獲 得 數 據 的 方 法 有 2 種 : 直 接 訪 問 設 備 節 點 ( 比 如/dev/input/event0,1,2,...),或者通過 tslib、 libinput 這類庫來間接訪問設備節點。這些庫簡化了對數據的處理

?# input_dev結構體詳解?

struct input_dev {const char *name;	//設備名稱const char *phys;	//設備在系統中的物理路徑const char *uniq;	//設備唯一識別符struct input_id id;	//設備工D,包含總線ID(PCI 、 USB)、廠商工D,與 input handler 匹配的時會用到/**EV_SYN	同步事件*EV_KEY	按鍵事件*EV_REL	相對坐標事件:比如說鼠標*EV_ABS	絕對坐標事件:比如觸摸屏*EV_MSC	雜項事件*EV_SW	開關事件*EV_LED	指示燈事件*EV_SND	音效事件*EV_REP	重復按鍵事件*EV_FF	力反饋事件*EV_PWR	電源事件*EV_FF_STATUS	力反饋狀態事件*/unsigned long evbit[BITS_TO_LONGS(EV_CNT)];	//設備支持的事件類型unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];	//設備支持的具體的按鍵、按鈕事件unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; 	//戶設備支持的具體的相對坐標事件unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];	//設備支持的具體的絕對坐標事件unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];	//設備支持的具體的混雜事件unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];	//設備支持的具體的LED指示燈事件unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];	//戶設備支持的具體的音效事件unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];	//設備支持的具體的力反饋事件unsigned long swbit[BITS_TO_LONGS(SW_CNT)];	//設備支持的具體的開關事件unsigned int keycodemax;	//鍵盤碼表的大小unsigned int keycodesize;	//鍵盤碼表中的元素個數void *keycode;	//設備的鍵盤碼表//下面兩個是可選方法,用于配置和獲取鍵盤碼表int (*setkeycode)(struct input_dev *dev,unsigned int scancode, unsigned int keycode);int (*getkeycode)(struct input_dev *dev,unsigned int scancode, unsigned int *keycode);struct ff_device *ff;	//如果設備支持力反饋,則該成員將指向力反饋設備描述結構unsigned int repeat_key;	//保存上一個鍵值,用于實現軟件自動重復按鍵(用戶按住某個鍵不放)struct timer_list timer;	//用于軟件自動重復按鍵的定時器int sync;		//在上次同步事件(EV_SYNC)發生后沒有新事件產生,則被設置為 1 int abs[ABS_CNT];	//用于上報的絕對坐標當前值int rep[REP_MAX + 1];	//記錄自動重復按鍵參數的當前值unsigned long key[BITS_TO_LONGS(KEY_CNT)];		//舍反映設備按鍵、 按鈕的當前狀態unsigned long led[BITS_TO_LONGS(LED_CNT)];	//反映設備LED指示燈的當前狀態時unsigned long snd[BITS_TO_LONGS(SND_CNT)];	//反映設備音效的當前狀態會unsigned long sw[BITS_TO_LONGS(SW_CNT)];	//反映設備開關的當前狀態int absmax[ABS_CNT];	//絕對坐標的最大值int absmin[ABS_CNT];	//絕對坐標的最小值int absfuzz[ABS_CNT];	//絕對坐標的噪音值,變化小于該值的一半可忽略該事件int absflat[ABS_CNT];	//搖桿中心位置大小int absres[ABS_CNT];//提供以下4個設備驅動層的操作接口,根據具體的設備需求實現它們int (*open)(struct input_dev *dev);void (*close)(struct input_dev *dev);int (*flush)(struct input_dev *dev, struct file *file);//用于處理送到設備驅動層來的事件,很多事件在事件處理層被處理,但有的事件仍需送到設備驅動中.//如LED指示燈事件和音效事件,因為這些操作通常需要設備驅動執行(如點亮某個鍵盤指示燈) int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);//指向獨占該設備的輸入句柄( input handle ),通常設備驅動上報的事件會被分發到與設備//關聯的所有事件處理程序( input handler )中處理,但如果通過ioctl 的EVIOCGRAB命令//設置了獨占句柄,則上報事件只能被所設置的輸入句柄對應的事件處理程序處理struct input_handle *grab;spinlock_t event_lock;	//調用 event () 時需要使用該自旋鎖來實現互斥struct mutex mutex;	//用于串行化的訪問 open()、 close()和flush()等設備方法//記錄輸入事件處理程序(input handlers)調用設備open()方法的次數.保證設備open()方法是在//第一次調用 input_open_device()中被調用,設備close()方法在最后一次調用 input_close_device()中被調用unsigned int users;bool going_away;struct device dev;  //內嵌device結構struct list_head	h_list;	//與該設備相關的輸入句柄列表(struct input handle)struct list_head	node;	//掛接到input_dev_list鏈表上
};

# 輸入事件

輸入事件結構體

????????APP 可以通過read函數得到一系列的輸入事件,就是一個一個“ struct input_event”:


/** The event structure itself* Note that __USE_TIME_BITS64 is defined by libc based on* application's request to use 64 bit time_t.*/struct input_event {
#if (__BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL__)struct timeval time;
#define input_event_sec time.tv_sec      /* 秒 */
#define input_event_usec time.tv_usec    /* 微妙 */
#else__kernel_ulong_t __sec;
#if defined(__sparc__) && defined(__arch64__)unsigned int __usec;unsigned int __pad;
#else__kernel_ulong_t __usec;
#endif
#define input_event_sec  __sec
#define input_event_usec __usec
#endif__u16 type;                        /* 哪類事件 */__u16 code;                        /* 哪個事件 */__s32 value;                       /* 事件值 */
};
  • type: 表示哪類事件
EV_SYN用于事件間的分割標志。事件可能按時間或空間進行分割,就像在多點觸摸協議中的例子
EV_KEY用來描述鍵盤,按鍵或者類似鍵盤設備的狀態變化
EV_REL用來描述相對坐標軸上數值的變化,例如:鼠標向左方移動了5個單位
?EV_ABS用來描述相對坐標軸上數值的變化,例如:描述觸摸屏上坐標的值
EV_MSC當不能匹配現有的類型時,使用該類型進行描述
EV_SW用來描述具備兩種狀態的輸入開關
EV_LED用于控制設備上的LED燈的開和關
EV_SND用來給設備輸出提示聲音
EV_REP用于可以自動重復的設備(autorepeating)
EV_FF用來給輸入設備發送強制回饋命令。(震動?)
EV_PWR特別用于電源開關的輸入
EV_FF_STATUS用于接收設備的強制反饋狀態
  • code:?表示該類事件下的哪一個事件

比如對于 EV_KEY(按鍵)類事件,它表示鍵盤。鍵盤上有很多按鍵,比如數字鍵 1、 2、 3,字母鍵 A、 B、 C 里等

#define KEY_RESERVED            0
#define KEY_ESC                 1
#define KEY_1                   2
#define KEY_2                   3
#define KEY_3                   4
#define KEY_4                   5
#define KEY_5                   6
#define KEY_6                   7
#define KEY_7                   8
#define KEY_8                   9
#define KEY_9                   10
#define KEY_0                   11
...

對于觸摸屏,它提供的是絕對位置信息,有 X 方向、 Y 方向,還有壓力值

/** Absolute axes*/#define ABS_X			0x00
#define ABS_Y			0x01
#define ABS_Z			0x02
#define ABS_RX			0x03
#define ABS_RY			0x04
#define ABS_RZ			0x05
#define ABS_THROTTLE		0x06
#define ABS_RUDDER		0x07
#define ABS_WHEEL		0x08
#define ABS_GAS			0x09
#define ABS_BRAKE		0x0a
#define ABS_HAT0X		0x10
#define ABS_HAT0Y		0x11
#define ABS_HAT1X		0x12
#define ABS_HAT1Y		0x13
#define ABS_HAT2X		0x14
#define ABS_HAT2Y		0x15
#define ABS_HAT3X		0x16
#define ABS_HAT3Y		0x17
#define ABS_PRESSURE		0x18
#define ABS_DISTANCE		0x19
#define ABS_TILT_X		0x1a
#define ABS_TILT_Y		0x1b
#define ABS_TOOL_WIDTH		0x1c#define ABS_VOLUME		0x20
#define ABS_PROFILE		0x21#define ABS_MISC		0x28
  • value:表示事件值

對于按鍵,它的 value 可以是 0(表示按鍵被按下)、 1(表示按鍵被松開)、2(表示長按);

對于觸摸屏,它的 value 就是坐標值、壓力值

  • 事件之間的界線

APP 讀取數據時,可以得到一個或多個數據,比如一個觸摸屏的一個觸點會上報 X、 Y 位置信息,也可能會上報壓力值。
APP 怎么知道它已經讀到了完整的數據?

驅動程序上報完一系列的數據后,會上報一個“同步事件”,表示數據上報完畢。 APP 讀到“同步事件”時,就知道已經讀完了當前的數據。

同步事件也是一個 input_event 結構體,它的 type、 code、 value 三項都是 0

?使用命令讀取數據

調試輸入系統時,直接執行類似下面的命令,然后操作對應的輸入設備即可讀出數據:

hexdump /dev/input/event1

在開發板上執行上述命令之后,點擊按鍵或是點擊觸摸屏,就會打印如下信息:

????????比如第一行 type 為3,即 EV_ABS ;code 為 0x39 對應的?ABS_MT_TRACKING_ID,表示手指ID。手指 ID 才能唯一代表一個手指,槽的 ID 并不能代表一個手指。因為假如一個手指抬起,另外一個手指按下,這兩個手指的事件可能由同一個槽進行上報,但是手指 ID 肯定是不一樣的。

? ? ? ? 比如?type 為3,即 EV_ABS ;code 為 0x35對應的?ABS_MT_POSITION_X,code 為 0x36對應的?ABS_MT_POSITION_Y

????????上圖中還發現有 2 個同步事件:它的 type、 code、 value 都為 0。表示電容屏上報了 2 次完整的數據

注意:

當第一個手指移動時,會有如下事件

EV_ABS       ABS_MT_POSITION_X    000002ec            
EV_ABS       ABS_MT_POSITION_Y    00000526    
?
EV_SYN       SYN_REPORT           00000000 

此時沒有指定 ABS_MT_SLOT 事件和 ABS_MT_TRACKING_ID 事件,默認使用前面的值,因為此時只有一個手指。

當第二個手指按下時,會有如下事件

EV_ABS       ABS_MT_SLOT          00000001            
EV_ABS       ABS_MT_TRACKING_ID   00000001            
EV_ABS       ABS_MT_POSITION_X    00000470            
EV_ABS       ABS_MT_POSITION_Y    00000475       
?
EV_SYN       SYN_REPORT           00000000 
?

?

很簡單,第二個手指的事件,由另外一個槽進行上報。

當兩個手指同時移動時,會有如下事件

EV_ABS       ABS_MT_SLOT          00000000            
EV_ABS       ABS_MT_POSITION_Y    000004e0            
EV_ABS       ABS_MT_SLOT          00000001            
EV_ABS       ABS_MT_POSITION_X    0000046f            
EV_ABS       ABS_MT_POSITION_Y    00000414   
?
EV_SYN       SYN_REPORT           00000000 
?

通過指定槽,就可以清晰看到事件由哪個槽進行上報,從而就可以區分出兩個手指產生的事件。

當其中一個手指抬起時,會有如下事件

EV_ABS       ABS_MT_SLOT          00000000  
// 注意,ABS_MT_TRACKING_ID 的值為 -1
EV_ABS       ABS_MT_TRACKING_ID   ffffffff            
EV_ABS       ABS_MT_SLOT          00000001            
EV_ABS       ABS_MT_POSITION_Y    000003ee  
?
EV_SYN       SYN_REPORT           00000000  
?

當一個手指抬起時,ABS_MT_TRACKING_ID 事件的值為 -1,也就是十六進制的 ffffffff。通過槽事件,可以知道是第一個手指抬起了。

如果最后一個手指也抬起了,會有如下事件

EV_ABS       ABS_MT_TRACKING_ID   ffffffff     // 同步事件,不屬于觸摸事件 
EV_SYN       SYN_REPORT           00000000    

# 查看輸入設備節點

使用如下指令查看輸入設備節點:

cat /proc/bus/input/devices

  • I:id of the device(設備 ID)

????????該參數由結構體 struct input_id 來進行描述,驅動程序中會定義這樣的結構體:

/** IOCTLs (0x00 - 0x7f)*/struct input_id {__u16 bustype;__u16 vendor;__u16 product;__u16 version;
};
  • ?N:name of the device

????????設備名稱

  • P:physical path to the device in the system hierarchy

????????系統層次結構中設備的物理路徑

  • S:sysfs path

????????位于 sys 文件系統的路徑

  • U:unique identification code for the device(if device has it)

????????設備的唯一標識碼

  • H:list of input handles associated with the device

????????與設備關聯的輸入句柄列表

  • B:bitmaps(位圖)

????????PROP:device properties and quirks(設備屬性)

????????EV:types of events supported by the device(設備支持的事件類型)

????????KEY:keys/buttons this device has(此設備具有的鍵/按鈕)

????????MSC:miscellaneous events supported by the device(設備支持的其他事件)

????????LED:leds present on the device(設備上的指示燈)

注意:

????????值得注意的是 B 位圖,比如上圖中“ B: EV=b”用來表示該設備支持哪類輸入事件。 b 的二進制是 1011, bit0、 1、 3 為 1,表示該設備支持 0、 1、 3 這三類事件,即 EV_SYN、 EV_KEY、 EV_ABS

????????再舉一個例子,“ B: ABS=2658000 3”這是 2 個 32 位的數 字: 0x2658000、 0x3, 高位在 前低 位在 后, 組成一 個 64 位 的數字 : “ 0x2658000,00000003”,數值為 1 的位有: 0、 1、 47、 48、 50、 53、 54,即: 0、 1、 0x2f、 0x30、 0x32、 0x35、 0x36,對應以下這些宏

????????即 這 款 輸 入 設 備 支 持 上 述 的 ABS_X 、 ABS_Y 、 ABS_MT_SLOT 、ABS_MT_TOUCH_MAJOR 、 ABS_MT_WIDTH_MAJOR 、 ABS_MT_POSITION_X 、ABS_MT_POSITION_Y 這些絕對位置事件
?

# APP訪問硬件

阻塞、非阻塞

#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <poll.h>
#include <signal.h>int fd = 0; 
char *ev_names[] = {"EV_SYN ","EV_KEY ","EV_REL ","EV_ABS ","EV_MSC ","EV_SW","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","EV_LED ","EV_SND ","NULL ","EV_REP ","EV_FF","EV_PWR ",};static void print_ev_info(void)
{int len;int err;struct input_id id;unsigned char byte;int bit;unsigned int evbit[5];err = ioctl(fd, EVIOCGID, &id);if(err < 0){perror("ioctl:");return;}printf("bustype = 0x%x\n", id.bustype );printf("vendor	= 0x%x\n", id.vendor  );printf("product = 0x%x\n", id.product );printf("version = 0x%x\n", id.version );len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit);if (len > 0 && len <= sizeof(evbit)){printf("support ev type: ");for (int i = 0; i < len; i++){byte = ((unsigned char *)evbit)[i];for (bit = 0; bit < 8; bit++){if (byte & (1<<bit)) {printf("%s ", ev_names[i*8 + bit]);}}}printf("\n");}
}static void read_block_nblock(void)
{int len = 0;struct input_event ev;while(true){len = read(fd, &ev, sizeof(struct input_event));if(len == sizeof(struct input_event)){printf("**********************************\r\n");printf("time.tv_sec = %ld\r\n", ev.time.tv_sec);printf("time.tv_usec = %ld\r\n", ev.time.tv_usec);printf("type = %d\r\n", ev.type);printf("code = %d\r\n", ev.code);printf("value = %d\r\n", ev.value);}else{printf("read error %d\r\n", len);}}
}int main(int argc, char **argv)
{if (argc < 2){printf("Usage: %s <dev> [noblock]\n", argv[0]);return -1;}if((argc == 3) && (!strcmp(argv[2], "noblock"))){fd = open(argv[1], O_RDWR | O_NONBLOCK);}else{fd = open(argv[1], O_RDWR);}if(fd < 0){perror("open:");return -1;}print_ev_info();read_block_nblock();//read_poll();//read_sync();close(fd);
}
  • 使用如下指令進行阻塞式詢問
 ./input_demo /dev/input/event1

  • ?使用如下指令進行非阻塞式詢問
./input_demo /dev/input/event1 noblock

POLL/SELECT 方式?

API

  • poll 函數

????????poll 是在指定時間內論詢一定數量的文件描述符,來測試其中是否有就緒的

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

函數參數:?

其中,struct?pollfd 定義為:


struct pollfd {/* 文件描述符 */int   fd;         /* file descriptor *//**監聽事件POLLIN 有數據可讀POLLPRI 等同于 POLLIN POLLOUT 可以寫數據POLLERR 發生了錯誤POLLHUP 掛起POLLNVAL 無效的請求,一般是 fd 未 openPOLLRDNORM 等同于 POLLINPOLLRDBAND Priority band data can be read,有優先級較較高的“ band data”可讀
Linux 系統中很少使用這個事件POLLWRNORM 等同于 POLLOUTPOLLWRBAND Priority data may be written.*/short events;     /* requested events *//**接收到的事件,參數同上*/short revents;    /* returned events */
};

返回值:??

  • 非負值:成功
  • 0:超時
  • -1:錯誤

程序代碼:

#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <poll.h>
#include <signal.h>int fd = 0; 
char *ev_names[] = {"EV_SYN ","EV_KEY ","EV_REL ","EV_ABS ","EV_MSC ","EV_SW","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","EV_LED ","EV_SND ","NULL ","EV_REP ","EV_FF","EV_PWR ",};static void print_ev_info(void)
{int len;int err;struct input_id id;unsigned char byte;int bit;unsigned int evbit[5];err = ioctl(fd, EVIOCGID, &id);if(err < 0){perror("ioctl:");return;}printf("bustype = 0x%x\n", id.bustype );printf("vendor	= 0x%x\n", id.vendor  );printf("product = 0x%x\n", id.product );printf("version = 0x%x\n", id.version );len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit);if (len > 0 && len <= sizeof(evbit)){printf("support ev type: ");for (int i = 0; i < len; i++){byte = ((unsigned char *)evbit)[i];for (bit = 0; bit < 8; bit++){if (byte & (1<<bit)) {printf("%s ", ev_names[i*8 + bit]);}}}printf("\n");}
}static void read_poll(void)
{int ret;struct input_event ev;struct pollfd fds[1];nfds_t nfds = 1;while (true){fds[0].fd = fd;fds[0].events = POLLIN;fds[0].revents = 0;ret = poll(fds, nfds, 5000);if(ret > 0){if(fds->revents == POLLIN){while(read(fd, &ev, sizeof(struct input_event)) == sizeof(struct input_event)){printf("**********************************\r\n");printf("time.tv_sec = %ld\r\n", ev.time.tv_sec);printf("time.tv_usec = %ld\r\n", ev.time.tv_usec);printf("type = %d\r\n", ev.type);printf("code = %d\r\n", ev.code);printf("value = %d\r\n", ev.value);}}}else if(ret == 0){printf("read out %d\r\n", ret);}else{perror("read:");}}}int main(int argc, char **argv)
{if (argc < 2){printf("Usage: %s <dev> [noblock]\n", argv[0]);return -1;}if((argc == 3) && (!strcmp(argv[2], "noblock"))){fd = open(argv[1], O_RDWR | O_NONBLOCK);}else{fd = open(argv[1], O_RDWR);}if(fd < 0){perror("open:");return -1;}print_ev_info();//read_block_nblock();read_poll();//read_sync();close(fd);
}
  • ?使用如下指令進行詢問
 ./input_demo /dev/input/event1 noblock

異步通知方式?

步驟:

  • 編寫信號處理函數:
static void sig_func(int sig){int val;read(fd, &val, 4);printf("get button : 0x%x\n", val); 
}
  • 注冊信號處理函數:
signal(SIGIO, sig_func);

?Linux 系統中也有很多信號,在 Linux 內核源文件 include\uapi\asmgeneric\signal.h 中,有很多信號的宏定義:

  • 打開驅動:
fd = open(argv[1], O_RDWR);
  • 把進程 ID 告訴驅動:
fcntl(fd, F_SETOWN, getpid());
  • 使能驅動的 FASYNC 功能:
flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | FASYNC);

API:

  • signal函數

????????設置某一信號的對應動作

typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);

函數參數:

參數描述
signum指明了所要處理的信號類型,它可以取除了SIGKILL和SIGSTOP外的任何一種信號
handler描述了與信號關聯的動作
  • fcntl

????????根據文件描述詞來操作文件的特性

int fcntl(int fd, int cmd);int fcntl(int fd, int cmd, long arg);         int fcntl(int fd, int cmd, struct flock *lock);

fcntl函數有5種功能:

  1.  復制一個現有的描述符(cmd=F_DUPFD).
  2.  獲得/設置文件描述符標記(cmd=F_GETFD或F_SETFD).
  3. ????獲得/設置文件狀態標記(cmd=F_GETFL或F_SETFL).
  4. ????獲得/設置異步I/O所有權(cmd=F_GETOWN或F_SETOWN).
  5. ????獲得/設置記錄鎖(cmd=F_GETLK,F_SETLK或F_SETLKW).

?cmd?選項:

  1. ?F_GETFD?????取得與文件描述符fd聯合close-on-exec標志
  2. ?F_SETFD?????設置close-on-exec旗標。該旗標以參數arg的FD_CLOEXEC位決定
  3. ?F_GETFL?????取得fd的文件狀態標志,如同下面的描述一樣(arg被忽略) ?
  4. ?F_SETFL?????設置給arg描述符狀態標志,可以更改的幾個標志是:O_APPEND,?O_NONBLOCK,O_SYNC和O_ASYNC(O_NONBLOCK :非阻塞I/O;如果read(2)調用沒有可讀取的數據,或者如果write(2)操作將阻塞,read或write調用返回-1和EAGAIN錯誤 ;? O_APPEND: 強制每次寫(write)操作都添加在文件大的末尾,相當于open(2)的O_APPEND標志 ;? O_DIRECT?:? 最小化或去掉reading和writing的緩存影響.系統將企圖避免緩存你的讀或寫的數據.? 如果不能夠避免緩存,那么它將最小化已經被緩存了的數 據造成的影響.如果這個標志用的不夠好,將大大的降低性能 ; O_ASYNC: 當I/O可用的時候,允許SIGIO信號發送到進程組,例如:當有數據可以讀的時候)
  5. ?F_GETOWN?取得當前正在接收SIGIO或者SIGURG信號的進程id或進程組id,進程組id返回成負值(arg被忽略)?
  6. ?F_SETOWN?設置將接收SIGIO和SIGURG信號的進程id或進程組id,進程組id通過提供負值的arg來說明,否則,arg將被認為是進程id

?程序代碼:

#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <poll.h>
#include <signal.h>int fd = 0; 
char *ev_names[] = {"EV_SYN ","EV_KEY ","EV_REL ","EV_ABS ","EV_MSC ","EV_SW","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","NULL ","EV_LED ","EV_SND ","NULL ","EV_REP ","EV_FF","EV_PWR ",};static void print_ev_info(void)
{int len;int err;struct input_id id;unsigned char byte;int bit;unsigned int evbit[5];err = ioctl(fd, EVIOCGID, &id);if(err < 0){perror("ioctl:");return;}printf("bustype = 0x%x\n", id.bustype );printf("vendor	= 0x%x\n", id.vendor  );printf("product = 0x%x\n", id.product );printf("version = 0x%x\n", id.version );len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit);if (len > 0 && len <= sizeof(evbit)){printf("support ev type: ");for (int i = 0; i < len; i++){byte = ((unsigned char *)evbit)[i];for (bit = 0; bit < 8; bit++){if (byte & (1<<bit)) {printf("%s ", ev_names[i*8 + bit]);}}}printf("\n");}
}static void my_sig_handler(int sig)
{struct input_event ev;if(sig == SIGIO){while(read(fd, &ev, sizeof(struct input_event)) == sizeof(struct input_event)){printf("**********************************\r\n");printf("time.tv_sec = %ld\r\n", ev.time.tv_sec);printf("time.tv_usec = %ld\r\n", ev.time.tv_usec);printf("type = %d\r\n", ev.type);printf("code = %d\r\n", ev.code);printf("value = %d\r\n", ev.value);}}
}static void read_sync(void)
{int flags;int count = 0;/* 注冊信號處理函數 */signal(SIGIO, my_sig_handler);/* 把APP的進程號告訴驅動程序 */fcntl(fd, F_SETOWN, getpid());/* 使能"異步通知" */flags = fcntl(fd, F_GETFL);fcntl(fd, F_SETFL, flags | FASYNC);while (true){printf("main loop count = %d\n", count++);sleep(2);}}int main(int argc, char **argv)
{if (argc < 2){printf("Usage: %s <dev> [noblock]\n", argv[0]);return -1;}if((argc == 3) && (!strcmp(argv[2], "noblock"))){fd = open(argv[1], O_RDWR | O_NONBLOCK);}else{fd = open(argv[1], O_RDWR);}if(fd < 0){perror("open:");return -1;}print_ev_info();//read_block_nblock();//read_poll();read_sync();close(fd);
}
  • ?使用如下指令進行詢問
./input_demo /dev/input/event1

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/717006.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/717006.shtml
英文地址,請注明出處:http://en.pswp.cn/news/717006.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【八】【SQL】子查詢和where

顯示與SMITH同一部門的員工 mysql> select *from emp where enameSMITH; ----------------------------------------------------------------------- | empno | ename | job | mgr | hiredate | sal | comm | deptno | --------------------------------…

Python調用C,python call c,pybind11

文章目錄 前言1.將pybind11 clone至當前項目下的extern目錄下2.在CmakeLists.txt中將pybind11項目包含3.接口cpp文件格式4.編譯5.導入Python使用6.性能比較pybind11項目地址 前言 通過https://github.com/pybind/pybind11項目實現Python調用C/C代碼 實現步驟 1.將pybind11 cl…

騰訊云4核8G服務器申請費用多少?性能如何?支持幾個人?

騰訊云4核8G服務器支持多少人在線訪問&#xff1f;支持25人同時訪問。實際上程序效率不同支持人數在線人數不同&#xff0c;公網帶寬也是影響4核8G服務器并發數的一大因素&#xff0c;假設公網帶寬太小&#xff0c;流量直接卡在入口&#xff0c;4核8G配置的CPU內存也會造成計算…

大數據報告檢測到風險等級太高是怎么回事呢?

隨著金融風控越來越多元化&#xff0c;大數據作為新興的技術被運用到貸前風控中去了&#xff0c;不少人也了解過自己的大數據&#xff0c;但是由于相關知識不足&#xff0c;看不懂報告&#xff0c;在常見的問題中&#xff0c;大數據檢測到風險等級太高是怎么回事呢?小易大數據…

《javascript高級程序設計》學習筆記 | 21.2.錯誤處理

關注[前端小謳]&#xff0c;閱讀更多原創技術文章 錯誤處理 相關代碼 → try/catch 語句 ES3 新增了try/catch語句&#xff0c;基本語法與 Java 中的 try/catch 一樣 try {// 可能出錯的代碼const a 3;a 4; } catch (error) {// 出錯時執行的代碼console.log("An er…

vsomeip源碼剖析--00環境搭建

環境 Win11 WSL2 Ubuntu22.04安裝依賴 sudo apt-get install cmake sudo apt-get install libboost-system1.71-dev libboost-thread1.71-dev libboost-log1.71-dev源碼編譯 獲取源碼 https://github.com/COVESA/vsomeip.git編譯 cd vsomeip mkdir build cd build// 一般…

漫漫數學之旅035

文章目錄 經典格言數學習題古今評注名人小傳 - 黎勒?笛卡爾 經典格言 完美的數和完美的人是同樣罕見的。——黎勒?笛卡爾&#xff08;Ren Descrates&#xff09; 完美的數和完美的人都是極為罕見的。這句話表達了一個哲學觀點&#xff0c;即無論是在數學領域還是人類自身&am…

Spring框架相關問題

RabbitMQ相關問題 Spring框架相關問題 一、Spring容器中的Bean是線程安全的嗎&#xff1f;二、如何保證Spring容器中的Bean是線程安全的呢&#xff1f;三、什么情況下會觸發Spring事務回滾&#xff1f;四、如果事務方法拋出IOException&#xff0c;是否會觸發Spring事務回滾&a…

Zookeeper學習2:原理、常用腳本、選舉機制、監聽器

文章目錄 原理選舉機制&#xff08;重點&#xff09;情況1&#xff1a;正常啟動集群情況2&#xff1a;集群啟動完&#xff0c;中途有機器掛了 監聽器客戶端向服務端寫入數據客戶端向服務端Leader節點寫入客戶端向服務端Follower節點寫入 Paxos算法&#xff08;每個節點都可以提…

AMDGPU KFD Test 編譯使用

ROCT-Thunk-Interface是一個用于在ROCm軟件堆棧中提供設備無關性的層。它是ROCm的一部分,允許不同的硬件平臺(如AMD GPU和Intel CPU)使用相同的API進行計算。 要安裝ROCT-Thunk-Interface,首先需要創建一個新的目錄,并進入該目錄: mkdir rocm-build cd rocm-build然后,…

ng : 無法將ng項識別為 cmdlet、函數、腳本文件或可運行程序的名稱

ng : 無法將“ng”項識別為 cmdlet、函數、腳本文件或可運行程序的名稱”&#xff0c;出現這種錯誤&#xff0c;那說明你angular-cli沒有下載所以環境變量里沒有相應的東西 1、需要在cmd里輸入npm install -g angular/cli 2、之后運行angular命令時還可能出現這種錯誤 “ng : …

ruoyi 圖片等文件資源讀取

老是忘&#xff0c;記錄一下 ResourcesConfig 文件下 /** 本地文件上傳路徑 */ registry.addResourceHandler(Constants.RESOURCE_PREFIX "/**").addResourceLocations("file:" RuoYiConfig.getProfile() "/"); /*** 資源映射路徑 前綴*/ …

kafka消費者重平衡是什么?怎么避免?

消費者重平衡是指主題下的分區怎么分配給消費者的過程。下面這個圖可以看出該過程&#xff1a;原來有2個消費者&#xff0c;3個分區&#xff0c;其中一個消費者肯定就的處理2個分區了。那么當新加入消費者時&#xff0c;則每個消費者就只處理一個分區了。處理這個分區過程的叫協…

詳解Nacos注冊中心的使用

文章目錄 1、安裝2、服務注冊2.1、引入依賴2.2、配置nacos地址2.3、重啟 3、服務分級存儲模型3.1、給user-service配置集群3.2、同集群優先的負載均衡 4、權重配置5、環境隔離5.1、創建namespace5.2、配置namespace 6、Nacos與Eureka的區別7、代碼免費分享 ?&#x1f343;作者…

首例以“冠狀病毒”為主題的勒索病毒,篡改系統MBR

前言概述 2020年勒索病毒攻擊仍然是網絡安全的最大威脅&#xff0c;在短短三個月的時間里&#xff0c;已經出現了多款新型的勒索病毒&#xff0c;關于2020年勒索病毒攻擊新趨勢&#xff0c;可以閱讀筆者寫的上一篇文章&#xff0c;里面有詳細的分析&#xff0c;從目前觀察到的…

Linux 學習筆記(9)

九、 運行級別 1 、 Linux 系統的運行級別 (runlevel) Linux 系統有 7 個運行級別&#xff0c; Linux 系統任何時候都運行在一個指定的運行級別上&#xff0c;不同的運行級 別所運行的程序和服務不盡相同&#xff0c;所要完成的工作和要達到的目的也不相同 運行級別…

RH850P1X芯片學習筆記-Generic Timer Module -ATOM

文章目錄 ARU-connected Timer Output Module (ATOM)OverviewGLOBAL CHANNEL CONTROL BLOCK ATOM Channel architectureATOM Channel modesSOMP-Signal Output Mode PWMSOMP - ARUSOMC-Signal Output Mode CompareSOMC - ARUSOMC – COMPARE COMMANDSOMC – OUTPUT ACTIONATOM …

Python縮進規則

Python的縮進規則是Python語法中非常重要的一部分&#xff0c;也是Python語言獨特的特點之一。在Python中&#xff0c;縮進被用來表示代碼塊的層次結構&#xff0c;而不是像其他語言一樣使用大括號或關鍵詞。這種縮進規則使得Python代碼更加簡潔、易讀、易于理解&#xff0c;同…

python模塊百科_操作系統接口_os【一】

python模塊百科_操作系統接口_os【一】 os --- 多種操作系統接口一、相關模塊1.1 os.path 文件路徑1.2 fileinput 文件讀取1.3 tempfile 臨時文件和目錄1.4 shutil 高級文件和目錄1.5 platform 操作系統底層模塊 二、關于函數適用性的說明2.1 與操作系統相同的接口2.2 支持字節…

Git版本管理常用指令

Git常用命令 一、基本指令二、本地倉庫管理三、遠程倉庫管理四、分支管理五、儲藏區六、標簽管理一、基本指令 查看Git安裝版本:git --version 查看log指令的幫助信息:git log --help 配置Git用戶名:git config --global user.name “xxxxx” 配置Git郵箱: git config --…