# 前置知識
(1)輸入子系統分為三層,分別是事件處理層、核心層、設備驅動層;
(2)鼠標移動、鍵盤按鍵按下等輸入事件都需要通過設備驅動層→核心層→事件處理層→用戶空間,層層上報,直到應用程序;
?事件處理層
(1)事情處理層主要是負責將輸入事件上報到應用程序;對于向內核輸入子系統注冊的輸入設備,在sysfs中創建設備節點,應用程序通過操作設備節點來獲取輸入事件;
(2)事件處理層將輸入事件劃分為幾大類,比如:通用事件(event)、鼠標事件(mouse)、搖桿事件(js)等等,每個輸入類設備在注冊時需要指定自己屬于哪個類;
(3)通用事件是能和任何輸入設備匹配上的,意味著只要注冊一個輸入類設備就會sysfs就會創建對應的/dev/input/eventn設備節點;
核心層?
(1)核心層是起到承上啟下的作用,負責協調輸入事件在事件處理層和設備驅動層之間的傳遞;
(2)核心層負責管理事件處理層和設備驅動層,核心層提供相關的接口函數,事件處理層和設備驅動層都必須先向核心層注冊,然后才能工作;
(3)核心層負責設備驅動層和事件處理層的匹配問題,設備驅動根據硬件特性是各種各樣的,事件處理層也是分為好幾種類型,具體硬件驅動和哪一類或者哪幾類事件處理類型匹配,需要核心層去做判斷;
設備驅動層?
(1)設備驅動層分為兩大部分:硬件特性部分 + 核心層注冊接口;
(2)設備驅動層的硬件特性部分是具體操作硬件的,不同的硬件差異很大,且不屬于內核,這也是我們移植驅動的重點;
(3)核心層注冊接口:輸入子系統提供的輸入設備向內核注冊的接口,屬于內核代碼部分,我們需要理解和會使用這些接口,接口的使用都是模式化的,降低了編寫驅動的難度;
?示例
假設用戶程序直接訪問/dev/input/event0 設備節點,或者使用 tslib 訪問設備節點,數據的流程如下:
- APP 發起讀操作,若無數據則休眠;
- 用戶操作設備,硬件上產生中斷;
- 輸入系統驅動層對應的驅動程序處理中斷:讀取到數據,轉換為標準的輸入事件,向核心層匯報。所謂輸入事件就是一個“ struct input_event”結構體。
- 核心層可以決定把輸入事件轉發給上面哪個 handler 來處理:從 handler 的名字來看,它就是用來處輸入操作的。有多種 handler,比如: evdev_handler、 kbd_handler、 joydev_handler 等等。最常用的是 evdev_handler:它只是把 input_event 結構體保存在內核buffer 等, APP 來讀取時就原原本本地返回。它支持多個 APP 同時訪問輸入設備,每個 APP 都可以獲得同一份輸入事件。當 APP 正在等待數據時, evdev_handler 會把它喚醒,這樣 APP 就可以返回數據。
APP 對輸入事件的處理:
- 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種功能:
- 復制一個現有的描述符(cmd=F_DUPFD).
- 獲得/設置文件描述符標記(cmd=F_GETFD或F_SETFD).
- ????獲得/設置文件狀態標記(cmd=F_GETFL或F_SETFL).
- ????獲得/設置異步I/O所有權(cmd=F_GETOWN或F_SETOWN).
- ????獲得/設置記錄鎖(cmd=F_GETLK,F_SETLK或F_SETLKW).
?cmd?選項:
- ?F_GETFD?????取得與文件描述符fd聯合close-on-exec標志
- ?F_SETFD?????設置close-on-exec旗標。該旗標以參數arg的FD_CLOEXEC位決定
- ?F_GETFL?????取得fd的文件狀態標志,如同下面的描述一樣(arg被忽略) ?
- ?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信號發送到進程組,例如:當有數據可以讀的時候)
- ?F_GETOWN?取得當前正在接收SIGIO或者SIGURG信號的進程id或進程組id,進程組id返回成負值(arg被忽略)?
- ?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