新聞中心
在Linux系統(tǒng)中,設備驅動是一個非常重要的組件。同時,作為一個Linux開發(fā)者,熟練掌握設備驅動的編寫是非常必要的。在本文中,我們將討論Linux設備驅動的一種實現:按鍵輸入。

為什么需要按鍵輸入驅動?
在現代計算機系統(tǒng)中,特別是在一些嵌入式設備中,按鍵是一個極其重要的輸入設備。按鍵可以用于各種目的,例如啟動或停止某個應用程序,或者執(zhí)行某個操作。因此,對于我們的應用程序或操作系統(tǒng)來說,需要一個可靠的驅動程序來處理按鍵輸入事件。
按鍵輸入驅動的實現
Linux內核為我們提供了一些內核接口和數據結構以支持設備驅動的編寫。最常用的數據結構是input_dev結構體,如下所示:
struct input_dev {
const char *name;
const struct input_id *id;
unsigned long evbit[NBITS(EV_CNT)];
unsigned long keybit[NBITS(KEY_CNT)];
unsigned long ledbit[NBITS(LED_CNT)];
unsigned long mscbit[NBITS(MSC_CNT)];
unsigned long relbit[NBITS(REL_CNT)];
unsigned long abit[NBITS(ABS_CNT)];
unsigned long ffbbit[NBITS(FF_CNT)];
unsigned long propbit[NBITS(INPUT_PROP_CNT)];
unsigned long quirk;
struct input_absinfo *absinfo;
struct device *dev;
struct input_mt_slot *mt;
};
在實際的按鍵輸入驅動中,我們需要完成以下任務:
1. 定義input_dev結構體:我們需要指定輸入設備的名稱,以及支持的輸入事件類型和輸入事件所對應的數據格式。
2. 注冊輸入設備:我們需要注冊輸入設備和它的事件處理函數。
3. 處理輸入事件:在輸入事件被注冊之后,我們需要編寫一個輸入事件處理函數來處理它。這個函數會被內核自動調用,以處理輸入設備生成的輸入事件。
在實現按鍵輸入驅動之前,我們需要先了解一下Linux內核輸入子系統(tǒng)提供的常見輸入事件類型:
EV_SYN:輸入事件的同步信號。
EV_KEY:按鍵事件。
EV_REL:相對運動事件(鼠標滾輪)。
EV_ABS:絕對運動事件(鼠標位置、觸摸屏,等等)。
EV_MSC:描述設備的狀態(tài)或模式。
EV_SW:開關事件。
EV_LED:LED燈的開關事件。
EV_SND:音頻事件。
EV_REP:用于自動重復事件的時間設置。
EV_FF:力反饋事件。這可以用于游戲手柄的振動反饋等功能。
在按鍵這里,我們將使用EV_KEY事件。EV_KEY事件和其他事件一樣,由一個值、一個代碼和一個狀態(tài)組成。值是事件類型,代碼是鍵碼,而狀態(tài)則表示按鍵是按下還是釋放。
我們現在來看一下實現按鍵輸入驅動的代碼:
// 定義input_dev結構體
static struct input_dev *input_device;
static int button_init(void)
{
int ret;
// 在內核中注冊一個新的輸入設備
input_device = input_allocate_device();
if (!input_device) {
printk(KERN_ERR “input_allocate_device fled\n”);
return -ENOMEM;
}
// 指定輸入設備的名稱
input_device->name = “button”;
// 指定輸入設備支持的事件類型和數據格式
input_set_capability(input_device, EV_KEY, N_0);
// 注冊輸入設備
ret = input_register_device(input_device);
if (ret) {
printk(KERN_ERR “input_register_device() fled\n”);
input_free_device(input_device);
}
return ret;
}
static void button_exit(void)
{
// 撤銷輸入設備的注冊
input_unregister_device(input_device);
// 釋放輸入設備
input_free_device(input_device);
}
static int __init button_init_module(void)
{
return button_init();
}
static void __exit button_cleanup_module(void)
{
button_exit();
}
// 處理輸入事件的函數
static irqreturn_t button_handler(int irq, void *dev_id)
{
struct input_dev *dev = dev_id;
// 讀取當前按鍵狀態(tài)
int value = gpio_get_value(GPIO_BUTTON);
input_report_key(dev, N_0, value);
input_sync(dev);
return IRQ_HANDLED;
}
在這個例子中,我們首先調用input_allocate_device()函數來創(chuàng)建一個input_dev結構體并分配內存。然后,我們將設備的名稱設置為“button”,并使用input_set_capability()函數來指定輸入設備支持的事件類型和數據格式。在我們的實現中,我們只使用了是一個N_0按鍵。我們調用input_register_device()來注冊輸入設備。
處理輸入事件的函數由button_handler()實現。它首先讀取一個GPIO輸入,并使用input_report_key()函數來生成一個輸入事件。該函數將生成一個EV_KEY事件,其中N_0作為鍵碼,并根據GPIO的狀態(tài)設置按下或釋放狀態(tài)。
我們使用input_sync()函數將事件提交給內核輸入子系統(tǒng)。
到此為止,我們已經學習了如何編寫Linux設備驅動,實現了按鍵輸入驅動。在實際應用中,我們可以根據需要擴展此驅動程序以支持各種不同的輸入設備、事件類型和數據格式。
對于Linux內核的驅動程序開發(fā)者來說,掌握輸入事件類型和數據格式,編寫輸入事件處理函數以及在內核中注冊和注銷輸入設備都是非常重要的技能。
成都網站建設公司-創(chuàng)新互聯為您提供網站建設、網站制作、網頁設計及定制高端網站建設服務!
linux下如何模擬按鍵輸入和模擬鼠標
網頁鏈閉祥接
這個是我找的源態(tài)埋雹螞一個別人的博客
linux/input.h 中有定義,這個文件還定義了標準按鍵的編猜孝銀碼等 struct input_event { struct timeval time; //按鍵時間 __u16 type; //類型,在下面有定義穗宴 __u16 code; //要模擬成什么按鍵 __s32 value;//是按下還是釋放 }; code: 事件的代碼.如果事件的類型代碼是EV_KEY,該代碼code 為設備鍵盤代碼.代碼植0~127 為鍵盤上的按鍵代碼,0x110~0x116 為鼠標上按鍵代碼,其中0x110(N_ LEFT)為鼠標左鍵,0x111(N_RIGHT)為鼠標右鍵,0x112(N_ MIDDLE)為鼠標中鍵.其它代碼含義請參看 include/linux/input.h 文件. 如果事件的類型代碼是EV_REL,code 值表示軌跡的類型.如指示鼠標的X軸方向REL_X(代碼為0x00),指示鼠標的Y 軸方向REL_Y(代碼為0x01),指示鼠標中輪子方向 REL_WHEEL(代碼為0x08). type: EV_KEY,鍵盤 EV_REL,相對坐標 EV_ABS,絕對坐標 value: 事件的值.如果事件的類型代碼是EV_KEY,當按鍵按下時值為1,松開時值為0;如果事件的類型代碼是 EV_ REL,value 的正數值和負數值分別代表兩個不同方向的值. /* * Event types */ #define EV_SYN 0x00 #define EV_KEY 0x01 //按鍵 #define EV_REL 0x02 //相對坐標(軌跡球) #define EV_ABS 0x03 //絕對坐標 #define EV_MSC 0x04 //其他 #define EV_SW 0x05 #define EV_LED 0x11 //LED #define EV_SND 0x12//聲音 #define EV_REP 0x14//repeat #define EV_FF 0x15 #define EV_PWR 0x16 #define EV_FF_STATUS 0x17 #define EV_MAX 0x1f #define EV_CNT (EV_MAX+1) 1。模擬按鍵輸入 //其中0 表示釋放,1 按鍵按下,2 表示一直按下 //0 for EV_KEY for release, 1 for keypress and 2 for autorepeat. void simulate_key(int fd,int value) { struct input_event event; event.type = EV_KEY; //event.code = KEY_0;//要模擬成什么按鍵 event.value = value;//是按下還是釋放按鍵慎衡或者重復 gettimeofday(&event.time,0); if(write(fd,&event,sizeof(event)) window; //一定要設置為主窗口 event->key.keyval = keyval; //FIXME:一定要加上這個,要不然容易出錯 g_object_ref(event->key.window); gdk_threads_enter(); //FIXME: 記得用這個來發(fā)送事件 gtk_main_do_event(event); gdk_threads_leave(); gdk_event_free(event); } kernel 里input 模塊 input_dev 結構: struct input_dev { void *private; const char *name; const char *phys; const char *uniq; struct input_id id; /* * 根據各種輸入信號的類型來建立類型為unsigned long 的數組, * 數組的每1bit 代表一種信號類型, * 內核中會對其進行置位或清位操作來表示時間的發(fā)生和被處理. */ unsigned long evbit; unsigned long keybit; unsigned long relbit; unsigned long abit; unsigned long mscbit; unsigned long ledbit; unsigned long sndbit; unsigned long ffbit; unsigned long swbit; ………………………………….. }; /** * input_set_capability – mark device as capable of a certain event * @dev: device that is capable of emitting or accepting event * @type: type of the event (EV_KEY, EV_REL, etc…) * @code: event code * * In addition to setting up corresponding bit in appropriate capability * bitmap the function also adjusts dev->evbit. */ /* 記錄本設備對于哪些事件感興趣(對其進行處理)*/ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code) { switch (type) { case EV_KEY: __set_bit(code, dev->keybit);//比如按鍵,應該對哪些鍵值的按鍵進行處理(對于其它按鍵不予理睬) break; case EV_REL: __set_bit(code, dev->relbit); break; case EV_ABS: __set_bit(code, dev->abit); break; case EV_MSC: __set_bit(code, dev->mscbit); break; case EV_SW: __set_bit(code, dev->swbit); break; case EV_LED: __set_bit(code, dev->ledbit); break; case EV_SND: __set_bit(code, dev->sndbit); break; case EV_FF: __set_bit(code, dev->ffbit); break; default: printk(KERN_ERR “input_set_capability: unknown type %u (code %u)\n”, type, code); dump_stack(); return; } __set_bit(type, dev->evbit);//感覺和前面重復了(前面一經配置過一次了) } EXPORT_SYMBOL(input_set_capability); static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { int i; struct platform_device *pdev = dev_id; struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct input_dev *input = platform_get_drvdata(pdev); for (i = 0; i nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons; int gpio = button->gpio; if (irq == gpio_to_irq(gpio)) {//判斷哪個鍵被按了? unsigned int type = button->type ?: EV_KEY; int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low;//記錄按鍵狀態(tài) input_event(input, type, button->code, !!state);//匯報輸入事件 input_sync(input);//等待輸入事件處理完成 } } return IRQ_HANDLED; } /* * input_event() – report new input event * @dev: device that generated the event * @type: type of the event * @code: event code * @value: value of the event * * This function should be used by drivers implementing various input devices * See also input_inject_event() */ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct input_handle *handle; if (type > EV_MAX || !test_bit(type, dev->evbit))//首先判斷該事件類型是否有效且為該設備所接受 return; add_input_randomness(type, code, value); switch (type) { case EV_SYN: switch (code) { case SYN_CONFIG: if (dev->event) dev->event(dev, type, code, value); break; case SYN_REPORT: if (dev->sync) return; dev->sync = 1; break; } break; case EV_KEY: /* * 這里需要滿足幾個條件: * 1: 鍵值有效(不超出定義的鍵值的有效范圍) * 2: 鍵值為設備所能接受(屬于該設備所擁有的鍵值范圍) * 3: 按鍵狀態(tài)改變了 */ if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) return; if (value == 2) break; change_bit(code, dev->key);//改變對應按鍵的狀態(tài) /* 如果你希望按鍵未釋放的時候不斷匯報按鍵事件的話需要以下這個(在簡單的gpio_keys 驅動中不需要這個,暫時不去分析) */ if (test_bit(EV_REP, dev->evbit) && dev->rep && dev->rep && dev->timer.data && value) { dev->repeat_key = code; mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep)); } break; ……………………………………………….. if (type != EV_SYN) dev->sync = 0; if (dev->grab) dev->grab->handler->event(dev->grab, type, code, value); else /* * 循環(huán)調用所有處理該設備的handle(event,mouse,ts,joy 等), * 如果有進程打開了這些handle(進行讀寫),則調用其對應的event 接口向氣匯報該輸入事件. */ list_for_each_entry(handle, &dev->h_list, d_node) if (handle->open) handle->handler->event(handle, type, code, value); } EXPORT_SYMBOL(input_event); event 層對于input 層報告的這個鍵盤輸入事件的處理: drivers/input/evdev.c: static struct input_handler evdev_handler = { .event = evdev_event, .connect = evdev_connect, .disconnect = evdev_disconnect, .fops = &evdev_fops, .minor = EVDEV_MINOR_BASE, .name = “evdev”, .id_table = evdev_ids, }; Linux 有自己的 input 子系統(tǒng),可以統(tǒng)一管理鼠標和鍵盤事件。 基于輸入子系統(tǒng) 實現的 uinput 可以方便的在用戶空間模擬鼠標和鍵盤事件。 當然,也可以自己造輪子, 做一個字符設備接收用戶輸入,根據輸入,投遞 input 事件。 還有一種方式就是直接 往 evnent 里寫入數據, 都可以達到控制鼠標鍵盤的功能。 本篇文章就是演示直接寫入 event 的方法。 linux/input.h 中有定義,這個文件還定義了標準按鍵的編碼等 struct input_event { struct timeval time; //按鍵時間 __u16 type; //類型,在下面有定義 __u16 code; //要模擬成什么按鍵 __s32 value;//是按下還是釋放 }; code: 事件的代碼.如果事件的類型代碼是EV_KEY,該代碼code 為設備鍵盤代碼.代碼植0~127 為鍵盤上的按鍵代碼, 0x110~0x116 為鼠標上按鍵代碼,其中0x110(N_ LEFT)為鼠標左鍵,0x111(N_RIGHT)為鼠標右鍵,0x112(N_ MIDDLE)為鼠標中鍵.其它代碼含義請參看 include/linux /input.h 文件. 如果事件的類型代碼是EV_REL,code 值表示軌跡的類型.如指示鼠標的X軸方向 REL_X (代碼為0x00),指示鼠標的Y 軸方向REL_Y(代碼為0x01),指示鼠標中輪子方向 REL_WHEEL(代碼為0x08). type: EV_KEY,鍵盤 EV_REL,相對坐標 EV_ABS,絕對坐標 value: 事件的值.如果事件的類型代碼是EV_KEY,當按鍵按下時值為1,松開時值為0;如果事件的類型代碼是 EV_ REL,value 的正數值和負數值分別代表兩個不同方向的值. /* * Event types */ #define EV_SYN 0x00 #define EV_KEY 0x01 //按鍵 #define EV_REL 0x02 //相對坐標(軌跡球) #define EV_ABS 0x03 //絕對坐標 #define EV_MSC 0x04 //其他 #define EV_SW 0x05 #define EV_LED 0x11 //LED #define EV_SND 0x12//聲音 #define EV_REP 0x14//repeat #define EV_FF 0x15 #define EV_PWR 0x16 #define EV_FF_STATUS 0x17 #define EV_MAX 0x1f #define EV_CNT (EV_MAX+1) 下面是一個模擬鼠標和鍵盤輸入的例子: #include #include #include #include #include #include #include #include #include #include #include void simulate_key(int fd,int kval) { struct input_event event; event.type = EV_KEY; event.value = 1; event.code = kval; gettimeofday(&event.time,0); write(fd,&event,sizeof(event)) ; event.type = EV_SYN; event.code = SYN_REPORT; event.value = 0; write(fd, &event, sizeof(event)); memset(&event, 0, sizeof(event)); gettimeofday(&event.time, NULL); event.type = EV_KEY; event.code = kval; event.value = 0; write(fd, &event, sizeof(event)); event.type = EV_SYN; event.code = SYN_REPORT; event.value = 0; write(fd, &event, sizeof(event)); } void simulate_mouse(int fd) { struct input_event event; memset(&event, 0, sizeof(event)); gettimeofday(&event.time, NULL); event.type = EV_REL; event.code = REL_X; event.value = 10; write(fd, &event, sizeof(event)); event.type = EV_REL; event.code = REL_Y; event.value = 10; write(fd, &event, sizeof(event)); event.type = EV_SYN; event.code = SYN_REPORT; event.value = 0; write(fd, &event, sizeof(event)); } int main() { int fd_kbd; int fd_mouse; fd_kbd = open(“/dev/input/event1”,O_RDWR); if(fd_kbdlinux設備驅動 按鍵的介紹就聊到這里吧,感謝你花時間閱讀本站內容,更多關于linux設備驅動 按鍵,Linux設備驅動:按鍵輸入的實現,linux下如何模擬按鍵輸入和模擬鼠標的信息別忘了在本站進行查找喔。
香港云服務器機房,創(chuàng)新互聯(www.cdcxhl.com)專業(yè)云服務器廠商,回大陸優(yōu)化帶寬,安全/穩(wěn)定/低延遲.創(chuàng)新互聯助力企業(yè)出海業(yè)務,提供一站式解決方案。香港服務器-免備案低延遲-雙向CN2+BGP極速互訪!
文章名稱:Linux設備驅動:按鍵輸入的實現(linux設備驅動按鍵)
網站網址:http://m.fisionsoft.com.cn/article/dpjjdgg.html


咨詢
建站咨詢
