Linux內核驅動學習(十)Input子系統詳解

      網友投稿 1135 2022-05-29

      文章目錄

      前言

      框架

      如何實現`input device` 設備驅動?

      頭文件

      注冊input_dev設備

      上報按鍵值

      dev->open()和dev->close()

      其他事件類型,處理輸出事件

      查看input device信息

      附錄

      前言

      這次主要會學習linux中對于輸入設備統一封裝的框架,在計算機組成原理中,我們可以知道計算機的組成主要分為五個部分:控制器,運算器,存儲器,輸入,輸出。可見,輸入作為其中的一個子系統,但是對于眾多的設備來說,需要一套統一的規范。所以,在嵌入式系統中的外設,鼠標、鍵盤、按鍵、G-Sensor等等都可以注冊為Input設備。Linux在用戶層提供了相應的接口讀取數據,這里我暫時只介紹在上一篇文章的基礎上,如何編寫一個Input驅動。

      框架

      首先還是先看一下Input子系統的整體框架,具體如下圖所示;

      如何實現input device 設備驅動?

      如何編寫input驅動的sample?在內核文檔中已經有詳細的介紹,雖然是英文的,但是相當規范,簡單,詳細,本文就是在此基礎上展開,具體可以參考內核input文檔。

      在這里,我們需要在代碼中做哪些工作呢?下面將會重點羅列成每一個步驟進行講解。

      頭文件

      首先需要包含頭文件#include ,這個頭文件包含了input設備的各種定義,結構體以及方法。

      注冊input_dev設備

      首先定義一個用來注冊input device的變量,這里會用到input_dev這個結構體。目前的情況是,我把需要的各種變量都封裝到gpio_demo_device這個結構體中,但是,實際上這里暫時這關心input_demo就可以了。

      struct gpio_demo_device { struct platform_device *pdev; struct device *dev; struct gpio_platform_data *pdata; struct workqueue_struct *gpio_monitor_wq; struct delayed_work gpio_delay_work ; struct input_dev *input_demo; };

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      在這里需要為我們創建的gpio_demo_device結構體定義一個變量priv,并且使用devm_kzalloc為其分配相應的內存。

      struct gpio_demo_device *priv; priv = devm_kzalloc(dev, sizeof(*priv) , GFP_KERNEL);

      1

      2

      然后,我們再為input_demo分配內存;

      priv->input_demo = devm_input_allocate_device(priv->dev);

      1

      初始化input_demo;

      // kernel/include/uapi/linux/input-event-codes.h priv->input_demo->name = "input-demo"; priv->input_demo->dev.parent = priv->dev; priv->input_demo->evbit[0] = BIT_MASK(EV_KEY); //事件類型注冊為EV_KEY priv->input_demo->keybit[BIT_WORD(KEY_VOLUMEDOWN)] = BIT_MASK(KEY_VOLUMEDOWN); //按鍵值

      1

      2

      3

      4

      5

      name:當前初始化的input device的名字;

      evbit[0]:配置為BIT_MASK(EV_KEY),將事件類型注冊為EV_KEY類型,具體有哪些類型如下所示;

      所有定義都在kernel/include/uapi/linux/input-event-codes.h文件中。

      除了EV_KEY之外,還有兩種基本事件類型:EV_REL和EV_ABS。它們用于設備提供的相對值和絕對值。相對值可以是例如X軸上的鼠標移動。鼠標將其報告為與上一個位置的相對差異,因為它沒有任何絕對坐標系可供使用。絕對事件即操縱桿和數字化儀 - 在絕對坐標系中工作的設備。

      /*

      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

      #define EV_SND 0x12

      #define EV_REP 0x14

      #define EV_FF 0x15

      #define EV_PWR 0x16

      #define EV_FF_STATUS 0x17

      #define EV_MAX 0x1f

      #define EV_CNT (EV_MAX+1)

      keybit[BIT_WORD(KEY_VOLUMEDOWN)]:按鍵值設置為BIT_MASK(KEY_VOLUMEDOWN),就是音量減的按鍵;

      BITS_TO_LONGS(), BIT_WORD(), BIT_MASK()

      These three macros from bitops.h help some bitfield computations:

      BITS_TO_LONGS(x) - returns the length of a bitfield array in longs for x bits

      BIT_WORD(x) - returns the index in the array in longs for bit x

      BIT_MASK(x) - returns the index in a long for bit x

      完成初始化之后,然后注冊input_demo;

      ret = input_register_device(priv->input_demo);

      1

      上報按鍵值

      當用戶發生了向系統輸入一定信息的操作之后,input device需要將一些列信息上傳,本文需要上傳按鍵值。在這里要可以通過中斷觸發,或者輪詢去檢測用戶的動作,前面已經有提及,這里不再贅述,

      本文代碼已經在附錄中,已經涵蓋這兩種方式。

      關于上報數據可以通過input_report_key和input_sync去完成。

      dev->open()和dev->close()

      如果驅動程序必須重復輪詢設備,因為它沒有來自它的中斷,并且輪詢太昂貴而無法一直進行,或者如果設備中斷資源比較稀缺,可以使用open和close回調來通知設備何時可以停止輪詢或釋放中斷以及何時必須重新開始輪詢或再次獲取中斷。為此,我們將以下代碼添加到示例驅動程序:

      static int button_open(struct input_dev *dev) { if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) { printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq); return -EBUSY; } return 0; } static void button_close(struct input_dev *dev) { free_irq(IRQ_AMIGA_VERTB, button_interrupt); } static int gpio_demo_probe(struct platform_device *pdev)t(void) { ... button_dev->open = button_open; button_dev->close = button_close; ... }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      請注意,輸入內核會跟蹤設備的用戶數,并確保僅在第一個用戶連接到設備時調用dev->open(),并在最后一個用戶斷開連接時調用dev-> close() 。對兩個回調的調用都是序列化的。

      open()回調應該在成功的情況下返回0,或者在失敗的情況下返回任何非零值。close()回調(無效)必須始終成功。

      其他事件類型,處理輸出事件

      到目前為止的其他事件類型是:

      EV_LED - 用于鍵盤LED。

      EV_SND - 用于鍵盤蜂鳴聲。

      它們與按鍵事件非常相似,但它們朝另一個方向 - 從系統到輸入設備驅動程序。如果您的輸入設備驅動程序可以處理這些事件,則必須在evbit中設置相應的位, 以及回調例程:

      button_dev->event = button_event; int button_event(struct input_dev * dev,unsigned int type, unsigned int code,int value){ if(type == EV_SND && code == SND_BELL){ outb(value,BUTTON_BELL); return 0; } return -1; }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      這個回調例程可以從一個中斷或一個BH調用(雖然這不是一個規則),因此不能休眠,并且不能花太長時間才能完成。

      查看input device信息

      系統啟動之后,可以通過cat /proc/bus/input/devices來查看當前系統中已經注冊的input device信息,這里可以看到已經注冊成功的input-demo

      附錄

      #include #include #include //API for libgpio #include //API for malloc #include //API for device tree #include #include #include //API for thread #include #include #include //API for delaywork #include #include #include //API for input #include #define TIMER_MS_COUNTS 1000 // default value of dts #define DEFAULT_POLL_TIME 5 #define DEFAULT_MODE 1 struct gpio_platform_data { int mode; int count; int gpio_index; struct mutex mtx; int poll_ms; }; struct gpio_demo_device { struct platform_device *pdev; struct device *dev; struct gpio_platform_data *pdata; struct workqueue_struct *gpio_monitor_wq; struct delayed_work gpio_delay_work ; struct input_dev *input_demo; int gpio_irq; }; static int gpio_parse_data(struct gpio_demo_device *di){ int ret; struct gpio_platform_data *pdata; struct device *dev = di->dev; struct device_node *np = di->dev->of_node; pdata = devm_kzalloc(di->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { return -ENOMEM; } di->pdata = pdata; // set default value for platform data pdata->mode = DEFAULT_MODE; pdata->poll_ms = DEFAULT_POLL_TIME * 1000; dev_info(dev,"parse platform data\n"); ret = of_property_read_u32(np, "mode", &pdata->mode); if (ret < 0) { dev_err(dev, "can't get mode property\n"); } ret = of_property_read_u32(np, "poll_time", &pdata->poll_ms); if (ret < 0) { dev_err(dev, "can't get poll_ms property\n"); } pdata->gpio_index = of_get_named_gpio(np,"input-gpio", 0); if (pdata->gpio_index < 0) { dev_err(dev, "can't get input gpio\n"); } // debug parse device tree data dev_info(dev, "Success:mode is %d\n", pdata->mode); dev_info(dev, "Success:gpio index is %d\n", pdata->gpio_index); return 0; } static void gpio_demo_work(struct work_struct *work) { struct gpio_demo_device *di = container_of(work, struct gpio_demo_device, gpio_delay_work.work); struct gpio_platform_data *padta = di->pdata; int gpio_index,value; gpio_index = padta->gpio_index; if (!gpio_is_valid(gpio_index) ) { dev_err(di->dev, "gpio is not valid\n"); goto end; } if ( (value = gpio_get_value(gpio_index) ) == 0) { dev_info(di->dev,"get value is %d\n",value); }else{ dev_info(di->dev,"get value is %d\n",value); } end: queue_delayed_work(di->gpio_monitor_wq, &di->gpio_delay_work, msecs_to_jiffies(di->pdata->poll_ms)); } static int gpio_demo_init_poll(struct gpio_demo_device *di) { dev_info(di->dev,"%s\n", __func__); di->gpio_monitor_wq = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM | WQ_FREEZABLE, "gpio-demo-wq"); INIT_DELAYED_WORK(&di->gpio_delay_work, gpio_demo_work); queue_delayed_work(di->gpio_monitor_wq, &di->gpio_delay_work, msecs_to_jiffies(TIMER_MS_COUNTS * 5)); return 0; } static irqreturn_t gpio_demo_isr(int irq, void *dev_id) { struct gpio_demo_device *di = (struct gpio_demo_device *)dev_id; struct gpio_platform_data *pdata = di->pdata; BUG_ON(irq != gpio_to_irq(pdata->gpio_index)); input_report_key(di->input_demo, KEY_VOLUMEDOWN, 1); input_sync(di->input_demo); input_report_key(di->input_demo, KEY_VOLUMEDOWN, 0); input_sync(di->input_demo); dev_info(di->dev, "%s\n", __func__); //printk("%s\n",__func__); return IRQ_HANDLED; } static int gpio_demo_init_interrupt(struct gpio_demo_device *di) { int irq, ret; int gpio_index = di->pdata->gpio_index; dev_info(di->dev,"%s\n", __func__); if (!gpio_is_valid(gpio_index)){ return -1; } irq = gpio_to_irq(gpio_index); if (irq < 0) { dev_err(di->dev, "Unable to get irq number for GPIO %d, error %d\n", gpio_index, irq); gpio_free(gpio_index); return -1; } ret = devm_request_irq(di->dev, irq, gpio_demo_isr, IRQF_TRIGGER_FALLING, "gpio-demo-isr", di); if (ret) { dev_err(di->dev, "Unable to claim irq %d; error %d\n", irq, ret); gpio_free(gpio_index); return -1; } return 0; } static int gpio_demo_probe(struct platform_device *pdev){ int ret; struct gpio_demo_device *priv; struct device *dev = &pdev->dev; priv = devm_kzalloc(dev, sizeof(*priv) , GFP_KERNEL); if (!priv) { return -ENOMEM; } priv->dev = dev; //important ret = gpio_parse_data(priv); if (ret){ dev_err(dev,"parse data failed\n"); } priv->input_demo = devm_input_allocate_device(priv->dev); if(!priv->input_demo) { dev_err(dev,"Can't allocate input device\n"); return -ENOMEM; } // kernel/include/uapi/linux/input-event-codes.h priv->input_demo->name = "input-demo"; priv->input_demo->dev.parent = priv->dev; priv->input_demo->evbit[0] = BIT_MASK(EV_KEY); priv->input_demo->keybit[BIT_WORD(KEY_VOLUMEDOWN)] = BIT_MASK(KEY_VOLUMEDOWN); ret = input_register_device(priv->input_demo); if(ret) { dev_err(priv->dev, "register input device failed\n"); input_free_device(priv->input_demo); return ret; } //input_set_capability(priv->dev, EV_KEY, KEY_VOLUMEDOWN); platform_set_drvdata(pdev,priv); if (priv->pdata->mode == 0){ gpio_demo_init_poll(priv); } else { gpio_demo_init_interrupt(priv); } return 0; } #ifdef CONFIG_OF static struct of_device_id gpio_demo_of_match[] = { { .compatible = "gpio-demo"}, {}, } MODULE_DEVICE_TABLE(of,gpio_demo_of_match); #else static struct of_device_id gpio_demo_of_match[] = { { }, } #endif static struct platform_driver gpio_demo_driver = { .probe = gpio_demo_probe, .driver = { .name = "gpio-demo-device", .owner = THIS_MODULE, .of_match_table = of_match_ptr(gpio_demo_of_match), } }; static int __init gpio_demo_init(void){ return platform_driver_register(&gpio_demo_driver); } static void __exit gpio_demo_exit(void){ platform_driver_unregister(&gpio_demo_driver); } late_initcall(gpio_demo_init); module_exit(gpio_demo_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Gpio demo Driver"); MODULE_ALIAS("platform:gpio-demo");

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      69

      70

      71

      72

      73

      74

      75

      76

      77

      78

      79

      80

      81

      82

      83

      84

      85

      86

      87

      88

      89

      90

      91

      92

      93

      94

      95

      96

      97

      98

      99

      100

      101

      102

      103

      104

      105

      106

      107

      108

      109

      110

      111

      112

      113

      114

      115

      116

      117

      118

      119

      120

      121

      122

      123

      124

      125

      126

      127

      128

      129

      130

      131

      132

      133

      134

      135

      136

      137

      138

      139

      140

      141

      142

      143

      144

      145

      146

      147

      148

      149

      150

      151

      152

      153

      154

      155

      156

      157

      158

      159

      160

      161

      162

      163

      164

      165

      166

      167

      168

      169

      170

      171

      172

      173

      174

      175

      176

      177

      178

      179

      180

      181

      182

      183

      184

      185

      186

      187

      188

      189

      190

      191

      192

      193

      194

      195

      196

      197

      198

      199

      200

      201

      202

      203

      204

      205

      206

      207

      208

      209

      210

      211

      212

      213

      214

      215

      216

      217

      218

      219

      220

      221

      222

      223

      224

      225

      226

      227

      228

      229

      230

      231

      232

      233

      234

      235

      236

      237

      238

      239

      240

      241

      242

      243

      244

      245

      246

      247

      248

      249

      250

      251

      252

      253

      Linux內核驅動學習(十)Input子系統詳解

      254

      255

      256

      257

      258

      259

      260

      261

      262

      263

      264

      265

      266

      267

      268

      Linux

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:操作系統中幾個常用關鍵的概念
      下一篇:Git內部原理之深入解析Git的引用和包文件
      相關文章
      亚洲视频在线观看不卡| 日韩精品亚洲专区在线观看| 亚洲丶国产丶欧美一区二区三区 | 亚洲一区爱区精品无码| 国产午夜亚洲精品不卡免下载| 亚洲精品无码久久久久APP | 国产精品亚洲视频| 亚洲国产婷婷综合在线精品| 亚洲成AV人网址| 亚洲国产成人VA在线观看| 亚洲av成人一区二区三区在线观看| 蜜桃传媒一区二区亚洲AV| 亚洲日本一线产区和二线| 亚洲欧洲日韩国产一区二区三区 | 自拍偷自拍亚洲精品情侣| 中文字幕亚洲一区二区三区| 久久久久亚洲精品天堂久久久久久| 亚洲精品无码你懂的网站| 久久久久亚洲精品男人的天堂| 亚洲男人的天堂www| 国产亚洲精品观看91在线| 久久亚洲精品国产精品黑人| 久久久久亚洲av无码专区喷水| 亚洲色偷偷av男人的天堂| 亚洲国产综合第一精品小说| 亚洲国产成a人v在线观看| 亚洲日韩精品无码专区加勒比☆| 亚洲色最新高清av网站| 亚洲av无码一区二区三区人妖| 亚洲AV无码一区二区一二区| 丁香亚洲综合五月天婷婷| 亚洲精品线路一在线观看| 亚洲熟妇无码另类久久久| 亚洲AV永久无码精品水牛影视| 亚洲福利在线视频| 亚洲国产品综合人成综合网站| 亚洲国产精品免费观看| 亚洲av成人一区二区三区观看在线| 亚洲国产专区一区| 亚洲处破女AV日韩精品| 久久久久亚洲AV无码专区体验|